居中 Canvas 无重复图案填充

Centering a Canvas no-repeat pattern fill

我正在尝试复制 mapbox 的动画图标 https://docs.mapbox.com/mapbox-gl-js/example/add-image-animated/

除了我想要内圈的图像。

到目前为止,我可以通过使用上面的创建模式非常接近。但是图像是重复的,而不是在内圈居中。

如何裁剪图片并将其居中放置在内圈中?

    // Copy pasta from mapbox example ... 
    // Draw the outer circle.
    context.clearRect(0, 0, this.width, this.height)
    context.beginPath()
    context.arc(this.width / 2, this.height / 2, outerRadius, 0, Math.PI * 2)
    
    context.fillStyle = `hsl(46deg 85% 67% / ${1 - t})`
    context.fill()

    // Draw the inner circle.
    context.beginPath()
    context.arc(this.width / 2, this.height / 2, radius, 0, Math.PI * 2)
    
    // I've added this 
    const pattern = context.createPattern(image, 'repeat')
    context.fillStyle = pattern

    // I've tried this but the image isn't cropped and isn't centered. 
    // context.drawImage(image, this.width / 2, this.height / 2, 150, 150)

    context.strokeStyle = 'white'
    context.lineWidth = 2 + 4 * (1 - t)
    context.fill()
    context.stroke()

实际上,我认为你快到了。

既然不想重复,先把花样设置为不重复:

const pattern = ctx.createPattern(img, 'no-repeat');

图案填充计算从左上角开始 (0, 0),因此仅靠它是无法完成的。我们还必须转换模式:

pattern.setTransform(
    new DOMMatrix(
    [
        // No rotation, 1-1 scale
        1, 0, 0, 1,
        // Translate to center, offset by half-image
        canvas.width / 2 - img.width / 2, 
        canvas.height / 2 - img.height / 2
    ])
);

文档:


这是一个如何使用它的演示。我无法让他们的 CodePen 演示工作,所以我不得不以此为基础并即兴创作:

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');

const size = 300;

const img = new Image();
img.src = 'https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle/canvas_fillstyle.png';
img.onload = function() {
  const pattern = ctx.createPattern(img, 'no-repeat');
  const radius = (size / 2) * 0.3;
    const outerRadius = (size / 2) * 0.7 + radius;
  
  ctx.fillStyle = `hsl(46deg 85% 67% / [=12=])`;
  ctx.arc(canvas.width / 2, canvas.height / 2, outerRadius, 0, Math.PI * 2);
  ctx.fill();

  ctx.beginPath();
  ctx.arc(canvas.width / 2, canvas.height / 2, radius, 0, Math.PI * 2);

  pattern.setTransform(new DOMMatrix([1, 0, 0, 1, canvas.width / 2 - img.width / 2, canvas.height / 2 - img.height / 2]));
  ctx.fillStyle = pattern;

  ctx.strokeStyle = 'white';
  ctx.lineWidth = 2;
  ctx.fill();
  ctx.stroke();
};
canvas
{
  border: 1px solid black;
  background-color: gray;
}
<canvas width="400" height="400"></canvas>
<div>For reference, here's the original image:</div>
<img src="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle/canvas_fillstyle.png" />