用 Canvas 在浏览器里生成封面图:从渐变背景到圆角矩形的完整实践

举报
yd_269248920 发表于 2026/10/22 16:44:52 2026/10/22
【摘要】 不依赖任何第三方库,用 Canvas API 生成符合尺寸要求的封面图:渐变背景、网格装饰、圆角矩形兼容处理、字体与 DPR 适配,以及导出上传时的四个常见坑和完整可运行代码。

一、为什么要用代码生成封面图

写技术文章、做课程封面、给项目生成预览图时,经常需要一张尺寸固定、风格统一的图片。手工用设计工具做,每换一次文字就要重做;用图片素材站找,又容易撞图或者有授权问题。

其实浏览器自带的能力就够用了:Canvas API 可以在几十行代码里画出一张符合尺寸要求、内容完全自定义的图片,而且不依赖任何第三方库。

二、基本骨架

核心就三步:创建画布、绘制内容、导出成图片。

const canvas = document.createElement('canvas');
canvas.width = 800;
canvas.height = 450;
const ctx = canvas.getContext('2d');

// ……在这里绘制

const dataUrl = canvas.toDataURL('image/png');

导出这一步要注意:toDataURL 返回的是 base64 字符串,可以直接塞进 img 的 src,也可以转成 Blob 再上传。

三、画出需要的元素

1. 渐变背景。用 createLinearGradient 做斜向渐变,比纯色更有层次:

const g = ctx.createLinearGradient(0, 0, 800, 450);
g.addColorStop(0, '#0b1b33');
g.addColorStop(0.55, '#123055');
g.addColorStop(1, '#0a1626');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 800, 450);

2. 网格线。用循环画等距线条,透明度调低,作为装饰不抢主体:

ctx.strokeStyle = 'rgba(90,160,255,0.14)';
ctx.lineWidth = 1;
for (let x = 0; x <= 800; x += 40) {
  ctx.beginPath();
  ctx.moveTo(x, 0);
  ctx.lineTo(x, 450);
  ctx.stroke();
}

3. 圆角矩形。现代浏览器支持 ctx.roundRect,但为了兼容旧环境,建议做一次能力检测:

ctx.beginPath();
if (ctx.roundRect) {
  ctx.roundRect(x, y, w, h, 10);
} else {
  ctx.rect(x, y, w, h);
}
ctx.fill();
ctx.stroke();

4. 文字。字体要显式指定,否则不同系统渲染差异很大。中文字体建议带上回退栈:

ctx.font = 'bold 40px "Microsoft YaHei", "PingFang SC", sans-serif';
ctx.fillStyle = '#ffffff';
ctx.fillText('标题文字', 570, 180);

四、几个容易踩的坑

坑一:导出后图片模糊。Canvas 默认按 CSS 像素绘制,在高分屏或需要放大显示时会糊。解决办法是按 devicePixelRatio 放大画布尺寸,再用 ctx.scale 缩放坐标系:

const dpr = window.devicePixelRatio || 1;
canvas.width = 800 * dpr;
canvas.height = 450 * dpr;
canvas.style.width = '800px';
canvas.style.height = '450px';
ctx.scale(dpr, dpr);

坑二:上传平台有最小尺寸要求。很多平台对封面有下限,比如要求大于等于 240×135。建议一开始就按 16:9 的较大尺寸(如 800×450)生成,缩小时不会失真。

坑三:toDataURL 体积偏大。PNG 无损但体积大,纯色较多的图可以用 toBlob 指定 jpeg 和压缩质量:

canvas.toBlob((blob) => {
  // blob 可直接用 FormData 上传
}, 'image/jpeg', 0.88);

坑四:字体加载时机。如果用到自定义字体,要在 document.fonts.ready 之后再绘制,否则会回退到默认字体。

五、完整可运行示例

把下面这段直接贴进浏览器控制台,就能得到一张 800×450 的技术风封面:

function makeCover(title, subtitle) {
  const W = 800, H = 450;
  const c = document.createElement('canvas');
  c.width = W; c.height = H;
  const ctx = c.getContext('2d');

  const g = ctx.createLinearGradient(0, 0, W, H);
  g.addColorStop(0, '#0b1b33');
  g.addColorStop(1, '#0a1626');
  ctx.fillStyle = g;
  ctx.fillRect(0, 0, W, H);

  ctx.strokeStyle = 'rgba(90,160,255,0.14)';
  for (let x = 0; x <= W; x += 40) {
    ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, H); ctx.stroke();
  }

  ctx.fillStyle = '#2f7cff';
  ctx.fillRect(0, 0, 10, H);

  ctx.fillStyle = '#ffffff';
  ctx.font = 'bold 40px "Microsoft YaHei", sans-serif';
  ctx.fillText(title, 60, 200);

  ctx.fillStyle = '#8fc3ff';
  ctx.font = '20px "Microsoft YaHei", sans-serif';
  ctx.fillText(subtitle, 60, 250);

  return c.toDataURL('image/png');
}

console.log(makeCover('前端排障实录', 'Canvas 生成封面'));

六、小结

Canvas 生成图片的价值不在于替代设计工具,而在于把"尺寸固定、内容可变"的图变成可编程的产物。批量生成、动态文字、CI 流程里自动出图,这些场景用代码远比手工高效。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。