基于canvas实现物理运动效果与动画效果(一)

1、为何要写这篇文章javascript

 某年某月某时某种缘由,我在慕课网上看到了一个大神实现了关于小球的抛物线运动的代码,心中非常欣喜,故而写这篇文章来向这位大神致敬,同时也为了弥补本身在运动效果和动画效果制做方面的不足css

2、几种简单的直线运动html

这一部分主要讲解的是简单的运动效果的实现原理,其实全部的canvas动画效果的实如今核心思想是一致的:都是先定义个初始的状态,而后定义一个定时器,定时器内执行一个方法,记得在这个方法中要对当前的画面清除,而后在这个方法中从新绘制须要变化的效果,因为人眼存在残影,因此短期内的中断的变化能够当作是连续的变化,这个就是canva动画运动原理java

 最简单的要从匀速直线运动提及,而后是匀加速直线运动。canvas

匀速直线运动api

HTML代码浏览器

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>canvas匀速直线运动</title>
    <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
    <canvas id="canvas">你的浏览器不支持canvas,请跟换其余浏览器试一试</canvas>
    <script type="text/javascript" src="script.js"></script>
</body>
</html>

 

 

 

JS代码函数

window.onload=function(){
    var canvas=document.getElementById('canvas');
    canvas.height=728;
    canvas.width=1024;
    var context=canvas.getContext('2d');
    context.fillStyle='red';
    context.beginPath();
    context.arc(800,300,30,0,2*Math.PI,true);
    context.closePath();
    context.fill();
    setInterval(function(){
        run(context);
    }, 50);
};
var speed=0;
var startPoint=800;
function run(cxt){
    speed=-7;
    cxt.clearRect(0,0,1024,728);
    //cxt.top+=speed;    
    startPoint+=speed;
    cxt.beginPath();
    cxt.arc(startPoint,300,30,0,2*Math.PI,true);
    cxt.closePath();
    cxt.fill();
}

运行效果以下:动画

PS:这里面画面有点卡顿,是录制的时候软件的因素形成的,直接运行上诉代码是能够看到正常运行的效果spa

重点代码分析:

var speed=0;
var startPoint=800;
function run(cxt){
    speed=-7;
    cxt.clearRect(0,0,1024,728);
    //cxt.top+=speed;    
    startPoint+=speed;
    cxt.beginPath();
    cxt.arc(startPoint,300,30,0,2*Math.PI,true);
    cxt.closePath();
    cxt.fill();
}

先把速度定义为0和获取开始点,而后将canvas画面的内容清除,接着是计算变化后的坐标,而后进行重绘(坐标从新计算是运动关键所在)

 

匀变速直线运动

匀变速直线运动的定义:在直线运动中,把加速度的大小和方向都不改变的运动(加速度为正时),称之为匀加速直线运动。

基本公式:

速度公式: V=V0+at
位移公式: x=V0t+1/2at^2

因此咱们依次须要定义这样的几个变量加速度a,初始速度V0,位移量x,随时间变化的速度v,时间time,这几个变量的初始化值均为0,代码以下所示:

HTML代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>canvas匀加速直线运动</title>
    <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
    <canvas id="canvas">你的浏览器不支持canvas,请跟换其余浏览器试一试</canvas>
    <script type="text/javascript" src="script.js"></script>
</body>
</html>

 

JavaScript代码:

window.onload=function(){
    var canvas=document.getElementById('canvas');
    canvas.height=728;
    canvas.width=1024;
    var context=canvas.getContext('2d');
    context.fillStyle='red';
    context.beginPath();
    context.arc(800,300,30,0,2*Math.PI,true);
    context.closePath();
    context.fill();
    setInterval(function(){
        run(context);
    }, 50);
};
var v0=0;//初始速度
var a=0;//加速度
var v=0;//变化的速度
var time=0;//时间
var x=0;//位移量
var startPoint=800;//起始点
// V=V0+at
// x=v0t+1/2at^2
// v^2-V^2=2ax
function run(cxt){
    time+=0.05;
    a=10;
    x=-(0.5*a*(time*time));//位移公式代入
    startPoint+=x;
    cxt.clearRect(0,0,1024,728);
    cxt.beginPath();
    cxt.arc(startPoint,300,30,0,2*Math.PI,true);
    cxt.closePath();
    cxt.fill();
}

 

运行的效果以下:

基本上直线运动比较典型的也就是这两种,若有遗漏其余运动或者是须要博主讲解其余运动的制做的,请在留言板上留言

 

3、简单的曲线运动的实现

 说到简单的曲线运动,咱们就从最简单的也是我认为最基础的运动圆周运动提及

一、匀速圆周运动

圆周运动的定义:质点沿圆周运动,若是在任意相等的时间里经过的圆弧长度都相等,这种运动就叫作“匀速圆周运动”,亦称“匀速率圆周运动”。由于物体做圆周运动时速率不变,但速度方向随时发生变化。因此匀速圆周运动的线速度是每时每刻都在发生变化的。

匀速圆周运动的实现与分析

匀速圆周运动的实现第一反应咱们会选择经过匀速圆周运动的物理公式进行计算获得,可是物理公式中没有哪条明确的公式是能够把单位时间的变化量和所在点的具体坐标相关联的,显然这样的一条思路是行不通的,从物理公式上面是来讲是不具备可行性的,因此咱们应该要换另外的一种方法来实现,这个时候咱们应该要看透匀速圆周运动的本质,本质上来讲,匀速圆周运动的实现其实就是经过一个原点,而后在这个原点的基础之上对一个物体进行360度的旋转。好的,相信对canvas api熟悉的小伙伴已经想到了,是的,咱们能够经过旋转或者是矩阵来实现

 

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>canvas实现圆周运动</title>
    <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
    <canvas id="canvas">你的浏览器不支持canvas,请跟换其余浏览器试一试</canvas>
    <script type="text/javascript" src="script.js"></script>
</body>
</html>

 

相关的JavaScript代码:

 

window.onload=function(){
    var canvas=document.getElementById('canvas');
    canvas.height=728;
    canvas.width=1024;
    var context=canvas.getContext('2d');
    drawNotChange(context);
    context.fillStyle='blue';
    context.beginPath();
    context.arc(500,550,30,0,2*Math.PI,true);
    context.closePath();
    context.fill();
    setInterval(function(){
        run(context);
    }, 50);
};
var time=0;//定义运动的执行次数
function run(cxt){

    cxt.clearRect(0,0,1024,728);   
    drawNotChange(cxt);
    cxt.save();//将当前以左上角坐标为(0,0)的上下文环境进行保存,这样是为了在接下来中要进行画布偏移后,能够进行还原当前的环境
    cxt.translate(500,400);
    cxt.rotate(time*8*Math.PI/180);//设定每次旋转的度数
    cxt.fillStyle='blue';
    cxt.beginPath();
    cxt.arc(0,150,30,0,2*Math.PI,false);
    cxt.closePath();
    cxt.fill();
    cxt.restore();//将当前为(500,400)的点还原为(0,0),其实在save中就是将上下文环境保存到栈中,在restore下面对其进行还原
    time+=1;
}

//绘制不变因素
function drawNotChange(context){
    context.fillStyle='red';
    context.beginPath();
    context.arc(500,400,30,0,2*Math.PI,true);
    context.closePath();
    context.fill();
    context.beginPath();
    context.arc(500,400,150,0,2*Math.PI,true);
    context.closePath();
    context.stroke();
}

 

运行的结果以下:

为了让读者可以明白其中的原理,我会在注释中尽可能将代码注释清楚

 

二、椭圆运动

可能有些小伙伴们对于高中的知识都已经遗忘了,可是这个不妨碍,由于在接下来咱们会经过一步一步的复习相关数学知识最后才来实现效果,可是若是对高中知识比较熟悉的小伙伴,建议跳过这个阶段,直接看代码就好了,以避免浪费时间

椭圆的定义:椭圆(Ellipse)是平面内到定点F一、F2的距离之和等于常数(大于|F1F2|)的动点P的轨迹,F一、F2称为椭圆的两个焦点。其数学表表达式为:|PF1|+|PF2|=2a(2a>|F1F2|)

椭圆图解:

 长轴长咱们用2a表示,短轴长咱们用2b表示

假设椭圆的长轴与X轴平行,那么表达式以下所示:

根据三角函数之间的关系咱们能够推导出:

x=a+cos(t)

y=b+sin(t)

这里面的t表明的是单位是单位时间内旋转的弧度

具体的推导会在之后有时间,为你们专门写一篇博文来说解一些公式的推导过程

关于椭圆的数学知识已经讲完了,仍是不太清楚的同窗请执行去复习高中的知识,在这里就再也不累赘了。

咱们还开始代码实现以前还有先假设好一些参数,咱们假设原点O(500,300),绕椭圆运动的物体为圆形半径为30,其中长半轴长a=200,短半轴长为b=100,每次从新获取物体的运动后的移动位置的时候,x都会变化一个单位,旋转为顺时针旋转,开始位置为原点的正左边的端点,最后原点咱们以一个黑色且半径为10的小球表示。注意:上述的数据能够读者自行定义,可是要注意这个前提是必须保证a>b,若是a<b那么就不是这个公式了

咱们先来实现椭圆的轨迹效果:

HTML代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>canvas实现椭圆运动</title>
    <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
    <canvas id="canvas">你的浏览器不支持canvas,请跟换其余浏览器试一试</canvas>
    <script type="text/javascript" src="script.js"></script>
</body>
</html>

 

JavaScript代码:

 

var a=200,
    b=100,
    radius=30;
window.onload=function(){
    var canvas=document.getElementById('canvas');
    canvas.height=668;
    canvas.width=1024;
    var cxt=canvas.getContext('2d');
    cxt.beginPath();
    cxt.arc(300,300,10,0,2*Math.PI,true)
    cxt.closePath();
    cxt.fill();
    route(cxt,300,300,200,100);
};

//椭圆路线绘制
function route(context,x,y,a,b){
    //max是等于1除以长轴值a和b中的较大者
   //i每次循环增长1/max,表示度数的增长
   //这样可使得每次循环所绘制的路径(弧线)接近1像素
   var step = (a > b) ? 1 / a : 1 / b;
   context.beginPath();
   context.moveTo(x + a, y); //从椭圆的左端点开始绘制
   for (var i = 0; i < 2 * Math.PI; i += step)
   {
      //参数方程为x = a * cos(i), y = b * sin(i),
      //参数为i,表示度数(弧度)
      context.lineTo(x + a * Math.cos(i), y + b * Math.sin(i));
   }
   context.closePath();
   context.stroke();
}

 

 椭圆的轨迹的思路是:经过循环,将极小的线段首尾相连,绘制了一个相似于椭圆的一个图像,可是因为线段太过细小致使了咱们肉眼看上去就成了一个椭圆

 运行的效果是:

椭圆上小球的运动实现

这个的制做思路跟上面的思路是同样的,因此这里就再也不分析

此次咱们就先看一看效果如何:

HTML代码和上面的例子相同

JavaScript代码以下:

var a=200,
    b=100,
    radius=30;
    time=0;//循环的次数
window.onload=function(){
    var canvas=document.getElementById('canvas');
    canvas.height=768;
    canvas.width=1024;
    var cxt=canvas.getContext('2d');
    centerPoint(cxt);
    arcRoute(cxt,300,300,a,b,radius);
    setInterval(function(){
        arcRoute(cxt,300,300,a,b,radius);
        }, 70);
};

//绘制原点
function centerPoint(cxt){
    cxt.fillStyle="black";
    cxt.beginPath();
    cxt.arc(300,300,10,0,2*Math.PI,true)
    cxt.closePath();
    cxt.fill();
}
//椭圆路线绘制
function route(context,x,y,a,b){
   var step = (a > b) ? 1 / a : 1 / b;
   context.beginPath();
   context.moveTo(x + a, y); //从椭圆的左端点开始绘制
   for (var i = 0; i < 2 * Math.PI; i += step)
   {
      context.lineTo(x + a * Math.cos(i), y + b * Math.sin(i));
   }
   context.closePath();
   context.stroke();
}

//椭圆上小球运动的实现
function arcRoute(context,x,y,a,b,r){
    context.clearRect(0,0,1024,768);
    route(context,x,x,a,b);
    centerPoint(context);
    var step = (a > b) ? 1 / a : 1 / b;
    context.fillStyle="red";
    if(time==0){
        context.beginPath();
        context.arc(x,y,r,0,2*Math.PI,true);
        context.closePath();
        context.fill();
    }else{
    context.beginPath();
    context.arc(x+a*Math.cos(time),y+b*Math.sin(time),r,0,2*Math.PI,true);
    context.closePath();
    context.fill();
    }
    time+=1;
}

 

4、相关的参考资料

火狐开发者中心

沈佳洋

好了,这一节的内容就先结束了,下一节预告,下一节:会谈谈一些其余的运动效果的实现和这些运动效果的另外一种实现方式,同时还会有一些关于碰撞的检测等等的知识,敬请期待。若是有什么不懂或者是错误的地方,欢迎给位朋友在留言板留下你的想法,你的支持是我前进的动力

相关文章
相关标签/搜索