verlet.js 运动算法详解
本系列文章可以在这里查看:verletjs动画框架
你可以从这里查看该项目的所有源文件:
涉及文件:lib/vec2.js
构造函数
1 2 3 4 | function Vec2(x, y) { this .x = x || 0; this .y = y || 0; } |
该函数定义了空间中(二维)的一点,对应canvas的坐标性质(左上角为坐标原点)
向量
正如高中数学所学,verletjs的动画都是基于向量实现的,向量顾名思义:有大小,有方向,这里的方向就是x,y两个方向。动画中涉及的任何一个“量”都可以看成一个向量。如:速度、重力、摩擦力等等。
向量加法
1 2 3 | Vec2.prototype.add = function (v) { return new Vec2( this .x + v.x, this .y + v.y); } |
如果一个向量A[1,2],另一个向量B[3,4];那么向量A+B=[1+3,2+4]=[4,6]
注意:以下的几个方法都是返回一个新对象。
向量减法
1 2 3 | Vec2.prototype.sub = function (v) { return new Vec2( this .x - v.x, this .y - v.y); } |
向量乘法
1 2 3 | Vec2.prototype.mul = function (v) { return new Vec2( this .x * v.x, this .y * v.y); } |
向量除法
1 2 3 | Vec2.prototype.div = function (v) { return new Vec2( this .x / v.x, this .y / v.y); } |
缩放
1 2 3 | Vec2.prototype.scale = function (coef) { return new Vec2( this .x*coef, this .y*coef); } |
其实缩放可以看成乘法,也可以看成除法;因为都能通过以上两种方法来实现。
位置重置
1 2 3 4 5 | Vec2.prototype.mutableSet = function (v) { this .x = v.x; this .y = v.y; return this ; } |
向量是否相等
1 2 3 | Vec2.prototype.equals = function (v) { return this .x == v.x && this .y == v.y; } |
向量的长度(模)
1 2 3 | Vec2.prototype.length = function (v) { return Math.sqrt( this .x* this .x + this .y* this .y); } |
可以这么理解,速度是有大小,有方向的。但是当我们需要用速度大小进行计算的时候就需要用到速度的模了。
两点间的距离(标量)
1 2 3 4 5 6 7 8 | Vec2.prototype.dist2 = function (v) { var x = v.x - this .x; var y = v.y - this .y; return x*x + y*y; } Vec2.prototype.dist = function (v) { return Math.sqrt( this .dist2(v)); } |
法向量
1 2 3 4 | Vec2.prototype.normal = function () { var m = Math.sqrt( this .x* this .x + this .y* this .y); return new Vec2( this .x/m, this .y/m); } |
所谓法向量就是和当前向量夹角为90度的向量;可以理解为:物体的重力和受到地面的反作用力。
向量点积
1 2 3 | Vec2.prototype.dot = function (v) { return this .x*v.x + this .y*v.y; } |
向量的点积得到的结果是一个数字,并非是一个向量
两个向量之间的夹角
1 2 3 4 5 6 7 | Vec2.prototype.angle = function (v) { return Math.atan2( this .x*v.y- this .y*v.x, this .x*v.x+ this .y*v.y); } Vec2.prototype.angle2 = function (vLeft, vRight) { return vLeft.sub( this ).angle(vRight.sub( this )); } |
向量旋转
1 2 3 4 5 | Vec2.prototype.rotate = function (origin, theta) { var x = this .x - origin.x; var y = this .y - origin.y; return new Vec2(x*Math.cos(theta) - y*Math.sin(theta) + origin.x, x*Math.sin(theta) + y*Math.cos(theta) + origin.y); } |
这个旋转是基于某个位置,某个角度进行的旋转。旋转后得到一个新的向量;旋转的用处很大,比如有些时候根据鼠标的移动展现不同角度的动画。旋转在3D动画开发中尤其重要,接下来补充几个3D旋转算法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | /* * 绕X轴旋转 * @param {Number} angle 旋转的角度(弧度) */ Vector3D.prototype.rotateX: function (angle) { var ca = Math.cos(angle); var sa = Math.sin(angle); var tmpY = this .y * ca - this .z * sa; var tmpZ = this .y * sa + this .z * ca; this .y = tmpY; this .z = tmpZ; } /* * 绕Y轴旋转 * @param {Number} angle 旋转的角度(弧度) */ Vector3D.prototype.rotateY: function (angle) { var ca = Math.cos(angle); var sa = Math.sin(angle); with ( this ) { var tmpX = x * ca + z * sa; var tmpZ = x * -sa + z * ca; x = tmpX; z = tmpZ; } } |
实际应用中并不推荐使用with语句,其实with语句的目的就是改变当前语句块的执行环境;即把this注入到语句块中,省去每次都要写this的麻烦。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | /* * 绕Z轴旋转 * @param {Number} angle 旋转的角度(弧度) */ Vector3D.prototype.rotateZ: function (angle) { var ca = Math.cos(angle); var sa = Math.sin(angle); var tmpX = this .x * ca - this .y * sa; var tmpY = this .x * sa + this .y * ca; this .x = tmpX; this .y = tmpY; } /* * 绕XY轴旋转 * @param {Number} a 旋转的角度(弧度) * @param {Number} b 旋转的角度(弧度) */ Vector3D.prototype.rotateXY: function (a, b) { var ca = Math.cos(a); var sa = Math.sin(a); var cb = Math.cos(b); var sb = Math.sin(b); with ( this ) { // 绕x轴旋转 var rz = y * sa + z * ca; y = y * ca - z * sa; // 绕y轴旋转 z = x * -sb + rz * cb; x = x * cb + rz * sb; } } /* * 绕XYZ轴旋转 * @param {Number} a 旋转的角度(弧度) * @param {Number} b 旋转的角度(弧度) * @param {Number} c 旋转的角度(弧度) */ Vector3D.prototype.rotateXYZ: function (a, b, c) { var ca = Math.cos(a); var sa = Math.sin(a); var cb = Math.cos(b); var sb = Math.sin(b); var cc = Math.cos(c); var sc = Math.sin(c); with ( this ) { // x var ry = y * ca - z * sa; var rz = y * sa + z * ca; // y var rx = x * cb + rz * sb; z = x * -sb + rz * cb; // z x = rx * cc - ry * sc; y = rx * sc + ry * cc; } } |
如何使用
动画中的一切和位置相关的计算都可以用向量来表示,这里以自由落体为例。
1、物体下落,有自身的重力、空气的阻力作用
2、假设物体从初速度为0开始下落
3、可以计算该物体下落的加速度
具体实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // 初速度 var vel = new Vec2(0, 0); // 重力为10,方向垂直向下 var gravity = new Vec2(0, 10); // 空气的阻力,大小为8,方向和重力相反 var f = new Vec2(0, 8); // 计算物体所受合力 var all = gravity.sub(f); // (0, 2) // 循环调用loop函数(可以理解为动画的一帧,循环播放。为一个时间单位) var loop = function () { // 下一时刻的速度 // 由于每帧为一个时间单位,也可以直接理解为下一时刻的位移 // 然后在canvas中按照vel的位置重绘就得到了自由落体的效果 vel.add(all); console.log(vel); window.requestAnimationFrame(loop); } loop(); |
接下来的几部分讲解verletjs是如何利用该动画原理来实现动画框架的。
特别申明:
本站所有资源都是由网友投稿发布,或转载各大下载站,请自行检测软件的完整性!
本站所有资源仅供学习与参考,请勿用于商业用途,否则产生的一切后果将由您自己承担!
如有侵权请联系我们删除下架,联系方式:lei1294551502@163.com