如何在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
相关产品推荐
相关产品推荐

