| ---
|
| import { sakuraConfig } from "@/config";
|
|
|
| const config = sakuraConfig;
|
| ---
|
|
|
| {sakuraConfig?.enable && (
|
| <script is:inline define:vars={{ sakuraConfig: config }}>
|
| let windowWidth = window.innerWidth;
|
| let windowHeight = window.innerHeight;
|
|
|
|
|
| class Sakura {
|
| constructor(x, y, s, r, a, fn, idx, img, limitArray, config) {
|
| this.x = x;
|
| this.y = y;
|
| this.s = s;
|
| this.r = r;
|
| this.a = a;
|
| this.fn = fn;
|
| this.idx = idx;
|
| this.img = img;
|
| this.limitArray = limitArray;
|
| this.config = config;
|
| }
|
|
|
| draw(cxt) {
|
| cxt.save();
|
| cxt.translate(this.x, this.y);
|
| cxt.rotate(this.r);
|
| cxt.globalAlpha = this.a;
|
| cxt.drawImage(this.img, 0, 0, 40 * this.s, 40 * this.s);
|
| cxt.restore();
|
| }
|
|
|
| update() {
|
| this.x = this.fn.x(this.x, this.y);
|
| this.y = this.fn.y(this.y, this.y);
|
| this.r = this.fn.r(this.r);
|
| this.a = this.fn.a(this.a);
|
|
|
| if (
|
| this.x > windowWidth ||
|
| this.x < 0 ||
|
| this.y > windowHeight ||
|
| this.y < 0 ||
|
| this.a <= 0
|
| ) {
|
|
|
| if (this.limitArray[this.idx] === -1) {
|
| this.resetPosition();
|
| }
|
|
|
| else {
|
| if (this.limitArray[this.idx] > 0) {
|
| this.resetPosition();
|
| this.limitArray[this.idx]--;
|
| }
|
| }
|
| }
|
| }
|
|
|
| resetPosition() {
|
| this.r = getRandom('fnr', this.config);
|
| if (Math.random() > 0.4) {
|
| this.x = getRandom('x', this.config);
|
| this.y = 0;
|
| this.s = getRandom('s', this.config);
|
| this.r = getRandom('r', this.config);
|
| this.a = getRandom('a', this.config);
|
| } else {
|
| this.x = windowWidth;
|
| this.y = getRandom('y', this.config);
|
| this.s = getRandom('s', this.config);
|
| this.r = getRandom('r', this.config);
|
| this.a = getRandom('a', this.config);
|
| }
|
| }
|
| }
|
|
|
|
|
| class SakuraList {
|
| constructor() {
|
| this.list = [];
|
| }
|
|
|
| push(sakura) {
|
| this.list.push(sakura);
|
| }
|
|
|
| update() {
|
| for (let i = 0, len = this.list.length; i < len; i++) {
|
| this.list[i].update();
|
| }
|
| }
|
|
|
| draw(cxt) {
|
| for (let i = 0, len = this.list.length; i < len; i++) {
|
| this.list[i].draw(cxt);
|
| }
|
| }
|
|
|
| get(i) {
|
| return this.list[i];
|
| }
|
|
|
| size() {
|
| return this.list.length;
|
| }
|
| }
|
|
|
|
|
| function getRandom(option, config) {
|
| let ret;
|
| let random;
|
|
|
| switch (option) {
|
| case 'x':
|
| ret = Math.random() * windowWidth;
|
| break;
|
| case 'y':
|
| ret = Math.random() * windowHeight;
|
| break;
|
| case 's':
|
| ret = config.size.min + Math.random() * (config.size.max - config.size.min);
|
| break;
|
| case 'r':
|
| ret = Math.random() * 6;
|
| break;
|
| case 'a':
|
| ret = config.opacity.min + Math.random() * (config.opacity.max - config.opacity.min);
|
| break;
|
| case 'fnx':
|
| random = config.speed.horizontal.min + Math.random() * (config.speed.horizontal.max - config.speed.horizontal.min);
|
| ret = function (x, _y) {
|
| return x + random;
|
| };
|
| break;
|
| case 'fny':
|
| random = config.speed.vertical.min + Math.random() * (config.speed.vertical.max - config.speed.vertical.min);
|
| ret = function (_x, y) {
|
| return y + random;
|
| };
|
| break;
|
| case 'fnr':
|
| ret = function (r) {
|
| return r + config.speed.rotation;
|
| };
|
| break;
|
| case 'fna':
|
| ret = function (alpha) {
|
| return alpha - config.speed.fadeSpeed * 0.01;
|
| };
|
| break;
|
| }
|
| return ret;
|
| }
|
|
|
|
|
| class SakuraManager {
|
| constructor(config) {
|
| this.config = config;
|
| this.canvas = null;
|
| this.ctx = null;
|
| this.sakuraList = null;
|
| this.animationId = null;
|
| this.img = null;
|
| this.isRunning = false;
|
| }
|
|
|
|
|
| async init() {
|
| if (!this.config.enable || this.isRunning) {
|
| return;
|
| }
|
|
|
|
|
| this.img = new Image();
|
| this.img.src = '/assets/images/sakura.png';
|
|
|
|
|
| await new Promise((resolve, reject) => {
|
| if (this.img) {
|
| this.img.onload = () => resolve();
|
| this.img.onerror = () => reject(new Error('Failed to load sakura image'));
|
| }
|
| });
|
|
|
| this.createCanvas();
|
| this.createSakuraList();
|
| this.startAnimation();
|
| this.isRunning = true;
|
| }
|
|
|
|
|
| createCanvas() {
|
| this.canvas = document.createElement('canvas');
|
| this.canvas.height = windowHeight;
|
| this.canvas.width = windowWidth;
|
| this.canvas.setAttribute('style', `position: fixed; left: 0; top: 0; pointer-events: none; z-index: ${this.config.zIndex}; transform: translateZ(0);`);
|
| this.canvas.setAttribute('id', 'canvas_sakura');
|
| document.body.appendChild(this.canvas);
|
| this.ctx = this.canvas.getContext('2d');
|
|
|
|
|
| window.addEventListener('resize', this.handleResize.bind(this));
|
| }
|
|
|
|
|
| createSakuraList() {
|
| if (!this.img || !this.ctx) return;
|
|
|
| this.sakuraList = new SakuraList();
|
| const limitArray = new Array(this.config.sakuraNum).fill(this.config.limitTimes);
|
|
|
| for (let i = 0; i < this.config.sakuraNum; i++) {
|
| const randomX = getRandom('x', this.config);
|
| const randomY = getRandom('y', this.config);
|
| const randomS = getRandom('s', this.config);
|
| const randomR = getRandom('r', this.config);
|
| const randomA = getRandom('a', this.config);
|
| const randomFnx = getRandom('fnx', this.config);
|
| const randomFny = getRandom('fny', this.config);
|
| const randomFnR = getRandom('fnr', this.config);
|
| const randomFnA = getRandom('fna', this.config);
|
|
|
| const sakura = new Sakura(
|
| randomX,
|
| randomY,
|
| randomS,
|
| randomR,
|
| randomA,
|
| {
|
| x: randomFnx,
|
| y: randomFny,
|
| r: randomFnR,
|
| a: randomFnA,
|
| },
|
| i,
|
| this.img,
|
| limitArray,
|
| this.config
|
| );
|
|
|
| sakura.draw(this.ctx);
|
| this.sakuraList.push(sakura);
|
| }
|
| }
|
|
|
|
|
| startAnimation() {
|
| if (!this.ctx || !this.canvas || !this.sakuraList) return;
|
|
|
| const animate = () => {
|
| if (!this.ctx || !this.canvas || !this.sakuraList) return;
|
|
|
| this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
| this.sakuraList.update();
|
| this.sakuraList.draw(this.ctx);
|
| this.animationId = requestAnimationFrame(animate);
|
| };
|
|
|
| this.animationId = requestAnimationFrame(animate);
|
| }
|
|
|
|
|
| handleResize() {
|
| windowWidth = window.innerWidth;
|
| windowHeight = window.innerHeight;
|
| if (this.canvas) {
|
| this.canvas.width = windowWidth;
|
| this.canvas.height = windowHeight;
|
| }
|
| }
|
|
|
|
|
| stop() {
|
| if (this.animationId) {
|
| cancelAnimationFrame(this.animationId);
|
| this.animationId = null;
|
| }
|
|
|
| if (this.canvas) {
|
| document.body.removeChild(this.canvas);
|
| this.canvas = null;
|
| }
|
|
|
| window.removeEventListener('resize', this.handleResize.bind(this));
|
| this.isRunning = false;
|
| }
|
|
|
|
|
| toggle() {
|
| if (this.isRunning) {
|
| this.stop();
|
| } else {
|
| this.init();
|
| }
|
| }
|
|
|
|
|
| updateConfig(newConfig) {
|
| const wasRunning = this.isRunning;
|
| if (wasRunning) {
|
| this.stop();
|
| }
|
| this.config = newConfig;
|
| if (wasRunning && newConfig.enable) {
|
| this.init();
|
| }
|
| }
|
|
|
|
|
| getIsRunning() {
|
| return this.isRunning;
|
| }
|
| }
|
|
|
|
|
| let globalSakuraManager = null;
|
|
|
|
|
| function initSakura(config) {
|
| if (globalSakuraManager) {
|
| globalSakuraManager.updateConfig(config);
|
| } else {
|
| globalSakuraManager = new SakuraManager(config);
|
| if (config.enable) {
|
| globalSakuraManager.init();
|
| }
|
| }
|
| }
|
|
|
|
|
| (function() {
|
|
|
| if (window.sakuraInitialized) {
|
| return;
|
| }
|
|
|
|
|
| const setupSakura = () => {
|
| if (sakuraConfig.enable && !window.sakuraInitialized) {
|
| initSakura(sakuraConfig);
|
| window.sakuraInitialized = true;
|
| }
|
| };
|
|
|
|
|
| if (document.readyState === 'loading') {
|
| document.addEventListener('DOMContentLoaded', setupSakura);
|
| } else {
|
| setupSakura();
|
| }
|
| })();
|
| </script>
|
| )}
|
|
|