使用JavaScript和Canvas实现下雪动画效果

该下雪动画效果使用了HTML5中Canvas画布实现,其中涉及了物理学中曲线运动的相关知识与运算。

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
    <meta content="yes" name="apple-mobile-web-app-capable">
    <meta content="black" name="apple-mobile-web-app-status-bar-style">
    <meta content="telephone=no" name="format-detection">
    <meta content="email=no" name="format-detection">
    <title>Snow</title>
    <link rel="stylesheet" href="css/main.css">
</head>
<body>
    <canvas id="canvas"></canvas>
    <script src="js/snow.js"></script>
    <script>
        window.addEventListener('load', function(){
            this.snow = new Snow();
            // 初始化snow对象并开始下雪动画
            snow.init().start();
        });
    </script>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

main.css

html, body{
    width: 100%;
    height: 100%;
    overflow: hidden;
    margin: 0;
    padding: 0;
    background-color: #000;
    font-family: 微软雅黑, 华文细黑, 黑体;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Snow.js

(function(exports, undefined){
    'use strict';
    var document = exports.document;
    function Snow(){
        this.colors = ['#fff'];
        this.balls = [];
        this.windDirection = -1;
        this.ballRadius = 3;
        this.ballsPerFrame = 2;
        this.timeInterval = 40;
        this.windDirectionChangedInterval = 5000;
        this.accumulativeTime = 0;
        return this;
    };
    exports.Snow = Snow;
    Snow.prototype = {
        init: function(args){
            for(var p in args){
                this[p] = args[p];
            }
            this.canvas = this.canvas || document.querySelector('#canvas');
            this.context = this.context || this.canvas.getContext('2d');
            this.canvasWidth = this.canvasWidth || document.body.offsetWidth || document.body.clientWidth;
            this.canvasHeight = this.canvasHeight || document.body.offsetHeight || document.body.clientHeight;
            this.canvas.width = this.canvasWidth;
            this.canvas.height = this.canvasHeight;
            return this;
        },
        start: function(){
            this.timer = this.timer || setTimeout(this.frame.bind(this), this.timeInterval);
            return this;
        },
        frame: function(){
            this.accumulativeTime += this.timeInterval;
            (this.accumulativeTime % this.windDirectionChangedInterval < this.timeInterval) && (this.windDirection *= -1);
            this.render.call(this);
            this.update.call(this);
            this.timer = null;
            this.timer = setTimeout(this.frame.bind(this), this.timeInterval);
        },
        update: function(){
            this.addBalls.call(this);
            this.updateBalls.call(this);
        },
        updateBalls: function(){
            var balls = this.balls,
                len = balls.length,
                i = 0,
                cnt = 0;
            for(;i<len;i++){
                balls[i].x += balls[i].vx * this.windDirection;
                balls[i].y += balls[i].vy;
                balls[i].vy += balls[i].g * balls[i].t;
                balls[i].t += this.timeInterval;
                if(balls[i].y - this.ballRadius < this.canvasHeight){
                    balls[cnt++] = balls[i];
                }
            }
            while(len>cnt){
                balls.pop();
                len--;
            }
        },
        addBalls: function(){
            var ball,
                i = 0,
                len = this.ballsPerFrame,
                _this = this;
            for(;i<len;i++){
                ball = {
                    x: Math.pow(-1, Math.ceil(Math.random() * 1000)) * Math.floor(Math.random() * _this.canvasWidth * 1.5),
                    y: Math.floor(Math.random() * this.ballRadius) * -1,
                    g: 0.00005,
                    vx: 1 + Math.floor(Math.random() * 2),
                    vy: 2 + Math.floor(Math.random() * 5),
                    t: 0,
                    color: _this.colors[Math.floor(Math.random() * _this.colors.length)]
                }
                this.balls.push(ball);
            }
        },
        render: function(){
            var cxt = this.context,
                i = 0,
                len = this.balls.length;
            cxt.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
            for(;i<len;i++){
                cxt.fillStyle = this.balls[i].color;
                cxt.beginPath();
                cxt.arc(this.balls[i].x, this.balls[i].y, this.ballRadius, 0, 2 * Math.PI, true);
                cxt.closePath();
                cxt.fill();
            }
        },
        pause: function(){
            clearTimeout(this.timer);
            this.timer = null;
        },
        resume: function(){
            this.start.call(this);
        },
        clear: function(){
            clearTimeout(this.timer);
            this.timer = null;
            this.context.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
        }
    }
})(window);

 

 
posted @ 2015-07-22 08:55  九九九零  阅读(1189)  评论(1编辑  收藏  举报