先日とある案件で桜吹雪を実装してほしいとデザイナーから相談を受けました。
- 極力リアルなもの
- 常に流れ続けてほしい
という要望があり、パッと思いついたのはやはり有名なparticles.jsです。
今回実現したい表現にはピッタリなライブラリですが、実装に猶予があったので自作jsでcanvasに描画させることにしました。
CodePen
html
<div class="contents">
<canvas id="sakuraCanvas" class="u-sakura"></canvas>
</div>
canvasタグで任意のIDを付与してください。
ここではsakuraCanvasとしています。
css
.contents {
position: relative;
width: 100vw;
height: 100dvh;
background-color: #95d8e8;
}
.u-sakura {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
z-index: 1;
pointer-events: none;
}
今回は親要素に対して幅も高さも100%で表示させたかったため上記のようなcssを当てています。
js
// 桜吹雪に使用する画像
const petalImageUrls = [
"https://www.konnotes.com/cms/wp-content/uploads/2025/06/sakura01.png",
"https://www.konnotes.com/cms/wp-content/uploads/2025/06/sakura02.png",
"https://www.konnotes.com/cms/wp-content/uploads/2025/06/sakura03.png",
"https://www.konnotes.com/cms/wp-content/uploads/2025/06/sakura04.png",
"https://www.konnotes.com/cms/wp-content/uploads/2025/06/sakura05.png"
];
// 画像をすべて読み込んでから開始
function loadImages(urls) {
return Promise.all(
urls.map(
(src) =>
new Promise((resolve) => {
const img = new Image();
img.src = src;
img.onload = () => resolve(img);
})
)
);
}
class SakuraPetal {
constructor(canvas, ctx, images) {
this.canvas = canvas;
this.ctx = ctx;
this.images = images;
this.init();
}
init() {
this.x = Math.random() * this.canvas.width;
this.y = Math.random() * this.canvas.height;
this.size = Math.random() * 30 + 4; // 花びらの大きさ
this.speedX = Math.random() * -10 - 1; // 左方向の速度(負の値)
this.speedY = Math.random() * 1 + 0.5; // 縦方向の速度(ゆっくり下に流れる)
this.rotation = Math.random() * 360; // 回転角度
this.rotationSpeed = Math.random() * 2 - 1; // 回転速度
this.opacity = Math.random() * 0.8 + 0.8; // 透明度
this.image = this.images[Math.floor(Math.random() * this.images.length)];
}
update() {
this.x += this.speedX; // 左方向に移動
this.y += this.speedY; // 下方向に移動
this.rotation += this.rotationSpeed;
if (this.x < -this.size || this.y > this.canvas.height) {
this.init();
this.x = this.canvas.width + this.size;
}
}
draw() {
this.ctx.save();
this.ctx.globalAlpha = this.opacity;
this.ctx.translate(this.x, this.y);
this.ctx.rotate((this.rotation * Math.PI) / 180);
this.ctx.drawImage(
this.image,
-this.size / 2,
-this.size / 2,
this.size,
this.size
);
this.ctx.restore();
}
}
function effectSakura(petalImages) {
const canvas = document.getElementById("sakuraCanvas");
if (!canvas) return;
const ctx = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const sakuraPetals = Array.from(
{ length: 50 }, // 花びらの数
() => new SakuraPetal(canvas, ctx, petalImages)
);
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
sakuraPetals.forEach((petal) => {
petal.update();
petal.draw();
});
requestAnimationFrame(animate);
}
animate();
window.addEventListener("resize", () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
});
}
loadImages(petalImageUrls).then(effectSakura);
少々長くなってしまいましたが、こちらがjsのコードになります。
- 花びらの大きさ
- 回転速度
- 回転角度
- 透明度
など、ある程度調整可能です。
実装する際にはコメントアウトしている箇所の数値を変えてみてご確認ください。
まとめ
結構いい感じになりました。
この場では桜の画像はシンプルなものを使っているので、よりリアルなものを用意すれば桜吹雪もリアルなものになりかもしれません。