HTML5 Canvas 无原生 glassRefraction 滤镜,需用 getImageData 读取背景像素,依位移贴图计算偏移量,再通过 putImageData 写回模拟折射效果。

HTML5 Canvas 里没有原生 glassRefraction 滤镜
Canvas 2D 上下文不支持类似 SVG 或 CSS 的声明式滤镜(如 filter: url(#refraction)),也没有内置的玻璃折射效果 API。所谓“HTML5 玻璃折射”,实际是靠手动采样、偏移、混合像素实现的模拟效果,本质是图像处理而非真光学折射。
用 getImageData + 偏移采样模拟折射
核心思路:把背景图绘制到离屏 canvas,再在主 canvas 上对每个目标像素,根据“玻璃表面法线”或简单位移规则,读取背景图中偏移后的像素值。常见做法是用一张位移贴图(displacement map)控制偏移量。
-
createImageData和getImageData用于读写像素;注意跨域图片需设置crossOrigin="anonymous" - 偏移量通常取自另一张灰度图的 R/G 通道值,乘以缩放系数(如
dx = (r - 128) * 0.5),避免越界访问 - 必须用
putImageData批量写回,逐像素fillRect性能极差 - 示例关键片段:
const bgData = bgCtx.getImageData(0, 0, w, h);
const outData = ctx.createImageData(w, h);
for (let i = 0; i < data.length; i += 4) {
const x = (i/4) % w;
const y = Math.floor((i/4) / w);
const dx = (displaceData.data[i] - 128) * 0.3;
const dy = (displaceData.data[i+1] - 128) * 0.3;
const srcX = Math.max(0, Math.min(w-1, x + dx));
const srcY = Math.max(0, Math.min(h-1, y + dy));
const srcIdx = (srcY * w + srcX) * 4;
outData.data[i] = bgData.data[srcIdx];
outData.data[i+1] = bgData.data[srcIdx+1];
outData.data[i+2] = bgData.data[srcIdx+2];
outData.data[i+3] = 255;
}
WebGL 是更可行的玻璃折射路径
Canvas 2D 的像素操作在中等尺寸(如 800×600)上已明显卡顿;真正可交互的玻璃效果(比如随鼠标扭曲的玻璃球)必须用 WebGL,通过 fragment shader 实时计算折射向量。
- 需要定义顶点着色器传入 UV 和法线(或用球面坐标生成),片元着色器中用
refract()GLSL 函数 - 依赖
THREE.js可简化:用MeshStandardMaterial配合envMap模拟反射+折射,但纯折射仍需自定义 shader - 注意:WebGL 纹理默认不支持重复采样(
WRAP),折射出界时易黑边,需手动做mod或 clamp 处理 - 移动端兼容性需检查
OES_standard_derivatives扩展是否可用,否则法线微分不准
用 CSS backdrop-filter 快速模拟毛玻璃,但不是折射
如果目标只是「玻璃感」而非物理准确的折射,backdrop-filter: blur(8px) 是最轻量方案,但它只模糊背景,不产生位移、色散或焦散效果。
- 仅适用于元素后方内容为同文档 DOM 的场景;iframe 或跨域内容不生效
- Safari 和 Chrome 支持较好,Firefox 默认禁用,需用户手动开启
layout.css.backdrop-filter.enabled - 与
opacity同时使用会触发渲染层叠问题,建议用rgba()控制透明度 - 无法响应鼠标位置动态变形——这是和真折射效果最根本的差距
真实折射要算光线路径,Canvas 2D 只能骗眼,WebGL 才算入门。位移贴图的精度、法线生成方式、以及如何避免采样撕裂,这些细节比“怎么做”更决定最终像不像玻璃。










