Canvas 2D で写真を円形に切り抜く例です。写真を先に描き、destination-in に切り替えてから円を描きます。既存の写真は円と重なる範囲に残り、外側は透明になります。
この例では入力をデコード済みの正方形画像に限定します。写真を正方形へ切り出す処理は含めません。長方形の写真をそのまま引き伸ばすと縦横比が変わるので、必要なら先に切り出す範囲を決めてください。
新しい canvas に画像を描いた後、合成モードを保存・変更します。円の色は黒でも、写真を黒く塗る用途ではありません。ここでは円の不透明な範囲をマスクに使っています。restore() 後は後続の描画に destination-in が残りません。
書き出した PNG は、白以外の背景にも重ねて確認します。白いプレビューだけでは、角が透明になったか判断しにくいためです。CSS で丸く見せるだけの場合と違い、この処理は書き出す画像自体の角を透明にします。
入力には同一オリジンの画像や、適切に CORS を設定して読み込んだ画像など、canvas の読み出しが許される素材を使います。合成処理が見た目どおりでも、書き出しが許可される条件まで変わるわけではありません。
円の縁には半透明のピクセルが含まれ得ます。透明を維持したい用途では PNG など対応する出力形式を選び、実際の配置先でも確認します。
function circularPhoto(image, size) {
if (image.naturalWidth <= 0 ||
image.naturalWidth !== image.naturalHeight ||
!Number.isInteger(size) || size <= 0) {
throw new Error("A decoded square image and positive size are required");
}
const canvas = document.createElement("canvas");
canvas.width = canvas.height = size;
const ctx = canvas.getContext("2d");
if (!ctx) throw new Error("2D canvas unavailable");
ctx.drawImage(image, 0, 0, size, size);
ctx.save();
ctx.globalCompositeOperation = "destination-in";
ctx.beginPath();
ctx.arc(size / 2, size / 2, size / 2, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
return canvas;
}
参考: MDN globalCompositeOperation
筆者は HEIC 変換プロジェクト Morphio に関わっています。画像の入出力を扱う際の実装メモとして書きました。