2020-08-15 05:42:03
在Canvas中实现图片动态模糊效果的核心方法是利用卷积核(如高斯卷积核)处理像素数据,并通过时间循环逐步增强模糊程度。 以下是具体实现步骤与关键代码说明:
一、基础实现步骤创建Canvas并加载图像
创建<canvas>元素并获取2D渲染上下文。
使用Image对象加载目标图片,确保图片加载完成后再操作像素数据。
const canvas = document.getElementById('canvas');const ctx = canvas.getContext('2d');const image = new Image();image.onload = function() { canvas.width = image.width; canvas.height = image.height; ctx.drawImage(image, 0, 0); // 后续步骤在此回调中执行};image.src = 'your_image.jpg';获取图像像素数据
使用getImageData(x, y, width, height)获取图像的ImageData对象,其中包含RGBA格式的像素数组。
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);const data = imageData.data; // 一维数组,每4个元素代表一个像素的RGBA值应用卷积核处理像素
卷积核原理:通过矩阵计算每个像素与其周围像素的加权平均值,实现模糊效果。高斯模糊的卷积核权重呈高斯分布(中心权重高,边缘权重低)。
实现步骤:
定义卷积核矩阵(如3x3或5x5)及权重总和。
遍历每个像素,计算其与周围像素的加权和,并归一化(除以权重总和)。
处理边界像素时需特殊判断(如复制边缘像素或忽略超出部分)。
function applyGaussianBlur(data, width, height, kernelSize = 3) { const kernel = createGaussianKernel(kernelSize); // 生成高斯卷积核 const halfSize = Math.floor(kernelSize / 2); const newData = new Uint8ClampedArray(data.length); for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { let r = 0, g = 0, b = 0, a = 0; for (let ky = -halfSize; ky <= halfSize; ky++) { for (let kx = -halfSize; kx <= halfSize; kx++) { const px = x + kx; const py = y + ky; if (px >= 0 && px < width && py >= 0 && py < height) { const idx = ((py * width + px) * 4); const kernelIdx = (ky + halfSize) * kernelSize + (kx + halfSize); const weight = kernel[kernelIdx]; r += data[idx] * weight; g += data[idx + 1] * weight; b += data[idx + 2] * weight; a += data[idx + 3] * weight; } } } const newIdx = (y * width + x) * 4; newData[newIdx] = r; newData[newIdx + 1] = g; newData[newIdx + 2] = b; newData[newIdx + 3] = a; } } return newData;}function createGaussianKernel(size) { const kernel = []; const sigma = size / 3; // 标准差,控制模糊程度 const sum = 0; for (let y = 0; y < size; y++) { for (let x = 0; x < size; x++) { const exponent = -((x - size/2)2 + (y - size/2)2) / (2 * sigma2); const weight = Math.exp(exponent); kernel.push(weight); sum += weight; } } // 归一化 return kernel.map(w => w / sum);}将处理后的数据放回Canvas
使用putImageData(imageData, x, y)将模糊后的像素数据重新绘制到Canvas上。
const blurredData = applyGaussianBlur(data, canvas.width, canvas.height);const blurredImageData = new ImageData(blurredData, canvas.width, canvas.height);ctx.putImageData(blurredImageData, 0, 0);通过requestAnimationFrame循环逐步增强模糊效果,每次迭代应用较小的卷积核或降低权重,实现平滑过渡。
let blurLevel = 0;const maxBlurLevel = 10; // 最大模糊迭代次数function animate() { if (blurLevel >= maxBlurLevel) return; const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; // 每次迭代使用更大的卷积核或调整权重 const currentKernelSize = 3 + Math.floor(blurLevel / 2); const blurredData = applyGaussianBlur(data, canvas.width, canvas.height, currentKernelSize); ctx.putImageData(new ImageData(blurredData, canvas.width, canvas.height), 0, 0); blurLevel++; requestAnimationFrame(animate);}// 在图片加载完成后启动动画image.onload = function() { canvas.width = image.width; canvas.height = image.height; ctx.drawImage(image, 0, 0); requestAnimationFrame(animate);};三、优化与注意事项卷积计算复杂度高,大图像或高模糊度可能导致卡顿。可缩小Canvas尺寸或使用Web Worker处理像素数据。
预计算卷积核避免重复计算。
使用CSS滤镜(如filter: blur(5px))更简单,但无法实现动态控制。
使用现成库(如StackBlur.js)提供高效模糊算法。
确保卷积核不越界,或采用镜像填充、重复填充等策略。
通过上述方法,可在Canvas中实现从清晰到模糊的动态过渡效果,适用于图像预览、过渡动画等场景。