导读:本期聚焦于小伙伴创作的《HTML5怎么实现水波荡漾滤镜效果,有哪些实用应用方法》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《HTML5怎么实现水波荡漾滤镜效果,有哪些实用应用方法》有用,将其分享出去将是对创作者最好的鼓励。

水波荡漾滤镜是网页视觉特效中常见的效果类型,通过HTML5的相关图形处理能力可以实现非常自然的水波动态效果,适配不同的网页场景需求。实现该效果的核心逻辑是模拟水波传播的位移变化,结合像素处理或着色器计算完成最终渲染。

水波滤镜的核心实现原理

水波荡漾的本质是模拟液体表面受到扰动后,波纹向外扩散的过程。在HTML5技术体系中,主要有两种实现思路,一种是使用Canvas的2D上下文逐像素处理图像,另一种是使用WebGL结合着色器程序完成高效计算。

两种方案各有适用场景,Canvas方案兼容性更好,适合简单的静态图像水波效果;WebGL方案性能更强,适合需要实时交互、多波纹叠加的复杂场景。

Canvas 2D实现方案

Canvas实现水波滤镜的核心是操作ImageData对象,通过计算每个像素的偏移量,模拟波纹带来的视觉位移。以下是一段基础的水波效果实现代码:

// 获取Canvas元素和上下文
const canvas = document.getElementById('waveCanvas');
const ctx = canvas.getContext('2d');
// 设置画布尺寸
canvas.width = 800;
canvas.height = 400;
// 加载待处理的图像
const img = new Image();
img.src = 'https://ipipp.com/demo/water-bg.jpg';
img.onload = function() {
    // 绘制原始图像
    ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
    // 获取图像像素数据
    const originalData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    const originalPixels = originalData.data;
    // 水波参数配置
    const waveConfig = {
        centerX: canvas.width / 2, // 波纹中心X坐标
        centerY: canvas.height / 2, // 波纹中心Y坐标
        amplitude: 15, // 波纹振幅
        wavelength: 80, // 波纹波长
        phase: 0 // 相位,用于动态效果
    };
    // 启动动画循环
    function animateWave() {
        // 创建新的像素数据对象
        const newData = ctx.createImageData(canvas.width, canvas.height);
        const newPixels = newData.data;
        // 遍历每个像素计算偏移
        for (let y = 0; y < canvas.height; y++) {
            for (let x = 0; x < canvas.width; x++) {
                // 计算当前像素到波纹中心的距离
                const dx = x - waveConfig.centerX;
                const dy = y - waveConfig.centerY;
                const distance = Math.sqrt(dx * dx + dy * dy);
                // 计算波纹带来的偏移量
                const offset = waveConfig.amplitude * Math.sin(distance / waveConfig.wavelength * 2 * Math.PI + waveConfig.phase);
                // 计算原始像素坐标
                const srcX = x + dx / distance * offset;
                const srcY = y + dy / distance * offset;
                // 边界判断,避免越界
                if (srcX >= 0 && srcX < canvas.width && srcY >= 0 && srcY < canvas.height) {
                    const srcIdx = (Math.floor(srcY) * canvas.width + Math.floor(srcX)) * 4;
                    const dstIdx = (y * canvas.width + x) * 4;
                    // 复制像素值
                    newPixels[dstIdx] = originalPixels[srcIdx];
                    newPixels[dstIdx + 1] = originalPixels[srcIdx + 1];
                    newPixels[dstIdx + 2] = originalPixels[srcIdx + 2];
                    newPixels[dstIdx + 3] = originalPixels[srcIdx + 3];
                }
            }
        }
        // 将处理后的像素绘制到画布
        ctx.putImageData(newData, 0, 0);
        // 更新相位,实现动态效果
        waveConfig.phase += 0.1;
        // 循环调用动画
        requestAnimationFrame(animateWave);
    }
    // 启动动画
    animateWave();
};

WebGL结合着色器实现方案

WebGL方案通过GPU并行计算处理像素,性能远高于Canvas 2D方案,适合需要实时交互的场景。核心是通过顶点着色器和片元着色器实现水波位移计算,以下是对应的实现代码:

<canvas id="webglWave" width="800" height="400"></canvas>
<script>
// 获取WebGL上下文
const glCanvas = document.getElementById('webglWave');
const gl = glCanvas.getContext('webgl');
if (!gl) {
    console.error('当前浏览器不支持WebGL');
}
// 顶点着色器代码
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 vec2 u_center;
    uniform float u_amplitude;
    uniform float u_wavelength;
    uniform float u_phase;
    uniform vec2 u_resolution;
    varying vec2 v_texCoord;
    void main() {
        // 将纹理坐标转换为画布像素坐标
        vec2 pixelCoord = v_texCoord * u_resolution;
        // 计算到波纹中心的距离
        float dx = pixelCoord.x - u_center.x;
        float dy = pixelCoord.y - u_center.y;
        float distance = sqrt(dx * dx + dy * dy);
        // 计算波纹偏移
        float offset = u_amplitude * sin(distance / u_wavelength * 6.28318 + u_phase);
        // 计算原始纹理坐标
        vec2 srcCoord = v_texCoord + (vec2(dx, dy) / distance * offset) / u_resolution;
        // 采样纹理颜色
        gl_FragColor = texture2D(u_texture, srcCoord);
    }
`;
// 创建着色器函数
function createShader(gl, type, source) {
    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 = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
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,
    1, -1,
    1, 1
]), gl.STATIC_DRAW);
const positionLocation = gl.getAttribLocation(program, 'a_position');
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
const texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
    0, 0,
    1, 0,
    0, 1,
    0, 1,
    1, 0,
    1, 1
]), gl.STATIC_DRAW);
const texCoordLocation = gl.getAttribLocation(program, 'a_texCoord');
gl.enableVertexAttribArray(texCoordLocation);
gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
// 加载纹理图像
const texture = gl.createTexture();
const textureImg = new Image();
textureImg.crossOrigin = 'anonymous';
textureImg.src = 'https://ipipp.com/demo/water-bg.jpg';
textureImg.onload = function() {
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textureImg);
    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);
    // 设置uniform变量
    const centerLocation = gl.getUniformLocation(program, 'u_center');
    gl.uniform2f(centerLocation, glCanvas.width / 2, glCanvas.height / 2);
    const amplitudeLocation = gl.getUniformLocation(program, 'u_amplitude');
    gl.uniform1f(amplitudeLocation, 20);
    const wavelengthLocation = gl.getUniformLocation(program, 'u_wavelength');
    gl.uniform1f(wavelengthLocation, 100);
    const resolutionLocation = gl.getUniformLocation(program, 'u_resolution');
    gl.uniform2f(resolutionLocation, glCanvas.width, glCanvas.height);
    const phaseLocation = gl.getUniformLocation(program, 'u_phase');
    let phase = 0;
    // 动画循环
    function render() {
        phase += 0.1;
        gl.uniform1f(phaseLocation, phase);
        gl.drawArrays(gl.TRIANGLES, 0, 6);
        requestAnimationFrame(render);
    }
    render();
};
</script>

水波滤镜的实用应用技巧

水波滤镜在实际开发中有多种应用方式,可以根据场景需求调整参数和交互逻辑:

  • 交互式水波效果:监听画布的click或者mousemove事件,将点击位置作为波纹中心,实现点击水面产生涟漪的效果,提升用户交互体验。
  • 多波纹叠加:维护一个波纹数组,记录每个波纹的中心、振幅、波长等参数,在每一帧计算时叠加所有波纹的偏移量,实现多个涟漪同时扩散的效果。
  • 性能优化技巧:Canvas方案可以只处理波纹影响区域的像素,不需要遍历整个画布;WebGL方案可以通过调整着色器精度、减少uniform变量更新频率提升性能。
  • 场景适配:游戏场景中的水波可以结合角色移动触发,视觉特效中可以调整振幅和波长匹配整体设计风格,表单交互中可以在输入框聚焦时添加轻微水波效果作为反馈。

两种方案的选择建议

如果项目只需要简单的静态水波效果,或者需要兼容低版本浏览器,优先选择Canvas 2D方案;如果需要实时交互、多波纹叠加、高性能渲染的场景,比如网页游戏、复杂视觉特效页面,优先选择WebGL方案。开发者可以根据项目的实际需求灵活选择实现路径,快速完成水波荡漾滤镜的开发和适配。

HTML5水波滤镜CanvasWebGLshader修改时间:2026-06-10 23:16:06

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