LOADING
1681 words
8 minutes
模糊背景
2025-10-25

Canvas

基本用法

通过 canvas 画一个矩形

<!-- 创建 canvas 画布 -->
<canvas id="canvas" width="500" height="500"></canvas>
<script>
//获得 canvas 元素
const canvas = document.getElementById("canvas");
// 获得 canvas 上下文(好比画笔)
const ctx = canvas.getContext("2d");
// 设置填充颜色
ctx.fillStyle = "green";
// 绘制矩形,起点位置(10,10),宽150 高100
ctx.fillRect(10, 10, 150, 100);
</script>

canvas 操作必须的两步,创建 canvas 画布,获得 canvas 上下文。根据需要画的内容选择相应 canvas api 来绘制图形

canvas 图片操作

util.js
export function blur(src) {
//设置 canvas 画布
const canvas = document.querySelector("#canvas");
canvas.width = 100;
canvas.height = 100;
// 获得 canvas 上下文
const ctx = canvas.getContext("2d");
//Image()函数将会创建一个新的HTMLImageElement实例。它的功能等价于 document.createElement('img')
const img = new Image();
img.src = src;
// 添加属性crossorigin,增加img元素的跨域支持,设置值为anonymous,表示对此元素的 CORS 请求将不设置凭据标志
img.setAttribute("crossorigin", "anonymous");
//当img元素加载完成时执行
img.onload = function () {
const { width, height } = canvas;
//图片裁剪操作
ctx.drawImage(img, 0, 0, img.width, img.height, 5, 5, width, height);
};
}
const src =
" https://tse1-mm.cn.bing.net/th?id=OIP.M2dHJdmuNPhuODWuMLIK_gHaEo&w=170&h=106&c=8&rs=1&qlt=90&dpr=1.25&pid=3.1&rm=2 ";
blur(src);

在上面的代码中出现了 drawImage api,接下来对这个 api 做详细说明。> Canvas 2D API 中的 CanvasRenderingContext2D.drawImage() 方法提供了多种方式在 Canvas 上绘制图像。根据参数个数可分为三种用法

//绘制图片
ctx.drawImage(image, dx, dy);
/*
image:绘制到上下文的元素,允许任何的 canvas 图像源,例如 HTMLImageElement、HTMLVideoElement、HTMLCanvasElement
dx:image的左上角在目标canvas上 X 轴坐标
dy:image的左上角在目标canvas上 Y 轴坐标。
*/
//图片缩放
ctx.drawImage(image, dx, dy, dWidth, dHeight);
/*
dWidth:image在目标canvas上绘制的宽度
dHeight:image在目标canvas上绘制的高度
*/
//图片裁剪
ctx.drawImage(
image,
//裁剪部分,前面两个参数是裁剪起始坐标,后面两个参数是裁剪的宽高
sx,
sy,
sWidth,
sHeight,
//绘制缩放部分,前面两个参数是图片绘制的起始坐标,后面两个参数是缩放比例
dx,
dy,
dWidth,
dHeight
);

图片裁剪可以通过以下图片来理解:

背景设置和高斯模糊

通过 js 来动态加载背景图片,核心原理就是通过 canvas 将 HTMLImageElement 转变为 base64 编码格式的图片,已背景图片的方式插入到指定的 div 内。

img.onload = function () {
const { width, height } = canvas;
ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, width, height);
//将图片转变为base64的编码格式
let imgSrc = canvas.toDataURL();
//设置背景图片
ele.style.backgroundImage = "url(" + imgSrc + ")";
};

图片的像素级操作及高斯模糊

canvas 中获得图片的像素信息,可以通过 getImageData 方法获得。CanvasRenderingContext2D.getImageData()返回一个ImageData对象,用来描述 canvas 区域隐含的像素数据,这个区域通过矩形表示,起始点为*(sx, sy)、_宽为_sw、_高为_sh。*ImageData 对象描述 canvas元素的一个隐含像素数据的区域,ImageData.data返回一个一维数组 Uint8ClampedArray,包含以 RGBA 顺序的数据,数据使用 0 至 255(包含)的整数表示。关于上面的描述我们可以从代码中进行验证

const canvas = document.getElementById("canvas");
canvas.width = 500;
canvas.height = 500;
const ctx = canvas.getContext("2d");
const img = new Image();
img.src = "./images/lena.jpg";
img.setAttribute("crossorigin", "anonymous");
img.onload = function () {
ctx.drawImage(img, 0, 0);
//得到第一个像素点的RGBA值
const imgData3 = ctx.getImageData(0, 0, 1, 1);
console.log("🚀 ~ file: 01-ImageData.html ~ line 56 ~ imgData3", imgData3);
};

从结果看 imageData.data 存储的确实和屏幕上的像素点信息一致知道怎么拿到像素点的信息后,接下来需要做的是对像素点执行高斯模糊的算法,高斯模糊核心思想就是对选取的像素点及模糊半径内的像素点按照权重相加并归一化(权重按照正态分布取值)得到后的值作为像素点模糊值。

//高斯模糊
function gaussBlur(imgData) {
var pixes = imgData.data;
var width = imgData.width;
var height = imgData.height;
var gaussMatrix = [],
gaussSum = 0,
x,
y,
r,
g,
b,
a,
i,
j,
k,
len;
var radius = 10;
var sigma = 5; //标准差σ,σ描述正态分布资料数据分布的离散程度,σ越大,数据分布越分散,σ越小,数据分布越集中
a = 1 / (Math.sqrt(2 * Math.PI) * sigma);
b = -1 / (2 * sigma * sigma);
//生成高斯矩阵
for (i = 0, x = -radius; x <= radius; x++, i++) {
g = a * Math.exp(b * x * x); //标准正态分布函数,正态分布记作N(μ,σ2),其中μ = 0 ,σ2 = 5
gaussMatrix[i] = g;
gaussSum += g;
}
//归一化, 保证高斯矩阵的值在[0,1]之间
for (i = 0, len = gaussMatrix.length; i < len; i++) {
gaussMatrix[i] /= gaussSum;
}
//x 方向一维高斯运算
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
r = g = b = a = 0;
gaussSum = 0;
for (j = -radius; j <= radius; j++) {
k = x + j;
if (k >= 0 && k < width) {
//确保 k 没超出 x 的范围
//r,g,b,a 四个一组
i = (y * width + k) * 4;
r += pixes[i] * gaussMatrix[j + radius];
g += pixes[i + 1] * gaussMatrix[j + radius];
b += pixes[i + 2] * gaussMatrix[j + radius];
// a += pixes[i + 3] * gaussMatrix[j];
gaussSum += gaussMatrix[j + radius];
}
}
i = (y * width + x) * 4;
// 除以 gaussSum 是为了消除处于边缘的像素, 高斯运算不足的问题
// console.log(gaussSum)
pixes[i] = r / gaussSum;
pixes[i + 1] = g / gaussSum;
pixes[i + 2] = b / gaussSum;
// pixes[i + 3] = a ;
}
}
//y 方向一维高斯运算
for (x = 0; x < width; x++) {
for (y = 0; y < height; y++) {
r = g = b = a = 0;
gaussSum = 0;
for (j = -radius; j <= radius; j++) {
k = y + j;
if (k >= 0 && k < height) {
//确保 k 没超出 y 的范围
i = (k * width + x) * 4;
r += pixes[i] * gaussMatrix[j + radius];
g += pixes[i + 1] * gaussMatrix[j + radius];
b += pixes[i + 2] * gaussMatrix[j + radius];
// a += pixes[i + 3] * gaussMatrix[j];
gaussSum += gaussMatrix[j + radius];
}
}
i = (y * width + x) * 4;
pixes[i] = r / gaussSum;
pixes[i + 1] = g / gaussSum;
pixes[i + 2] = b / gaussSum;
}
}
//end
return imgData;
}
const imgBox = document.querySelector(".player-background-image");
const src =
" https://tse1-mm.cn.bing.net/th?id=OIP.M2dHJdmuNPhuODWuMLIK_gHaEo&w=170&h=106&c=8&rs=1&qlt=90&dpr=1.25&pid=3.1&rm=2 ";
export function blur(ele, src) {
const canvas = document.createElement("canvas"); canvas.width = 100;
canvas.height = 100;
const ctx = canvas.getContext("2d");
//Image()函数将会创建一个新的HTMLImageElement实例。它的功能等价于 document.createElement('img')
const img = new Image();
img.src = src;
// 添加属性crossorigin,增加img元素的跨域支持,设置值为anonymous,表示对此元素的 CORS 请求将不设置凭据标志
img.setAttribute("crossorigin", "anonymous");
//当img元素加载完成时执行
img.onload = function () {
const { width, height } = canvas;
ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, width, height);
let imgData = ctx.getImageData(0, 0, width, height);
//高斯模糊
let gaussData = gaussBlur(imgData);
ctx.putImageData(gaussData, 0, 0);
//将图片转变为base64的编码格式
let imgSrc = canvas.toDataURL();
//设置背景图片
ele.style.backgroundImage = "url(" + imgSrc + ")";
};
}
blur(imgBox, src);
模糊背景
/posts/2025-10-25/模糊背景/
Author
Atopos
Published at
2025-10-25
License
CC BY-NC-SA 4.0

Some information may be outdated