html5滤镜怎样加动态波纹

来源:菜鸟站长作者:永濑头衔:网络博主
导读:本期聚焦于小伙伴创作的《html5滤镜怎样加动态波纹》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《html5滤镜怎样加动态波纹》有用,将其分享出去将是对创作者最好的鼓励。

在html5中实现动态波纹滤镜效果,核心是通过实时修改像素数据或者 shader 计算来模拟波纹的光学变化,常用的实现方案有canvas 2D像素操作和WebGL shader两种,下面分别介绍具体的实现步骤。

html5滤镜怎样加动态波纹

方案一:基于canvas 2D的动态波纹滤镜

实现原理

通过getImageData获取canvas中图像的原始像素数据,根据波纹的数学公式(正弦波叠加)计算每个像素的偏移量,重新映射像素位置后生成新的图像数据,再通过requestAnimationFrame循环更新实现动态效果。

具体步骤

  • 创建canvas元素并获取2D上下文,加载需要添加滤镜的图像资源
  • 定义波纹相关的参数,包括波纹频率、振幅、动画时间偏移量
  • 编写波纹偏移计算函数,根据像素坐标和时间计算新的像素位置
  • 在动画循环中不断更新波纹参数,重新绘制图像实现动态效果

完整代码示例

// 获取canvas和上下文
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
document.body.appendChild(canvas);

// 设置canvas尺寸
canvas.width = 800;
canvas.height = 600;

// 加载图像
const img = new Image();
img.src = 'https://ipipp.com/sample.jpg';
img.onload = () => {
    // 初始绘制图像
    ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
    // 开始动画循环
    animate();
};

// 波纹参数
let time = 0;
const waveFrequency = 0.02; // 波纹频率
const waveAmplitude = 10; // 波纹振幅
const waveSpeed = 0.05; // 波纹速度

// 动画循环函数
function animate() {
    // 获取原始图像像素数据
    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    const data = imageData.data;
    // 创建新的像素数据数组
    const newData = ctx.createImageData(canvas.width, canvas.height);
    const newDataArr = newData.data;

    // 遍历每个像素计算偏移
    for (let y = 0; y < canvas.height; y++) {
        for (let x = 0; x < canvas.width; x++) {
            // 计算波纹偏移量,使用正弦波叠加时间参数
            const offsetX = Math.sin(y * waveFrequency + time) * waveAmplitude;
            const offsetY = Math.cos(x * waveFrequency + time) * waveAmplitude;

            // 计算原始像素坐标,处理边界
            const srcX = Math.min(Math.max(Math.floor(x + offsetX), 0), canvas.width - 1);
            const srcY = Math.min(Math.max(Math.floor(y + offsetY), 0), canvas.height - 1);

            // 获取原始像素索引
            const srcIndex = (srcY * canvas.width + srcX) * 4;
            // 获取目标像素索引
            const destIndex = (y * canvas.width + x) * 4;

            // 复制像素值
            newDataArr[destIndex] = data[srcIndex];
            newDataArr[destIndex + 1] = data[srcIndex + 1];
            newDataArr[destIndex + 2] = data[srcIndex + 2];
            newDataArr[destIndex + 3] = data[srcIndex + 3];
        }
    }

    // 绘制新的像素数据
    ctx.putImageData(newData, 0, 0);
    // 更新时间参数
    time += waveSpeed;
    // 请求下一帧动画
    requestAnimationFrame(animate);
}

方案二:基于WebGL的动态波纹滤镜

实现原理

WebGL通过顶点着色器和片段着色器直接操作GPU渲染流程,在片段着色器中根据波纹公式实时计算每个像素的采样偏移,性能远高于canvas 2D的像素操作方式,适合处理大尺寸图像或者高帧率需求场景。

具体步骤

  • 创建WebGL上下文,编写顶点着色器和片段着色器代码
  • 加载纹理图像,绑定到WebGL纹理单元
  • 定义波纹相关的uniform变量,在动画循环中更新时间参数
  • 持续渲染实现动态波纹效果

完整代码示例

// 创建canvas并获取WebGL上下文
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');
document.body.appendChild(canvas);
canvas.width = 800;
canvas.height = 600;

// 顶点着色器代码
const vertexShaderSource = `
    attribute vec2 a_position;
    attribute vec2 a_texCoord;
    varying vec2 v_texCoord;
    void main() {
        gl_Position = vec4(a_position, 0.0, 1.0);
        v_texCoord = a_texCoord;
    }
`;

// 片段着色器代码,实现波纹效果
const fragmentShaderSource = `
    precision mediump float;
    uniform sampler2D u_texture;
    uniform float u_time;
    uniform float u_frequency;
    uniform float u_amplitude;
    varying vec2 v_texCoord;
    void main() {
        // 计算波纹偏移
        vec2 offset = vec2(
            sin(v_texCoord.y * u_frequency + u_time) * u_amplitude,
            cos(v_texCoord.x * u_frequency + u_time) * u_amplitude
        );
        // 采样纹理颜色
        vec4 color = texture2D(u_texture, v_texCoord + offset);
        gl_FragColor = color;
    }
`;

// 编译着色器函数
function compileShader(gl, source, type) {
    const shader = gl.createShader(type);
    gl.shaderSource(shader, source);
    gl.compileShader(shader);
    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        console.error('着色器编译失败:', gl.getShaderInfoLog(shader));
        gl.deleteShader(shader);
        return null;
    }
    return shader;
}

// 编译链接着色器程序
const vertexShader = compileShader(gl, vertexShaderSource, gl.VERTEX_SHADER);
const fragmentShader = compileShader(gl, fragmentShaderSource, gl.FRAGMENT_SHADER);
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
    console.error('着色器程序链接失败:', gl.getProgramInfoLog(program));
}
gl.useProgram(program);

// 设置顶点和纹理坐标数据
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
    -1, -1,
    1, -1,
    -1, 1,
    1, 1
]), gl.STATIC_DRAW);

const texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
    0, 0,
    1, 0,
    0, 1,
    1, 1
]), gl.STATIC_DRAW);

// 绑定顶点属性
const positionLocation = gl.getAttribLocation(program, 'a_position');
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);

const texCoordLocation = gl.getAttribLocation(program, 'a_texCoord');
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.enableVertexAttribArray(texCoordLocation);
gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);

// 创建纹理并加载图像
const texture = gl.createTexture();
const img = new Image();
img.crossOrigin = 'anonymous';
img.src = 'https://ipipp.com/sample.jpg';
img.onload = () => {
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    // 开始动画
    animate();
};

// 获取uniform变量位置
const timeLocation = gl.getUniformLocation(program, 'u_time');
const frequencyLocation = gl.getUniformLocation(program, 'u_frequency');
const amplitudeLocation = gl.getUniformLocation(program, 'u_amplitude');

// 波纹参数
let time = 0;
const frequency = 20.0;
const amplitude = 0.02;

// 动画循环
function animate() {
    time += 0.05;
    // 设置uniform变量
    gl.uniform1f(timeLocation, time);
    gl.uniform1f(frequencyLocation, frequency);
    gl.uniform1f(amplitudeLocation, amplitude);
    // 绘制
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
    requestAnimationFrame(animate);
}

两种方案对比

对比项canvas 2D方案WebGL方案
实现难度较低,无需掌握着色器语法较高,需要掌握GLSL着色器语法
性能表现较差,处理大图像时帧率下降明显优异,GPU加速渲染,适合高帧率场景
适用场景小尺寸图像、简单波纹效果需求大尺寸图像、视频滤镜、复杂波纹效果需求

注意事项

  • canvas 2D方案中修改像素数据属于CPU操作,图像尺寸超过1000*1000时建议降低波纹计算的采样精度提升性能
  • WebGL方案加载跨域图像时需要服务器配置CORS头,否则纹理无法正确读取
  • 动态波纹的振幅和频率参数需要根据实际显示效果调整,避免出现过度扭曲的情况

html5动态波纹滤镜canvasrequestAnimationFrameWebGL修改时间:2026-06-13 09:54:53

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。