一步步打造Canvas小球动画

我们需要使用到Canvas的方法有:

context.arc(x, y, r, sAngle, eAngle, counterclockwise);

第一步:绘制一个小球

var  canvas = document.getElementById('canvas'),
     ctx = canvas.getContext('2d'),
     w = canvas.width, //canvas画布的宽
     h = canvas.height,// canvas画布的高
     posX = 20,//定义圆心X坐标
     posY = 20,//定义圆心Y坐标 
     radius = 20;//定义半径
ctx.clearRect(0, 0, w, h);//清除画布
ctx.beginPath();//开始绘制
ctx.arc(posX,posY,radius,0,2*Math.PI);//画圆
ctx.fillStyle = 'red';//圆的填充颜色
ctx.closePath();//闭合路径
ctx.fill();//填充

在线预览:https://codepen.io/jianxiujiu…

第二步:让小球动起来

让小球动起来的原理就是,不断地改变小球的坐标位置并进行重绘。
重绘可以用setInterval、setTimeout或requestanimationframe。关于它们的区别此处不做详解,具体可以参考我好基友的一篇文章:setTimeout 和 requestAnimationFrame

window.requestAnimFrame = (function(){
    return window.requestAnimationFrame       || 
        window.webkitRequestAnimationFrame || 
        window.mozRequestAnimationFrame    || 
        window.oRequestAnimationFrame      || 
        window.msRequestAnimationFrame     ||
        function(callback){
            window.setTimeout(callback, 1000 / 60);
        };
})();
var canvas = document.getElementById('canvas'),
    ctx = canvas.getContext('2d'),
    w = canvas.width,
    h = canvas.height,
    posX = 20, //小球出现的X轴位置
    posY = 20, //小球出现的Y轴位置
    speedX = 3,//小球X轴速度
    speedY = 3,//小球Y轴速度
    radius = 20;//小球半径

    function ani(){
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        posX += speedX;
        posY += speedY;     
        ctx.beginPath();
        ctx.arc(posX,posY,radius,0,2*Math.PI);
        ctx.fillStyle = 'red';
        ctx.closePath();
        ctx.fill();   
        if(posY < h - radius){ 
            requestAnimFrame(ani) //如果Y轴位置不超过容器高度则一直移动
        }
    }
    ani();

在线预览:https://codepen.io/jianxiujiu…

第三步:一个小球的碰撞动画

接下来我们让小球动起来,并在画布四周进行碰撞运动。

window.requestAnimFrame = (function(){
    return window.requestAnimationFrame       || 
        window.webkitRequestAnimationFrame || 
        window.mozRequestAnimationFrame    || 
        window.oRequestAnimationFrame      || 
        window.msRequestAnimationFrame     ||
        function(callback){
            window.setTimeout(callback, 1000 / 60);
        };
})();
var canvas = document.getElementById('canvas'),
    ctx = canvas.getContext('2d'),
    w = canvas.width,
    h = canvas.height,
    posX = 20,
    posY = 20,
    speedX = 2,//小球X轴速度
    speedY = 2,//小球Y轴速度
    startSpeedX = 2,//小球X轴初始速度
    startSpeedY = 2,//小球Y轴初始速度
    radius = 20;//小球半径

    function ani(){       
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        posX += speedX;
        posY += speedY;
        
        //小球碰壁反弹
        if(posY > h - radius){ 
            speedY*=-1;
        }
        if(posX > w - radius){
            speedX*=-1;  
        }
        if(posY < radius){
            speedY = startSpeedY;
            posY = radius;
        }
        if(posX < radius){
            speedX = startSpeedX;
            posX = radius;
        }
        
        ctx.beginPath();
        ctx.arc(posX,posY,radius,0,2*Math.PI);
        ctx.fillStyle = 'red';
        ctx.closePath();
        ctx.fill();
        requestAnimFrame(ani);
    }

    ani();

在线预览:https://codepen.io/jianxiujiu…

第四步:多个小球的碰撞动画

window.requestAnimFrame = (function(){
    return window.requestAnimationFrame       || 
        window.webkitRequestAnimationFrame || 
        window.mozRequestAnimationFrame    || 
        window.oRequestAnimationFrame      || 
        window.msRequestAnimationFrame     ||
        function(callback){
            window.setTimeout(callback, 1000 / 60);
        };
})();
var canvas = document.getElementById('canvas'),
    ctx = canvas.getContext('2d'),
    w = canvas.width,
    h = canvas.height,
    posX,
    posY,
    speedX,
    speedY,
    radius;

    function randomNum(m,n) { //随机函数
        return Math.floor(Math.random() * (n - m + 1) + m);
    }

    var balls = [] //建立小球数组
    function getBall(){
        for( n = 0; n < 20;n++){ //小球的数量
            radius = randomNum (10,20), //半径随机10-20px
            posX = randomNum(radius,w-radius), //X轴位置随机
            posY = randomNum(radius,h-radius), //Y轴位置随机
            speedX = randomNum(3,6), //X轴速度随机
            speedY = randomNum(1,2), //Y轴速度随机
            startSpeedX = startSpeedX,//记录X轴起始速度
            startSpeedY = startSpeedY;//记录X轴起始速度
            fillColor = 'rgb(' + randomNum(0,255) + ',' + randomNum(0,255) + ',' + randomNum(0,255) + ')';//小球颜色随机
            var ball = {
                radius : radius,
                posX : posX,
                posY : posY,
                speedX : speedX,
                speedY : speedY,
                startSpeedX : startSpeedX, 
                startSpeedY : startSpeedX,
                fillColor : fillColor
            }
            balls.push(ball) //将生成的小球存到数组里
        }    
    }
    getBall();   
            
    function draw(){     
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        var l = balls.length; 
        for(var i = 0;i<l;i++){ //逐一绘制小球动画
            ctx.beginPath();  
            ballCur = balls[i];
            ballCur.posX += ballCur.speedX;
            ballCur.posY += ballCur.speedY;
            if(ballCur.posY > h - ballCur.radius){
                ballCur.speedY*=-1;
            }
            if(ballCur.posX > w - ballCur.radius){
                ballCur.speedX*=-1;  
            }
            if(ballCur.posY < ballCur.radius){
                ballCur.speedY = startSpeedY;
                ballCur.posY = ballCur.radius;
            }
            if(ballCur.posX < ballCur.radius){
                ballCur.speedX = startSpeedX;
                ballCur.posX = ballCur.radius;
            }            
                               
            ctx.arc(ballCur.posX,ballCur.posY,ballCur.radius,0,2*Math.PI);        
            ctx.fillStyle = ballCur.fillColor;
            ctx.closePath();
            ctx.fill();
           
        }  
        requestAnimFrame(draw);    
    }        
    draw();

在线预览:https://codepen.io/jianxiujiu…

第五步:小球为自定义图片的动画

小球动起来之后,如果希望小球为图片,则需要用到Canvas另外一个方法:

context.drawImage(image, x, y); //在画布上定位图像:
context.drawImage(image, x, y, width, height);//在画布上定位图像,并规定图像的宽度和高度
context.drawImage(image, sourceX, sourceY, sourceWidth, sourceHeight,destX, destY, destWidth, destHeight);//剪切图像,并在画布上定位被剪切的部分

和绘制普通小球不一样,绘制图片球的时候,我们需要把之前动画的代码在加载完图片之后再执行。并且碰撞的位置也需要进行调整。

window.requestAnimFrame = (function(){
    return window.requestAnimationFrame       || 
        window.webkitRequestAnimationFrame || 
        window.mozRequestAnimationFrame    || 
        window.oRequestAnimationFrame      || 
        window.msRequestAnimationFrame     ||
        function(callback){
            window.setTimeout(callback, 1000 / 60);
        };
})();
var canvas = document.getElementById('canvas'),
    ctx = canvas.getContext('2d'),
    w = canvas.width,
    h = canvas.height,
    ballWidth = 60, //图片球宽度
    posX,
    posY,
    speedX,
    speedY;
    var img = new Image();
    img.src = 'img/ball.png';
    img.onload = function(){     
        getBall();  
        draw();
    }

    function randomNum(m,n) {
        return Math.floor(Math.random() * (n - m + 1) + m);
    }

    var balls = []
    function getBall(){
        for( n = 0; n < 20;n++){   
            posX = randomNum(ballWidth,w - ballWidth),
            posY = randomNum(ballWidth,h - ballWidth),
            speedX = randomNum(3,7),
            speedY = randomNum(4,8),
            startSpeedX = speedX,
            startSpeedY = speedY;
            ballPicPos = randomNum(0,4)*60; //小球图片位置随机
            var ball = {
                posX : posX,
                posY : posY,
                speedX : speedX,
                speedY : speedY,
                startSpeedX : startSpeedX, 
                startSpeedY : startSpeedX,
                ballPicPos : ballPicPos
            }
            balls.push(ball);                  
        }   
    }
     
            
    function draw(){     
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        var l = balls.length;
        for(var i = 0;i<l;i++){  
            ballCur = balls[i];
            ballCur.posX += ballCur.speedX;
            ballCur.posY += ballCur.speedY;
            if(ballCur.posY > h - ballWidth){
                ballCur.speedY*=-1;
            }
            if(ballCur.posX > w - ballWidth){
                ballCur.speedX*=-1;  
            }
            if(ballCur.posY < 0){
                ballCur.speedY = startSpeedY;
                ballCur.posY = 0;
            }
            if(ballCur.posX < 0){
                ballCur.speedX = startSpeedX;
                ballCur.posX = 0;
            }                            
           
           ctx.beginPath(); 
           ctx.fillStyle = ctx.drawImage(img,0,ballCur.ballPicPos,ballWidth,ballWidth,ballCur.posX,ballCur.posY,ballWidth,ballWidth);
           ctx.fill();
           ctx.closePath();
           ctx.restore();
           
        }  
        requestAnimFrame(draw);  
    }                

线上预览:https://codepen.io/jianxiujiu…

PS:小球的图片位置如果取值大于图片本身的位置,则IE11下显示图片会有BUG。

    原文作者:一只小金鱼
    原文地址: https://segmentfault.com/a/1190000010728922
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞