canvas 动画——刚体碰撞小球

这篇博客中介绍了几个基础的动画,但是我们可以设置监听,通过鼠标控制实现更为高级的动画,在这里将绘制一个简单的桌面,桌面上再绘制一个刚体小球,我们可以给这个小球加上初速度,加速度,长尾效果…这是一个很经典的样例,在许多地方你都有可能见到它,并且应用到。

首先创建一个对象

var ball=
{
    x:100,
    y:100,
    vx:5,
    vy:3,
    r:20,
    color:"#8A2BE2",//紫色小球
    draw:function()
    {
        ctx.save();
        ctx.beginPath();
        ctx.arc(this.x,this.y,this.r,0,Math.PI*2,false);
        ctx.fillStyle=this.color;
        ctx.fill();
        ctx.restore();
    }
};

绘制函数

function draw()
{
    ctx.clearRect(0,0,canvas.width,canvas.height);
    //绘制背景
    ctx.save();
    ctx.fillStyle=" #2F4F4F";
    ctx.strokeStyle=getColor();
    ctx.lineWidth=40;
    ctx.fillRect(0,0,canvas.width,canvas.height);
    ctx.strokeRect(0,0,canvas.width,canvas.height);
    ctx.restore();
    //计算速度
    ball.vx*=(ball.x+ball.r+20>canvas.width||ball.x-ball.r-20<0)?-1:1;
    ball.vy*=(ball.y+ball.r+20>canvas.height||ball.y-ball.r-20<0)?-1:1;
    //计算位置
    ball.x+=ball.vx;
    ball.y+=ball.vy;
    //画出图形
    ball.color="rgb("+200+","+color1+","+color2+")";
    ball.draw();
    //递归调用
    raf=window.requestAnimationFrame(draw);
}

这个时候我们可以得到这样的小球:
在这里插入图片描述
并且随着时间的推移,小球的位置发生变化,遇到墙壁反弹会反方向。

添加变化的颜色getColor

想不想要小球随着时间变化?那将会更加吸引人。为了得到变换的颜色,我设置了这样一个函数:

function getColor()//产生颜色
{
    color1+=(t1==1)?2:-2;
    color2+=(t2==1)?1:-1;
    t1*=(color1>=255||color1<=0)?-1:1;
    t2*=(color2>=255||color2<=0)?-1:1;
    return "rgb("+color1+","+color2+","+20+")";
}

这样一来我们就能给小球和边框添加随时间变化的颜色了.
在这里插入图片描述

长尾效果

如何实现长尾效果?我们的绘制函数每当绘制一个新的图形时会清除整个画布,但是如果我们不清除画布而是用一个白色的半透明色fillRect函数取代它,这样每一帧就会越来越淡,直到消失。

    ctx.save();
    ctx.fillStyle='rgba(255,255,255,0.2)';
    ctx.fillRect(0,0,canvas.width,canvas.height);
    ctx.restore();

在这里插入图片描述
这个时候就能看见长尾效果了。

添加加速度

为了让小球的行为更加真实,我们可以给速度添加一个线性的变换函数:

    ball.vy *= .99;
    ball.vy += .25;

这个时候竖直方向的速度将会逐渐减小,最后稳定在某个值,一直在地板上弹跳。
在这里插入图片描述

设置监听

如果想要与鼠标互动,那么可以用canvas.addEventListener来设置监听

  • canvas.addEventListener(‘mousemove’, func)

关于鼠标的互动:

  • mouseout:鼠标远离canvas触发
  • click:鼠标点击canvas触发
  • mousemove:鼠标移动触发
  • mouseover:鼠标悬停触发

鼠标移动到画布上创建小球,移开鼠标动画停止

这个操作实现要结合mouseover和mouseout:

  • mouseover:鼠标悬停触发,创建动画,调用draw函数
  • mouseout:鼠标远离canvas触发,清除计时器,停止计时
  • raf = window.requestAnimationFrame(draw);创建并且获取计时器对象
  • window.cancelAnimationFrame(raf):清除定时器
window.onload=function()
{
    canvas=document.getElementById("canvas");
    ctx=canvas.getContext('2d');
    //draw();
    canvas.addEventListener("mouseover",function(e)
    {
        raf=requestAnimationFrame(draw);//创建动画
    })

    canvas.addEventListener("mouseout",function(e)
    {
        window.cancelAnimationFrame(raf);
    })
}

在这里插入图片描述

鼠标点击画布创建小球,移开鼠标动画停止,重新放上画布又能按以上触发

这个操作实现要结合mousemove,click和mouseout:

  • mousemove:鼠标移动触发。鼠标移动时,我们不能立即创建动画,而是只要画出小球,但是随着鼠标的移动,小球的移动轨迹将会练成一条涂鸦线,所以我们要在没有放上小球时清除整个画面再画上为触发的小球,调用draw函数。
  • click:当屏幕上没有小球的时候,点击将会创建动画
  • mouseout:鼠标远离canvas触发,清除计时器,停止计时
  • 如何判断屏幕上有没有小球?我们可以设置一个全局变量用于判断,如果click后将变量改为1,表示有小球。如果鼠标离开了canvas,修改为0
    canvas.addEventListener("mousemove",function(e)
    {
        if(!running)//没有小球
        {
            clear();
            ball.x=e.clientX;
            ball.y=e.clientY;
            ball.draw();//只画出小球
        }
    })
    canvas.addEventListener("click",function(e)
    {
        if(!running)//没有小球
        {
            raf=window.requestAnimationFrame(draw);
            running=true;
        }
    })
    canvas.addEventListener("mouseout",function()
    {
        window.cancelAnimationFrame(raf);//清除定时器
        running=false;
    })

在这里插入图片描述

最后是所有的代码:
HTML

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>test</title>
	<link rel="stylesheet" type="text/css" href="test.css">
	<script src="test.js"></script>
</head>
<body>
	<canvas id="canvas" width="600" height="300"></canvas>
	</canvas>
</body>
</html>

JScript

var canvas,ctx,raf,color1=0,color2=0,t1=1,t2=1;
var running = false;
window.onload=function()
{
    canvas=document.getElementById("canvas");
    ctx=canvas.getContext('2d');
    //绘制背景
    ctx.save();
    //ctx.fillStyle="#F0F8FF";
    ctx.strokeStyle=getColor();
    ctx.lineWidth=40;
    //ctx.fillRect(0,0,canvas.width,canvas.height);
    ctx.strokeRect(0,0,canvas.width,canvas.height);
    ctx.restore();
    canvas.addEventListener("mousemove",function(e)
    {
        if(!running)//没有小球
        {
            clear();
            ball.x=e.clientX;
            ball.y=e.clientY;
            ball.draw();//只画出小球
        }
    })
    canvas.addEventListener("click",function(e)
    {
        if(!running)//没有小球
        {
            raf=window.requestAnimationFrame(draw);
            running=true;
        }
    })
    canvas.addEventListener("mouseout",function()
    {
        window.cancelAnimationFrame(raf);//清除定时器
        running=false;
    })

}

function clear() {
  ctx.fillStyle = 'rgba(255,255,255,0.3)';
  ctx.fillRect(20,20,canvas.width-40,canvas.height-40);
}

var ball=
{
    x:100,
    y:100,
    vx:5,
    vy:3,
    r:20,
    color:"#8A2BE2",//紫色小球
    draw:function()
    {
        ctx.save();
        ctx.beginPath();
        ctx.arc(this.x,this.y,this.r,0,Math.PI*2,false);
        ctx.fillStyle=this.color;
        ctx.fill();
        ctx.restore();
    }
};
function getColor()//产生颜色
{
    color1+=(t1==1)?2:-2;
    color2+=(t2==1)?1:-1;
    t1*=(color1>=255||color1<=0)?-1:1;
    t2*=(color2>=255||color2<=0)?-1:1;
    return "rgb("+color1+","+color2+","+20+")";
}
function draw()
{
    ctx.save();
    ctx.fillStyle='rgba(255,255,255,0.2)';
    ctx.fillRect(0,0,canvas.width,canvas.height);
    ctx.restore();

    //绘制背景
    ctx.save();
    
    ctx.strokeStyle=getColor();
    ctx.lineWidth=40;
    ctx.strokeRect(0,0,canvas.width,canvas.height);
    ctx.restore();
    
    //计算速度
    ball.vy *= 0.99;
    ball.vy += 0.25;
    ball.vx*=(ball.x+ball.r+20>canvas.width||ball.x-ball.r-20<0)?-1:1;
    ball.vy*=(ball.y+ball.r+20>canvas.height||ball.y-ball.r-20<0)?-1:1;

    //计算位置
    ball.x+=ball.vx;
    ball.y+=ball.vy;
    //画出图形
    ball.color="rgb("+138+","+color1+","+226+")";
    ball.draw();
    //递归调用
    raf=window.requestAnimationFrame(draw);
}
发布了122 篇原创文章 · 获赞 134 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_44307065/article/details/104107245