使用JavaScript将阴影添加到画布上的圆角图像

jm2pwxwz  于 2023-08-02  发布在  Java
关注(0)|答案(1)|浏览(138)

我的目标是从一个URL加载一个图像,使边缘变圆,并为图像添加阴影。
在我的代码中,我可以成功地使每个步骤单独工作,但在一起,阴影不被添加。
JSFiddle:https://jsfiddle.net/zfckwep8/1/

// Load the image onto the canvas and apply rounded corners and shadow.
function addRoundedCornersWithShadow() {
  const canvas = document.getElementById('canvas');
  const ctx = canvas.getContext('2d');

  const image = new Image();
  image.src = 'https://picsum.photos/200/300'; 

  image.onload = function() {
    canvas.width = image.width;
    canvas.height = image.height;

    const cornerRadius = 20;
    const shadowBlur = 20;
    const shadowOffsetX = 5;
    const shadowOffsetY = 5;

    // Draw the shadow
    ctx.shadowBlur = shadowBlur;
    ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';
    ctx.shadowOffsetX = shadowOffsetX;
    ctx.shadowOffsetY = shadowOffsetY;

    // Draw the rounded rectangle with the image
    ctx.beginPath();
    ctx.moveTo(cornerRadius, 0);
    ctx.lineTo(canvas.width - cornerRadius, 0);
    ctx.arcTo(canvas.width, 0, canvas.width, cornerRadius, cornerRadius);
    ctx.lineTo(canvas.width, canvas.height - cornerRadius);
    ctx.arcTo(canvas.width, canvas.height, canvas.width - cornerRadius, canvas.height, cornerRadius);
    ctx.lineTo(cornerRadius, canvas.height);
    ctx.arcTo(0, canvas.height, 0, canvas.height - cornerRadius, cornerRadius);
    ctx.lineTo(0, cornerRadius);
    ctx.arcTo(0, 0, cornerRadius, 0, cornerRadius);
    ctx.closePath();

    // Clip the rounded rectangle
    ctx.clip();

    // Draw the image on the canvas with rounded corners and shadow
    ctx.drawImage(image, 0, 0, canvas.width, canvas.height);

    // Clear the shadow for future drawings (optional)
    ctx.shadowBlur = 0;
    ctx.shadowOffsetX = 0;
    ctx.shadowOffsetY = 0;
  };
}

addRoundedCornersWithShadow();

字符串

inb24sb2

inb24sb21#

问题是剪切区域也会剪切阴影,因此您将无法看到它。
对于像您这样的简单情况,您正在绘制图像,您可以从您的图像创建CanvasPattern,并使用该模式fill()您的路径。这样你就不需要clip(),阴影也可以画出来。但是你会面临另一个问题,因为你设置你的画布大小的图像的大小,阴影将被削减。要解决这个问题,你需要将画布的宽度和高度设置为图像的大小,阴影模糊的两倍,阴影偏移的两倍。
然后你需要将路径居中,因为我们使用的是CanvasPattern,它的位置总是相对于上下文的当前变换,最好的方法是将上下文translate()

async function addRoundedCornersWithShadow() {
  const canvas = document.getElementById('canvas');
  const ctx = canvas.getContext('2d');

  const image = new Image();
  image.src = 'https://picsum.photos/200/300'; 
  await image.decode();  

  const cornerRadius = 20;
  const shadowBlur = 20;
  const shadowOffsetX = 5;
  const shadowOffsetY = 5;

  canvas.width = image.width + (shadowBlur * 2) + shadowOffsetX;
  canvas.height = image.height + (shadowBlur * 2) + shadowOffsetY;

  // We'll use a CanvasPattern to draw our image
  ctx.fillStyle = ctx.createPattern(image, "no-repeat");

  // Prepare the shadow
  ctx.shadowBlur = shadowBlur;
  ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';
  ctx.shadowOffsetX = shadowOffsetX;
  ctx.shadowOffsetY = shadowOffsetY;

  // Place our drawing so that we can see the full shadow
  ctx.translate(shadowBlur - shadowOffsetX, shadowBlur - shadowOffsetY);
  // Trace the rounded rectangle (https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/roundRect)
  ctx.roundRect(0, 0, image.width, image.height, cornerRadius);

  // Draw the image on the canvas with rounded corners and shadow
  ctx.fill();

  // Clear the shadow and fillStyle for future drawings (optional)
  ctx.resetTransform();
  ctx.shadowBlur = 0;
  ctx.shadowOffsetX = 0;
  ctx.shadowOffsetY = 0;
  ctx.fillStyle = "black";
}

addRoundedCornersWithShadow();

个字符
现在,这是可行的,因为你只想画一个已经存在的图像。如果您想使用剪切区域来绘制由一些绘图命令组成的更复杂的图形,那么这不是最好的解决方案。
在这种情况下,可以做的是用一个巨大的偏移量绘制阴影,这样本来应该画在画布上的东西实际上画在外面,只有阴影留在预期的地方:

function addRoundedCornersWithShadow() {
  const canvas = document.getElementById('canvas');
  const ctx = canvas.getContext('2d');

  const width = 300;
  const height = 200;
  const cornerRadius = 20;
  const shadowBlur = 20;
  const shadowOffsetX = 5;
  const shadowOffsetY = 5;

  canvas.width = width + (shadowBlur * 2) + shadowOffsetX;
  canvas.height = height + (shadowBlur * 2) + shadowOffsetY;

  // So that we can clean up after we draw (optional)
  ctx.save();
  // We'll need to remove the clipping region during the drawing (required)
  ctx.save();
  
  // Trace the rounded rectangle (https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/roundRect)
  const roundRect = new Path2D();
  roundRect.roundRect(shadowBlur + shadowOffsetX, shadowBlur + shadowOffsetY, width, height, cornerRadius);
  
  // Clip the context
  ctx.clip(roundRect);

  // Draw some stuff
  for (let i = 0; i<120; i++) {
    ctx.fillStyle = `#${((Math.random()*0xFFFFFF)|0).toString(16).padStart(6, "0")}`;
    ctx.roundRect(Math.random() * canvas.width, Math.random()*canvas.height, Math.random()*width/2, Math.random()*height/2, 12);
    ctx.fill();
    ctx.beginPath();
  }
  
  // Remove the clipping region (required)
  ctx.restore();
  
  // Prepare the shadow
  ctx.shadowBlur = shadowBlur;
  ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';
  // Note that we add the canvas width to the offset
  ctx.shadowOffsetX = shadowOffsetX + canvas.width;
  ctx.shadowOffsetY = shadowOffsetY;
  // Set the context to draw behind the current content
  ctx.globalCompositeOperation = "destination-over";
  // If you wish to use the current content as the source for the shadow,
  // Redraw the canvas over itself, but in the non visible area
  ctx.drawImage(canvas, -canvas.width, 0);
  // If you wanted to use the roundRect instead you'd have done
  // ctx.translate(-canvas.width, 0);
  // ctx.fill(roundRect);

  // Clean up for future drawings (optional)
  ctx.restore();
}

addRoundedCornersWithShadow();
<canvas id="canvas"></canvas>

的字符串

相关问题