I'm trying to replicate the animated icon that mapbox has here https://docs.mapbox.com/mapbox-gl-js/example/add-image-animated/
Except I want to have an image for the inner circle.
So far I can get fairly close by using a create pattern as above. But the image is repeating instead of being centered in the inner circle.
How can I have my image cropped and centered in the inner circle ?
// 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()
Actually, I think you were almost there.
Since you don't want it to repeat, first set the pattern to not repeat:
const pattern = ctx.createPattern(img, 'no-repeat');
The pattern fill calculation starts in the upper left hand corner (0, 0
), so that alone won't do it. We'll have to transform the pattern as well:
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
])
);
Docs:
Here's a demo of how to use it. I couldn't get their CodePen demo to work, so I had to base it off if that and improvise a bit:
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% / ${0})`;
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" />