You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在HTML5 Canvas上为圆角图片添加阴影效果

HTML5 Canvas 实现带圆角和完整阴影的图片

你当前代码中圆角处阴影消失的原因是:clip() 会裁剪掉路径之外的所有内容,包括圆角区域的阴影。要同时保留圆角图片和完整的圆角阴影,需要将阴影绘制与圆角图片裁剪分开处理,避免裁剪操作影响阴影显示。

核心解决方案

先绘制带阴影的圆角路径(仅生成阴影,填充透明色),再通过裁剪路径绘制圆角图片,这样阴影会完整保留,图片也会呈现圆角效果。同时要确保图片加载完成后再执行绘制逻辑,避免绘制失败。

修改后的完整代码(浏览器环境)

HTML 部分

<canvas id="mycanvas" width="500" height="500"></canvas>
<div style="display:none;">
  <img id="source" src="https://images.unsplash.com/photo-1534644107580-3a4dbd494a95?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80" width="500" height="500" />
</div>

JavaScript 部分

var canvas = document.getElementById('mycanvas');
const background = document.getElementById("source");
const overlay = new Image(300, 300);
overlay.src = 'https://images.unsplash.com/photo-1518826778770-a729fb53327c?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1887&q=80';

// 等待图片加载完成后再绘制
overlay.onload = function() {
  const ctx = canvas.getContext('2d');
  
  // 绘制背景图
  ctx.drawImage(background, 0, 0, background.width, background.height);

  const x = (canvas.width - overlay.width) / 2;
  const y = (canvas.height - overlay.height) / 2;
  const cornerRadius = 20;

  // 第一步:绘制完整的圆角阴影
  ctx.save(); // 保存当前画布状态
  ctx.shadowColor = 'rgba(0, 0, 0, 1.0)';
  ctx.shadowBlur = 100;
  ctx.shadowOffsetX = 5;
  ctx.shadowOffsetY = 5;

  // 创建与图片区域一致的圆角路径
  ctx.beginPath();
  ctx.moveTo(x + cornerRadius, y);
  ctx.lineTo(x + overlay.width - cornerRadius, y);
  ctx.quadraticCurveTo(x + overlay.width, y, x + overlay.width, y + cornerRadius);
  ctx.lineTo(x + overlay.width, y + overlay.height - cornerRadius);
  ctx.quadraticCurveTo(x + overlay.width, y + overlay.height, x + overlay.width - cornerRadius, y + overlay.height);
  ctx.lineTo(x + cornerRadius, y + overlay.height);
  ctx.quadraticCurveTo(x, y + overlay.height, x, y + overlay.height - cornerRadius);
  ctx.lineTo(x, y + cornerRadius);
  ctx.quadraticCurveTo(x, y, x + cornerRadius, y);
  ctx.closePath();
  
  // 填充透明色,仅显示阴影
  ctx.fillStyle = 'transparent';
  ctx.fill();
  ctx.restore(); // 恢复画布状态,清除阴影设置

  // 第二步:绘制圆角图片
  ctx.beginPath();
  // 重新创建相同的圆角路径用于裁剪
  ctx.moveTo(x + cornerRadius, y);
  ctx.lineTo(x + overlay.width - cornerRadius, y);
  ctx.quadraticCurveTo(x + overlay.width, y, x + overlay.width, y + cornerRadius);
  ctx.lineTo(x + overlay.width, y + overlay.height - cornerRadius);
  ctx.quadraticCurveTo(x + overlay.width, y + overlay.height, x + overlay.width - cornerRadius, y + overlay.height);
  ctx.lineTo(x + cornerRadius, y + overlay.height);
  ctx.quadraticCurveTo(x, y + overlay.height, x, y + overlay.height - cornerRadius);
  ctx.lineTo(x, y + cornerRadius);
  ctx.quadraticCurveTo(x, y, x + cornerRadius, y);
  ctx.closePath();
  ctx.clip(); // 设置裁剪区域
  
  // 绘制图片到裁剪区域内
  ctx.drawImage(overlay, x, y, overlay.width, overlay.height);
}

Node.js Canvas API 适配方案

如果使用 Node.js 的 canvas 模块(npm 包),逻辑与浏览器环境完全一致,仅需调整图片加载方式:

const { createCanvas, loadImage } = require('canvas');
const fs = require('fs');

async function drawRoundedImageWithShadow() {
  const canvas = createCanvas(500, 500);
  const ctx = canvas.getContext('2d');

  // 加载本地或远程图片
  const background = await loadImage('path/to/background.jpg');
  const overlay = await loadImage('path/to/overlay.jpg');

  // 绘制背景
  ctx.drawImage(background, 0, 0, 500, 500);

  const x = (canvas.width - 300) / 2;
  const y = (canvas.height - 300) / 2;
  const cornerRadius = 20;

  // 绘制阴影
  ctx.save();
  ctx.shadowColor = 'rgba(0, 0, 0, 1.0)';
  ctx.shadowBlur = 100;
  ctx.shadowOffsetX = 5;
  ctx.shadowOffsetY = 5;

  ctx.beginPath();
  ctx.moveTo(x + cornerRadius, y);
  ctx.lineTo(x + 300 - cornerRadius, y);
  ctx.quadraticCurveTo(x + 300, y, x + 300, y + cornerRadius);
  ctx.lineTo(x + 300, y + 300 - cornerRadius);
  ctx.quadraticCurveTo(x + 300, y + 300, x + 300 - cornerRadius, y + 300);
  ctx.lineTo(x + cornerRadius, y + 300);
  ctx.quadraticCurveTo(x, y + 300, x, y + 300 - cornerRadius);
  ctx.lineTo(x, y + cornerRadius);
  ctx.quadraticCurveTo(x, y, x + cornerRadius, y);
  ctx.closePath();

  ctx.fillStyle = 'transparent';
  ctx.fill();
  ctx.restore();

  // 绘制圆角图片
  ctx.beginPath();
  ctx.moveTo(x + cornerRadius, y);
  ctx.lineTo(x + 300 - cornerRadius, y);
  ctx.quadraticCurveTo(x + 300, y, x + 300, y + cornerRadius);
  ctx.lineTo(x + 300, y + 300 - cornerRadius);
  ctx.quadraticCurveTo(x + 300, y + 300, x + 300 - cornerRadius, y + 300);
  ctx.lineTo(x + cornerRadius, y + 300);
  ctx.quadraticCurveTo(x, y + 300, x, y + 300 - cornerRadius);
  ctx.lineTo(x, y + cornerRadius);
  ctx.quadraticCurveTo(x, y, x + cornerRadius, y);
  ctx.closePath();
  ctx.clip();

  ctx.drawImage(overlay, x, y, 300, 300);

  // 保存结果到文件
  const buffer = canvas.toBuffer('image/png');
  fs.writeFileSync('output.png', buffer);
}

drawRoundedImageWithShadow();

关键注意点

  • 必须等待图片加载完成再执行绘制:浏览器中用onload,Node.js中用async/await配合loadImage
  • 使用save()和restore()隔离画布状态:避免阴影设置影响后续绘制操作
  • 阴影与图片路径保持一致:确保阴影的圆角和图片的圆角完全匹配

内容的提问来源于stack exchange,提问作者Aviato

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 23:35:58