网页樱花飘落特效

八月 16, 2023 / Mr.x / 5阅读 / 0评论/ 分类: 创意
网页樱花飘落特效 — 荣小站

网页樱花飘落特效

给博客添加唯美的樱花飘落动画效果,浪漫且性能良好。

这是一个基于 HTML5 Canvas 实现的樱花飘落特效。可以在网页上添加飘落的樱花花瓣,支持开关控制,移动端自动禁用以优化性能。

效果演示

点击下方按钮开启/关闭樱花飘落效果:

👇 点击体验樱花飘落

完整代码

💡 使用方法:可以新建一个 js 文件引入,也可以直接写在页面中。需要准备一张樱花图片 huaban.png

// 首先检测是否为移动设备,移动端不加载
if (! (navigator.userAgent.match(/(iPhone|iPod|Android|ios|iPad)/i))) {
    
    var stop, staticx;
    var img = new Image();
    img.src = './img/huaban.png';   // 滑板图片(樱花素材)
    
    // 樱花类
    function Sakura(x, y, s, r, fn) {
        this.x = x;      // X 坐标
        this.y = y;      // Y 坐标
        this.s = s;      // 大小
        this.r = r;      // 旋转角度
        this.fn = fn;    // 运动函数
    }
    
    // 绘制樱花
    Sakura.prototype.draw = function (cxt) {
        cxt.save();
        cxt.translate(this.x, this.y);
        cxt.rotate(this.r);
        cxt.drawImage(img, 0, 0, 40 * this.s, 40 * this.s)
        cxt.restore();
    }
    
    // 更新樱花位置
    Sakura.prototype.update = function () {
        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);
        
        // 超出边界后重置
        if (this.x > window.innerWidth || this.x < 0 || 
            this.y > window.innerHeight || this.y < 0) {
            this.r = getRandom('fnr');
            if (Math.random() > 0.4) {
                this.x = getRandom('x');
                this.y = 0;
                this.s = getRandom('s');
            } else {
                this.x = window.innerWidth;
                this.y = getRandom('y');
                this.s = getRandom('s');
            }
        }
    }
    
    // 樱花列表
    SakuraList = function () { this.list = []; }
    SakuraList.prototype.push = function (sakura) { this.list.push(sakura); }
    SakuraList.prototype.update = function () {
        for (var i = 0, len = this.list.length; i < len; i++) {
            this.list[i].update();
        }
    }
    SakuraList.prototype.draw = function (cxt) {
        for (var i = 0, len = this.list.length; i < len; i++) {
            this.list[i].draw(cxt);
        }
    }
    
    // 随机参数生成器
    function getRandom(option) {
        var ret, random;
        switch (option) {
            case 'x': ret = Math.random() * window.innerWidth; break;
            case 'y': ret = Math.random() * window.innerHeight; break;
            case 's': ret = Math.random(); break;
            case 'r': ret = Math.random() * 6; break;
            case 'fnx': 
                random = -0.5 + Math.random() * 1;
                ret = function (x, y) { return x + 0.5 * random - 1.7; };
                break;
            case 'fny':
                random = 1.5 + Math.random() * 0.7;
                ret = function (x, y) { return y + random; };
                break;
            case 'fnr':
                random = Math.random() * 0.03;
                ret = function (r) { return r + random; };
                break;
        }
        return ret;
    }
    
    // 启动樱花特效
    function startSakura() {
        var canvas = document.createElement('canvas');
        canvas.height = window.innerHeight;
        canvas.width = window.innerWidth;
        canvas.setAttribute('style', 'position: fixed;left: 0;top: 0;pointer-events: none;z-index:99999;');
        canvas.setAttribute('id', 'canvas_sakura');
        document.body.appendChild(canvas);
        var cxt = canvas.getContext('2d');
        
        var sakuraList = new SakuraList();
        for (var i = 0; i < 50; i++) {
            var sakura = new Sakura(
                getRandom('x'), getRandom('y'), getRandom('s'), getRandom('r'),
                { x: getRandom('fnx'), y: getRandom('fny'), r: getRandom('fnr') }
            );
            sakura.draw(cxt);
            sakuraList.push(sakura);
        }
        
        stop = requestAnimationFrame(function () {
            cxt.clearRect(0, 0, canvas.width, canvas.height);
            sakuraList.update();
            sakuraList.draw(cxt);
            stop = requestAnimationFrame(arguments.callee);
        })
    }
    
    // 停止樱花特效
    function stopp() {
        if (staticx) {
            var child = document.getElementById("canvas_sakura");
            child.parentNode.removeChild(child);
            window.cancelAnimationFrame(stop);
            staticx = false;
        } else {
            startSakura();
        }
    }
    
    img.onload = function () { startSakura(); }
}

核心机制解析

1. 移动端检测

if (! (navigator.userAgent.match(/(iPhone|iPod|Android|ios|iPad)/i))) {
    // 仅在非移动设备加载樱花特效
}

通过 UserAgent 检测是否为移动设备,移动端自动跳过加载,节省流量并提升性能。

2. Canvas 画布创建

var canvas = document.createElement('canvas');
canvas.setAttribute('style', 'position: fixed;left: 0;top: 0;pointer-events: none;z-index:99999;');

创建全屏 Canvas,使用 fixed 定位覆盖整个页面,z-index 99999 确保在最顶层,pointer-events: none 让点击穿透不影响页面交互。

3. 樱花对象与运动函数

属性作用
x, y樱花在画布上的坐标位置
s大小缩放比例 (0-1)
r旋转角度
fn.x, fn.y控制飘落轨迹的函数
fn.r控制旋转变化的函数

4. requestAnimationFrame 动画循环

stop = requestAnimationFrame(function () {
    cxt.clearRect(0, 0, canvas.width, canvas.height);  // 清除画布
    sakuraList.update();   // 更新所有樱花位置
    sakuraList.draw(cxt);  // 重新绘制
    stop = requestAnimationFrame(arguments.callee);  // 循环
})

使用 requestAnimationFrame 实现流畅的 60fps 动画,比 setInterval 性能更好。

5. 边界检测与循环再生

if (this.x > window.innerWidth || this.y > window.innerHeight) {
    // 超出边界后从顶部或右侧重新出现
    this.x = getRandom('x');
    this.y = 0;
}

樱花落下后不会销毁,而是重置位置实现循环利用,减少内存开销。

樱花素材

需要准备一张樱花/花瓣图片,命名为 huaban.png,放在 ./img/ 目录下。也可以使用其他图片素材(如树叶、雪花等)实现不同效果。

可调节参数

参数默认值作用
i < 5050樱花数量,可增减
40 * this.s40px樱花最大尺寸
fnx random-0.5 ~ 1水平飘动幅度
fny random1.5 ~ 2.2下落速度
fnr random0.03旋转变化速度

#网页樱花飘落特效(1)

评论