如何利用JavaScript的数组缓冲和视图处理二进制数据,以及它在网络通信或文件解析中的使用?

JavaScript通过ArrayBuffer提供固定大小的原始二进制内存块,再借助TypedArray或DataView视图以特定类型和字节序读写数据,实现高效处理二进制流,广泛应用于WebSocket通信、文件解析等场景。

如何利用javascript的数组缓冲和视图处理二进制数据,以及它在网络通信或文件解析中的使用?

JavaScript处理二进制数据,其核心思想是提供一个原始的、固定大小的内存块——ArrayBuffer,它本身不包含任何格式信息。接着,通过TypedArray(如Uint8ArrayInt32Array)或DataView这些“视图”,我们才能以特定的数据类型和字节序来解读和操作这块内存。这套机制在网络通信中,比如WebSocket收发二进制消息,或是前端解析文件(如图片、音频、压缩包)时,扮演着至关重要的角色,它让浏览器端的JavaScript能够直接、高效地与底层字节流打交道,这感觉就像是打开了通往计算机更深层能力的一扇门。

解决方案

要高效地利用ArrayBuffer和视图处理二进制数据,我们首先要理解它们各自的职责。ArrayBuffer就像一块空白的画板,只提供内存空间,不关心你画什么。而TypedArrayDataView则是不同的画笔和眼镜,让你能以特定的方式去“看”和“操作”这块画板上的内容。

1. ArrayBuffer:原始内存块ArrayBuffer是一个固定长度的原始二进制数据缓冲区。它不能直接操作,因为它只是一个内存区域,没有提供任何读写方法。你需要通过它的视图来访问其内容。

const buffer = new ArrayBuffer(16); // 创建一个16字节的缓冲区console.log(buffer.byteLength); // 16

2. TypedArray:类型化数组视图TypedArrayArrayBuffer最常见的视图。它将ArrayBuffer中的字节解释为特定类型的数值数组。例如,Uint8Array将每个字节视为一个无符号8位整数,而Int32Array则将每四个字节视为一个有符号32位整数。

立即学习“Java免费学习笔记(深入)”;

const buffer = new ArrayBuffer(16);const uint8View = new Uint8Array(buffer); // 创建一个Uint8Array视图uint8View[0] = 255;uint8View[1] = 128;console.log(uint8View); // Uint8Array [255, 128, 0, 0, ...]const int32View = new Int32Array(buffer); // 创建一个Int32Array视图int32View[0] = 123456789; // 写入一个32位整数,会占据前4个字节console.log(int32View); // Int32Array [123456789, 0, 0, 0]console.log(uint8View); // Uint8Array [21, 205, 91, 7, 0, ...] (原始字节内容已改变)

TypedArray在处理同类型数据序列时非常高效,比如图像的像素数据或音频的采样数据。

3. DataView:灵活的字节视图DataView提供了一种更精细、更灵活的方式来操作ArrayBuffer。它允许你在任意字节偏移量处读写不同大小和字节序(大端序/小端序)的数值。这在处理混合数据类型或需要严格控制字节序的协议时尤其有用。

const buffer = new ArrayBuffer(8); // 8字节缓冲区const dataView = new DataView(buffer);// 写入一个无符号8位整数dataView.setUint8(0, 0x41); // 偏移量0,写入字符'A'的ASCII值// 写入一个大端序的无符号16位整数dataView.setUint16(1, 0x4243, false); // 偏移量1,写入0x4243 (大端序)// 写入一个小端序的浮点数dataView.setFloat32(3, 3.14159, true); // 偏移量3,写入3.14159 (小端序)console.log(new Uint8Array(buffer)); // 看看原始字节// 读取数据console.log(dataView.getUint8(0)); // 65 (0x41)console.log(dataView.getUint16(1, false)); // 16963 (0x4243)console.log(dataView.getFloat32(3, true)); // 3.141590118408203

DataViewgetset方法都有一个可选的littleEndian参数,默认为false(大端序),这对于跨平台或特定协议的字节序兼容性至关重要。

在网络通信或文件解析中的应用:

网络通信 (WebSocket): WebSocket API可以直接发送和接收ArrayBuffer。发送时,socket.send(buffer)即可。接收时,event.data如果是二进制数据,就会是ArrayBuffer类型,你可以直接对其创建TypedArrayDataView进行解析。文件解析 (FileReaderfetch):本地文件: 使用FileReaderreadAsArrayBuffer()方法可以将用户选择的文件读取为一个ArrayBuffer远程文件: 使用fetch API获取二进制数据时,response.arrayBuffer()方法会返回一个Promise,解析后就是ArrayBuffer。一旦获取到ArrayBuffer,就可以根据文件格式规范(如JPEG、WAV、ZIP等)使用DataView在特定偏移量读取文件头、元数据和实际数据块。

为什么在处理二进制数据时,我们不能直接使用普通的JavaScript数组?

这是一个很好的问题,而且我个人觉得,理解这一点是深入掌握ArrayBuffer的关键。简单来说,JavaScript的普通数组(Array)设计之初就不是为了直接操作原始二进制数据。它们是高度抽象的、动态的、异构的集合。

一个普通的JavaScript数组,比如[1, "hello", {a: 1}],可以存储任何类型的值,而且长度是可变的。当你存储数字时,这些数字在底层可能被优化存储,但它们仍然是JavaScript的Number类型,是浮点数表示,而不是像C语言中intchar那样的固定位宽整数。这意味着:

内存布局不确定性: 普通数组中的元素在内存中不一定是连续存储的,或者说,即使数字连续,它们也不是原始字节。每个元素都可能是一个指向实际数据(如对象、字符串、或封装的数字)的指针。这使得我们无法像操作底层内存那样,以字节为单位进行精确的偏移量计算和读取。性能开销: 每次访问或修改普通数组的元素,JavaScript引擎都需要进行类型检查、装箱/拆箱操作(将原始数字转换为Number对象,反之亦然),以及潜在的垃圾回收管理。这对于需要大量、高速处理数字序列(如图像像素、音频采样)的场景来说,性能开销巨大。缺乏类型化视图: 普通数组无法提供“以8位无符号整数序列看待这块内存”或“以32位浮点数序列看待这块内存”的能力。ArrayBufferTypedArray正是为了填补这个空白而生,它们提供了一种直接映射到底层内存的方式,其操作几乎可以达到原生语言的效率。与底层API的互操作性: 许多Web API(如WebSocket、WebRTC、WebGL、WebAudio、File API)在处理二进制数据时,都期望或返回ArrayBuffer。如果你试图用普通数组去对接这些API,你会发现要么行不通,要么需要大量的手动转换,效率低下且容易出错。

所以,当我们需要进行字节级别的操作,或者与那些需要原始二进制数据的Web API交互时,ArrayBufferTypedArray是不可替代的。它们提供了一种桥梁,让JavaScript能够以一种高效且可控的方式,触及到更接近硬件的层面。

在网络通信中,如何有效地发送和接收二进制数据?

在现代Web应用中,有效地发送和接收二进制数据是实现高性能、富交互体验的关键。这通常涉及到与服务器进行更底层的数据交换,而不是传统的JSON或文本。

1. 使用WebSocket进行实时二进制通信:

WebSocket是进行双向、全双工通信的首选,它原生支持发送和接收二进制数据。

发送: 你可以直接将一个ArrayBufferTypedArray实例发送出去。

const ws = new WebSocket('ws://localhost:8080');ws.onopen = () => {    const buffer = new ArrayBuffer(4);    const view = new Uint8Array(buffer);    view[0] = 0x01; // 消息类型    view[1] = 0x02; // 命令    view[2] = 0x03; // 数据    view[3] = 0x04;    ws.send(buffer); // 直接发送ArrayBuffer    // 也可以发送TypedArray: ws.send(view);};

服务器端收到后,会将其作为二进制数据处理。

接收: 当WebSocket接收到二进制消息时,event.data将是一个ArrayBuffer实例。

ws.onmessage = (event) => {    if (event.data instanceof ArrayBuffer) {        console.log('收到二进制数据:', event.data);        const dataView = new DataView(event.data);        const messageType = dataView.getUint8(0);        const payloadLength = dataView.getUint16(1, false); // 假设第二个字节开始是长度,大端序        // 根据消息类型和长度解析后续数据        // ...    } else {        console.log('收到文本数据:', event.data);    }};

这里需要注意的是,你可能需要设置ws.binaryType = 'arraybuffer';来确保接收到的二进制数据是ArrayBuffer类型(通常是默认值)。

2. 使用Fetch API发送和接收二进制数据:

Fetch API是现代浏览器中进行HTTP请求的首选。它也可以处理二进制数据,尤其适合文件上传或下载。

发送 (例如,上传文件或二进制流):你可以将ArrayBufferBlob作为body发送。

const fileInput = document.getElementById('fileInput');fileInput.addEventListener('change', async (event) => {    const file = event.target.files[0];    if (file) {        const arrayBuffer = await file.arrayBuffer(); // 将文件读取为ArrayBuffer        fetch('/upload-binary', {            method: 'POST',            headers: {                'Content-Type': 'application/octet-stream' // 声明为二进制流            },            body: arrayBuffer // 直接发送ArrayBuffer        })        .then(response => response.json())        .then(data => console.log('上传成功:', data))        .catch(error => console.error('上传失败:', error));    }});

接收 (例如,下载图片、音频或自定义二进制文件):Response对象提供了arrayBuffer()方法来将响应体解析为ArrayBuffer

fetch('/get-binary-data')    .then(response => {        if (!response.ok) {            throw new Error('Network response was not ok');        }        return response.arrayBuffer(); // 解析为ArrayBuffer    })    .then(buffer => {        console.log('下载的二进制数据:', buffer);        const dataView = new DataView(buffer);        // 进一步解析,比如判断文件类型,或者处理自定义协议        // ...    })    .catch(error => console.error('下载失败:', error));

关键考虑点:

协议设计: 当你自定义二进制协议时,设计一个清晰的消息头(包含消息类型、长度、版本等)至关重要。DataView在这里能帮助你精确地读写这些头部信息。字节序 (Endianness): 这是跨平台通信中的一个常见陷阱。不同的CPU架构可能使用不同的字节序(大端序或小端序)。务必在协议中明确规定字节序,并在使用DataViewget/set方法时,正确设置littleEndian参数。例如,dataView.getUint32(0, true)表示以小端序读取32位无符号整数。分块与流式处理: 对于非常大的文件或数据流,你可能不想一次性加载到内存中。Web Streams API(配合fetch)允许你以流式方式处理响应,这对于内存管理和用户体验都很有益。

有效地处理二进制数据,不仅仅是技术层面的操作,更是一种对数据传输效率和底层机制的深刻理解。这使得我们能够构建出更强大、更高效的Web应用。

解析文件(如图片、音频)时,ArrayBuffer和DataView扮演了什么角色?

在前端解析文件,尤其是二进制格式的文件(如图片、音频、视频、PDF等),ArrayBufferDataView简直是不可或缺的工具。它们为JavaScript提供了一双“透视眼”和一双“手术刀”,让我们能够直接深入文件的字节流,理解其内部结构。

1. 获取文件的ArrayBuffer

首先,无论是用户上传的本地文件,还是从网络下载的远程文件,我们都需要将其内容获取为ArrayBuffer

本地文件: 使用FileReader API。

const fileInput = document.getElementById('fileInput');fileInput.addEventListener('change', (event) => {    const file = event.target.files[0];    if (file) {        const reader = new FileReader();        reader.onload = (e) => {            const arrayBuffer = e.target.result; // 这里就是ArrayBuffer            console.log('文件ArrayBuffer:', arrayBuffer);            // 接下来就可以用DataView解析了            parseFile(arrayBuffer);        };        reader.readAsArrayBuffer(file); // 以ArrayBuffer形式读取文件    }});

远程文件: 使用fetch API。

async function downloadAndParseRemoteFile(url) {    try {        const response = await fetch(url);        if (!response.ok) {            throw new Error(`HTTP error! status: ${response.status}`);        }        const arrayBuffer = await response.arrayBuffer(); // 获取ArrayBuffer        console.log('远程文件ArrayBuffer:', arrayBuffer);        parseFile(arrayBuffer);    } catch (error) {        console.error('下载或解析文件失败:', error);    }}// downloadAndParseRemoteFile('path/to/your/image.jpg');

2. DataView的解析魔法:

一旦有了ArrayBufferDataView就登场了。文件格式通常都有一个严格的二进制结构:文件头(magic number)、元数据(尺寸、编码、采样率等)、以及实际的数据块。DataView允许我们精确地读取这些结构化信息。

以解析一个简单的WAV音频文件为例(简化版):

WAV文件通常以一个RIFF块开始,接着是WAVE格式块,然后是fmt子块(包含音频格式信息),最后是data子块(包含实际音频数据)。

function parseWAVHeader(arrayBuffer) {    const dataView = new DataView(arrayBuffer);    let offset = 0;    // 读取RIFF块    const riffChunkId = String.fromCharCode(dataView.getUint8(offset++), dataView.getUint8(offset++), dataView.getUint8(offset++), dataView.getUint8(offset++)); // "RIFF"    const fileSize = dataView.getUint32(offset, true); // 文件大小,小端序    offset += 4;    const waveFormat = String.fromCharCode(dataView.getUint8(offset++), dataView.getUint8(offset++), dataView.getUint8(offset++), dataView.getUint8(offset++)); // "WAVE"    offset += 4; // 跳过"WAVE"    // 读取fmt子块    const fmtChunkId = String.fromCharCode(dataView.getUint8(offset++), dataView.getUint8(offset++), dataView.getUint8(offset++), dataView.getUint8(offset++)); // "fmt "    const fmtChunkSize = dataView.getUint32(offset, true); // fmt块大小    offset += 4;    const audioFormat = dataView.getUint16(offset, true); // 音频格式 (1 = PCM)    offset += 2;    const numChannels = dataView.getUint16(offset, true); // 声道数    offset += 2;    const sampleRate = dataView.getUint32(offset, true); // 采样率    offset += 4;    const byteRate = dataView.getUint32(offset, true); // 字节率    offset += 4;    const blockAlign = dataView.getUint16(offset, true); // 块对齐    offset += 2;    const bitsPerSample = dataView.getUint16(offset, true); // 每采样位数    offset += 2;    // 假设我们已经解析了fmt块,现在跳过剩余的fmt块数据    offset += (fmtChunkSize - 16); // 16是fmt块固定部分的大小    // 查找data子块(可能会有其他块在中间,需要循环查找)    let dataChunkId = '';    let dataChunkSize = 0;    while (offset < arrayBuffer.byteLength - 8) { // 确保还有足够的空间读取ID和大小        dataChunkId = String.fromCharCode(dataView.getUint8(offset++), dataView.getUint8(offset++), dataView.getUint8(offset++), dataView.getUint8(offset++));        dataChunkSize = dataView.getUint32(offset, true);        offset += 4;        if (dataChunkId === 'data') {            break;        }        offset += dataChunkSize; // 跳过当前块    }    console.log('--- WAV Header Info ---');    console.log(`RIFF ID: ${riffChunkId}`);    console.log(`File Size: ${fileSize} bytes`);    console.log(`WAVE Format: ${waveFormat}`);    console.log(`FMT ID: ${fmtChunkId}`);    console.log(`Audio Format: ${audioFormat} (1=PCM)`);    console.log(`Channels: ${numChannels}`);    console.log(`Sample Rate: ${sampleRate} Hz`);    console.log(`Bits per Sample: ${bitsPerSample}`);    console.log(`Data Chunk ID: ${dataChunkId}`);    console.log(`Data Chunk Size: ${dataChunkSize} bytes`);    console.log(`Audio Data Starts at Offset: ${offset}`);    // 音频数据部分可以创建一个新的TypedArray视图    const audioData = new Int16Array(arrayBuffer, offset, dataChunkSize / (bitsPerSample / 8));    // console.log('First 10 audio samples:', audioData.slice(0, 10));    return {

以上就是如何利用JavaScript的数组缓冲和视图处理二进制数据,以及它在网络通信或文件解析中的使用?的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1522684.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月20日 15:15:21
下一篇 2025年12月20日 15:15:36

相关推荐

  • 为什么前端固定定位会发生移动问题?

    前端固定定位为什么会出现移动现象? 在进行前端开发时,我们经常会使用CSS中的position属性来控制元素的定位。其中,固定定位(position: fixed)是一种常用的定位方式,它可以让元素相对于浏览器窗口进行定位,保持在页面的固定位置不动。 然而,有时候我们会遇到一个问题:在使用固定定位时…

    2025年12月24日
    000
  • 从初学到专业:掌握这五种前端CSS框架

    CSS是网站设计中重要的一部分,它控制着网站的外观和布局。前端开发人员为了让页面更加美观和易于使用,通常使用CSS框架。这篇文章将带领您了解这五种前端CSS框架,从入门到精通。 Bootstrap Bootstrap是最受欢迎的CSS框架之一。它由Twitter公司开发,具有可定制的响应式网格系统、…

    2025年12月24日
    200
  • 克服害怕做选择的恐惧症:这五个前端CSS框架将为你解决问题

    选择恐惧症?这五个前端CSS框架能帮你解决问题 近年来,前端开发者已经进入了一个黄金时代。随着互联网的快速发展,人们对于网页设计和用户体验的要求也越来越高。然而,要想快速高效地构建出漂亮的网页并不容易,特别是对于那些可能对CSS编码感到畏惧的人来说。所幸的是,前端开发者们早已为我们准备好了一些CSS…

    2025年12月24日
    200
  • 深入理解CSS框架与JS之间的关系

    深入理解CSS框架与JS之间的关系 在现代web开发中,CSS框架和JavaScript (JS) 是两个常用的工具。CSS框架通过提供一系列样式和布局选项,可以帮助我们快速构建美观的网页。而JS则提供了一套功能强大的脚本语言,可以为网页添加交互和动态效果。本文将深入探讨CSS框架和JS之间的关系,…

    2025年12月24日
    000
  • 项目实践:如何结合CSS和JavaScript打造优秀网页的经验总结

    项目实践:如何结合CSS和JavaScript打造优秀网页的经验总结 随着互联网的快速发展,网页设计已经成为了各行各业都离不开的一项技能。优秀的网页设计可以给用户留下深刻的印象,提升用户体验,增加用户的黏性和转化率。而要做出优秀的网页设计,除了对美学的理解和创意的运用外,还需要掌握一些基本的技能,如…

    2025年12月24日
    200
  • is与where选择器:提升前端编程效率的秘密武器

    is与where选择器:提升前端编程效率的秘密武器 在前端开发中,选择器是一种非常重要的工具。它们用于选择文档中的元素,从而对其进行操作和样式设置。随着前端技术的不断发展,选择器也在不断演化。而其中,is与where选择器成为了提升前端编程效率的秘密武器。 is选择器是CSS Selectors L…

    2025年12月24日
    000
  • 前端技巧分享:使用CSS3 fit-content让元素水平居中

    前端技巧分享:使用CSS3 fit-content让元素水平居中 在前端开发中,我们常常会遇到需要将某个元素水平居中的情况。使用CSS3的fit-content属性可以很方便地实现这个效果。本文将介绍fit-content属性的使用方法,并提供代码示例。 fit-content属性是一个相对于元素父…

    2025年12月24日
    000
  • 前端技术分享:利用fit-content实现页面元素的水平对齐效果

    前端技术分享:利用fit-content实现页面元素的水平对齐效果 在前端开发中,实现页面元素的水平对齐是一个常见的需求。尤其在响应式布局中,我们经常需要让元素根据设备的屏幕大小自动调整位置,使页面更加美观和易读。在本文中,我将分享一种利用CSS属性fit-content来实现页面元素的水平对齐效果…

    2025年12月24日
    000
  • 学完HTML和CSS之后我应该做什么?

    网页开发是一段漫长的旅程,但是掌握了HTML和CSS技能意味着你已经赢得了一半的战斗。这两种语言对于学习网页开发技能来说非常重要和基础。现在不可或缺的是下一个问题,学完HTML和CSS之后我该做什么呢? 对这些问题的答案可以分为2-3个部分,你可以继续练习你的HTML和CSS编码,然后了解在学习完H…

    2025年12月24日
    000
  • 聊聊怎么利用CSS实现波浪进度条效果

    本篇文章给大家分享css 高阶技巧,介绍一下如何使用css实现波浪进度条效果,希望对大家有所帮助! 本文是 CSS Houdini 之 CSS Painting API 系列第三篇。 现代 CSS 之高阶图片渐隐消失术现代 CSS 高阶技巧,像 Canvas 一样自由绘图构建样式! 在上两篇中,我们…

    2025年12月24日 好文分享
    200
  • 13 个实用CSS技巧,助你提升前端开发效率!

    本篇文章整理分享13 个前端可能用得上的 css技巧,包括修改输入占位符样式、多行文本溢出、隐藏滚动条、修改光标颜色等,希望对大家有所帮助! 修改输入占位符样式、多行文本溢出、隐藏滚动条、修改光标颜色、水平和垂直居中。多么熟悉的场景!前端开发者几乎每天都会和它们打交道,本文收集 13 个CSS技巧,…

    2025年12月24日
    000
  • 巧用距离、角度及光影制作炫酷的 3D 文字特效

    如何利用 css 实现3d立体的数字?下面本篇文章就带大家巧用视觉障眼法,构建不一样的 3d 文字特效,希望对大家有所帮助! 最近群里有这样一个有意思的问题,大家在讨论,使用 CSS 3D 能否实现如下所示的效果: 这里的核心难点在于,如何利用 CSS 实现一个立体的数字?CSS 能做到吗? 不是特…

    2025年12月24日 好文分享
    000
  • CSS高阶技巧:实现图片渐隐消的多种方法

    将专注于实现复杂布局,兼容设备差异,制作酷炫动画,制作复杂交互,提升可访问性及构建奇思妙想效果等方面的内容。 在兼顾基础概述的同时,注重对技巧的挖掘,结合实际进行运用,欢迎大家关注。 正文从这里开始。 在过往,我们想要实现一个图片的渐隐消失。最常见的莫过于整体透明度的变化,像是这样: 立即学习“前端…

    2025年12月24日 好文分享
    000
  • 聊聊CSS中怎么让auto height支持过渡动画

    css如何让auto height完美支持过渡动画?下面本篇文章带大家聊聊css中让auto height支持过渡动画的方法,希望对大家有所帮助! 众所周知,高度在设置成auto关键词时是不会触发transition过渡动画的,下面是伪代码 div{ height: 0; transition: 1…

    2025年12月24日 好文分享
    000
  • 看看这些前端面试题,带你搞定高频知识点(一)

    每天10道题,100天后,搞定所有前端面试的高频知识点,加油!!!,在看文章的同时,希望不要直接看答案,先思考一下自己会不会,如果会,自己的答案是什么?想过之后再与答案比对,是不是会更好一点,当然如果你有比我更好的答案,欢迎评论区留言,一起探讨技术之美。 面试官:给定一个元素,如何实现水平垂直居中?…

    2025年12月24日 好文分享
    300
  • 看看这些前端面试题,带你搞定高频知识点(二)

    每天10道题,100天后,搞定所有前端面试的高频知识点,加油!!!,在看文章的同时,希望不要直接看答案,先思考一下自己会不会,如果会,自己的答案是什么?想过之后再与答案比对,是不是会更好一点,当然如果你有比我更好的答案,欢迎评论区留言,一起探讨技术之美。 面试官:页面导入样式时,使用 link 和 …

    2025年12月24日 好文分享
    200
  • 看看这些前端面试题,带你搞定高频知识点(三)

    每天10道题,100天后,搞定所有前端面试的高频知识点,加油!!!,在看文章的同时,希望不要直接看答案,先思考一下自己会不会,如果会,自己的答案是什么?想过之后再与答案比对,是不是会更好一点,当然如果你有比我更好的答案,欢迎评论区留言,一起探讨技术之美。 面试官:清除浮动有哪些方式? 我:呃~,浮动…

    2025年12月24日 好文分享
    000
  • 看看这些前端面试题,带你搞定高频知识点(四)

    每天10道题,100天后,搞定所有前端面试的高频知识点,加油!!!,在看文章的同时,希望不要直接看答案,先思考一下自己会不会,如果会,自己的答案是什么?想过之后再与答案比对,是不是会更好一点,当然如果你有比我更好的答案,欢迎评论区留言,一起探讨技术之美。 面试官:请你谈一下自适应(适配)的方案 我:…

    2025年12月24日 好文分享
    000
  • 看看这些前端面试题,带你搞定高频知识点(五)

    每天10道题,100天后,搞定所有前端面试的高频知识点,加油!!!,在看文章的同时,希望不要直接看答案,先思考一下自己会不会,如果会,自己的答案是什么?想过之后再与答案比对,是不是会更好一点,当然如果你有比我更好的答案,欢迎评论区留言,一起探讨技术之美。 面试官:css 如何实现左侧固定 300px…

    2025年12月24日 好文分享
    000
  • css实现登录按钮炫酷效果(附代码实例)

    今天在网上看到一个炫酷的登录按钮效果;初看时感觉好牛掰;但是一点一点的抛开以后发现,并没有那么难;我会将全部代码贴出来;如果有不对的地方,大家指点一哈。 分析 我们抛开before不谈的话;其实原理和就是通过背景大小以及配合位置达到颜色渐变的效果。 text-transform: uppercase…

    2025年12月24日
    000

发表回复

登录后才能评论
关注微信