Canvas 2D 样式、渐变与图案:管理绘图状态和跨域图片
本文目录
Canvas 2D 的绘制上下文带有一组持续生效的状态。设置颜色、线宽、阴影、裁剪或变换后,后续绘制都会继承它;忘记恢复状态是图表和小游戏里“后面所有图形都变色/错位”的常见来源。把状态边界和资源加载写清楚,比逐个属性死记更可靠。
颜色、透明度和线型
fillStyle 用于填充,strokeStyle 用于描边,可接收 CSS 颜色、CanvasGradient 或 CanvasPattern。globalAlpha 会与样式自身的 alpha 一起影响绘制结果;若只想让一个样式半透明,也可把 alpha 放进 CSS 颜色。它们都属于上下文状态,不能假定一次设置只影响紧接着的命令。
线条由 lineWidth、lineCap、lineJoin 与 miterLimit 控制;setLineDash() 设定线段和间隔的长度序列,lineDashOffset 移动虚线起点。线宽是路径两侧各延伸一半,抗锯齿会让边缘像素呈现部分颜色;精确像素对齐要结合实际缩放和设备像素比验证,不要照搬单一半像素口诀。
渐变、纹理和阴影
createLinearGradient()、createRadialGradient() 和 createConicGradient() 创建渐变对象;通过 addColorStop(0..1, color) 设置色标,再把渐变赋给 fillStyle/strokeStyle。渐变坐标是在创建时确定的用户空间坐标,不是每个图形自己的百分比。重复位置可制造硬边分界,但顺序会影响结果。
createPattern(image, repetition) 创建重复图案,图片需先加载完成;返回值可能是 null。shadowColor、shadowBlur、shadowOffsetX/Y 提供阴影,但大范围模糊和多层阴影成本较高,动态画布不应无节制地对每个对象启用。路径可使用 fill('evenodd') 选择偶奇填充规则,为复合路径形成孔洞。
function paintCard(ctx) {
const gradient = ctx.createLinearGradient(0, 0, 220, 0);
gradient.addColorStop(0, '#167d72');
gradient.addColorStop(1, '#45b8ac');
ctx.save();
try {
ctx.globalAlpha = 0.92;
ctx.fillStyle = gradient;
ctx.shadowColor = 'rgb(16 42 67 / 18%)';
ctx.shadowBlur = 8;
ctx.shadowOffsetY = 3;
ctx.fillRect(12, 12, 196, 96);
} finally {
ctx.restore();
}
}
save()/restore() 按栈保存和恢复大部分绘图状态,例如变换、裁剪、alpha、合成、线型、阴影与样式;它们不是撤销画面像素的历史记录。用 try/finally 保证异常时也恢复。当前路径应在每个图形前显式 beginPath(),不要把 save() 当作清除当前路径的替代。
图片加载与跨域安全
用 drawImage() 把图片、视频帧或另一个 canvas 绘入当前画布;确认资源已解码、尺寸有效再绘制。跨域图像只有在来源服务器通过 CORS 允许后,才可安全读取像素。若把未经 CORS 许可的跨域图片画入画布,画布会变成 tainted;之后 getImageData()、toBlob() 或 toDataURL() 等读取/导出操作会抛出 SecurityError。
若目标站支持 CORS,可在设置图片 src 前设置 crossOrigin = 'anonymous',并由服务器返回适当的 Access-Control-Allow-Origin。前端属性本身不能替对方服务器授予许可;不要通过代理用户提供的任意 URL 来绕过限制,这会引出 SSRF 和隐私风险。对跨域失败提供说明和本地文件选择等正常替代路径。
async function createTexture(ctx, url) {
const image = new Image();
image.crossOrigin = 'anonymous';
image.src = url;
await image.decode();
const pattern = ctx.createPattern(image, 'repeat');
if (!pattern) throw new Error('图案资源尚未就绪');
ctx.save();
try {
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 240, 120);
} finally {
ctx.restore();
}
}
不要把 clearRect()、ctx.reset() 和重设 canvas 尺寸混为一谈:前者擦一块像素但保留状态,reset 清空像素并恢复默认上下文状态,重设 width/height 还改变位图分辨率并清空绘图。根据是否需要保留变换、裁剪和样式,选择最小影响的操作。
- 每个独立对象只修改需要的状态,并用 save/restore 限定作用范围。
- 检查渐变色标范围、纹理加载失败、图片 decode 拒绝和空图案返回值。
- 用不支持 CORS 的外域图片验证导出失败提示,不要吞掉 SecurityError。
- 在高频动画中对比关闭阴影/复杂渐变后的帧耗时。
- 修改清理逻辑后确认画布尺寸、变换、裁剪和颜色状态均符合预期。
参考:MDN Canvas 样式与颜色、MDN createPattern、MDN Canvas reset、MDN 跨域图像与 Canvas、MDN toDataURL、MDN Canvas 基础。
Live Notes
留言提交后会先进入审核,审核通过后对其他读者公开。