未加星标

曲线轨迹动画原理

字体大小 | |
[前端(javascript) 所属分类 前端(javascript) | 发布者 店小二04 | 时间 2017 | 作者 红领巾 ] 0人收藏点击收藏
一.动画函数

动画,是位移关于时间的函数:s = f(t)

自变量是 t ,因变量是 s ,物体的位移随着时间变化,看起来就是动画,例如:

// 已知 var property = 'marginLeft'; var s0 = 100; // 起点 var s1 = 200; // 终点 var duration = 1000; // 由题意得 var S = s1 - s0; // 总位移 var T = duration; // 总时间 // 求任意时刻t对应的位移 var t0 = +new Date(); var tick, interval = 1000 / 60; setTimeout(tick = function() { var t = +new Date() - t0; // 完成度 var p = Math.min(t / T, 1); // t时刻相对起点的位移s var s = S * p; document.body.style[property] = s0 + s + 'px'; if (p !== 1) setTimeout(tick, interval); }, interval);

marginLeft 从 100px 到 200px 均匀改变, body 先向右跳 100px ,然后在1秒内匀速向右移动 100px

要实现这样的动画,面临的 唯一问题 是:已知总位移 S 和总时间 T ,求任意时刻 t 相对起点的位移 s

我们实现了匀速直线运动,看起来好像没有用到 s = vt ,其实是有的:

s = v * t = (S / T) * t = S * (t / T) = S * p

因为动画函数是 s = f(t) ,里面没有 v ,需要把 v 换成已知量 ,因为完成度 p = t / T ,所以 动画也是位移关于完成度的函数

二.匀变速运动

同样的道理,换掉匀变速运动位移公式中的 v 和 a ,得到位移 s 关于时间 t 的函数

匀加速

位移公式:

// v0 = 0时,只有一个未知量a s = 1/2at^2

已知总时间 T 、总位移 S 、完成度 p = t / T ,求任意时刻 t 相对起点的位移 s :

// 终点处有 S = 1/2 * a * T^2 // 得 a = 2S / T^2 // 任意时刻 s = 1/2 * a * t^2 = 1/2 * (2S / T^2) * t^2 = 1/2 * 2S * (t^2 / T^2) = S * p^2 匀减速

位移公式:

// 含有2个未知量v0和a s = v0t - 1/2at^2

已知总时间 T 、总位移 S 、完成度 p = t / T ,求任意时刻 t 相对起点的位移 s :

// 1.逆向匀加速求v0 // 起点处有 S = 1/2 * a * T^2 // 得 a = 2S / T^2 v0 = aT = 2S / T^2 * T = 2S / T // 2.任意时刻 s = v0 * t - 1/2 * a * t^2 = (2S / T) * t - 1/2 * (2S / T^2) * t^2 = 2S * (t / T) - S * (t^2 / T^2) = 2S * p - S * p^2 = S * p * (2 - p) 三.曲线运动

简单的曲线运动可以分解成直线运动,例如正弦函数 y = sinx 可以分解为:

// x轴匀速直线 x = S * p = 2PI * p // y轴sinx y = sinx = sin(2PI * p)

平抛运动可以分解为:

// x轴匀速直线 x = S * p = X * p // y轴匀加速 y = S * p^2 = Y * p^2

抛物线的左半边可以看作向左平抛的逆向运动:

// x轴匀速直线 x = S * p = X * p // y轴匀减速 y = S * p * (2 - p) = Y * p * (2 - p)

圆周运动稍微特殊一点,代数方程 (x - a)^2 + (y - b)^2 = r^2 ,计算 x, y 存在取正负号的问题(比较麻烦,但可行),所以考虑用参数方程:

// 对于圆上任意一点,有 sinθ = y / r, cosθ = x / r // 得参数方程 x = a + r * cosθ, y = b + r * sinθ // 圆心为(0, 0)时 x = r * cosθ = r * cos(θ * p), y = r * sinθ = r * sin(θ * p) 角度 [0, 2PI] 均匀变化, x, y 随角度变化 P.S.圆周运动用极坐标解释起来有些 牵强 ( r(θ) = r ,设置圆心,再 [0, 360] 均匀 rotate ,没有 transform 的时代要怎么计算位置?) 四.easing函数

对比上面得出的公式:

s = S * p // 匀速 s = S * p^2 // 匀加速 s = S * p * (2 - p) // 匀减速 s = S * cos(2PI * p) // cos s = S * sin(2PI * p) // sin

发现总位移 S 不变,后面的部分不同,所以:

var easings = { linear: function(p) { return p; }, acceleration: function(p) { return p * p; }, deceleration: function(p) { return p * (2 - p); }, sin: function(p) { return Math.sin(2 * Math.PI * p); }, cos: function(p) { return Math.cos(2 * Math.PI * p); } }

这些 easing 函数用来修正 p ,所以动画应该是:

// 任意时刻t对应的位移 st = s0 + S * easing(p) // 即 // 当前值 = 初始值 + totalDelta * easing函数修正后的完成度

因为 p = t / T ,所以实际上 easing 作用于 t ,也叫 时间控制函数 ( timingFunction )

动画库都是这样干的,例如 jQuery :

// from https://github.com/jquery/jquery/blob/2d4f53416e5f74fa98e0c1d66b6f3c285a12f0ce/src/effects/Tween.js jQuery.easing = { linear: function( p ) { return p; }, swing: function( p ) { return 0.5 - Math.cos( p * Math.PI ) / 2; }, _default: "swing" };

velocity :

// from https://github.com/ayqy/velocity-1.4.1/blob/master/velocity.js Velocity.Easings = { linear: function(p) { // 线性,直接返回完成度 return p; }, swing: function(p) { // 两头慢中间快,cos从+1到-1变化,中间斜率最大变化最快 return 0.5 - Math.cos(p * Math.PI) / 2; }, /* Bonus "spring" easing, which is a less exaggerated version of easeInOutElastic. */ spring: function(p) { // easeInOutElastic的温和版 return 1 - (Math.cos(p * 4.5 * Math.PI) * Math.exp(-p * 6)); } };

其它复杂的 easing ,比如摩擦力( spring )、重力( bounce )等物理效果,常见的时间控制 easing 系列(各种Bezier曲线对应的缓动函数), step 效果也是同样的原理,修正完成度,也就是所谓的速度控制

五.在线Demo

通过 velocity 自定义 easing 和 Redirects 来实现这些曲线轨迹,例如:

// 自定义缓动函数 // 匀加速 Velocity.Easings.acceleration = function (p, opts, tweenDelta) { return p * p; }; // 匀减速 Velocity.Easings.deceleration = function (p, opts, tweenDelta) { return p * (2 - p); }; // 自定义动画效果 Velocity.Redirects['throw-h'] = function(element, options, elementsIndex, elementsSize, elements, promiseData) { Velocity(this, { translateX: [300, 'linear', 0], translateY: [300, 'acceleration', 0] }, options); }; // run Velocity(document.body, 'throw-h', 3000);

详细见Demo: http://ayqy.net/temp/curve-path-animation.html

Demo过程中发现 velocity 在完成度为1时,会强制赋值一遍终点,这在 sin 之类的场景下 有问题 ,源码如下:

else if (percentComplete === 1) { // 已完成,手动赋值,确保终点准确(不受计算精度影响) //!!! 不应该手动赋值终点了,因为sin之类的,终点是0 //!!! 强制赋值就错了 /* If this is the last tick pass (if we've reached 100% completion for this tween), ensure that currentValue is explicitly set to its target endValue so that it's not subjected to any rounding. */ // currentValue = tween.endValue; currentValue = tween.currentValue; }

修复方法是信任 easing 函数(直接去掉上面这部分内容),终点处也通过 easing 计算得到当前值,这样做的缺点是存在计算精度的问题,比如 sin 在 2PI 为0,计算结果是一个极小值,而不是0,但没什么实际影响

参考资料 关于动画,你需要知道的 :最后一遍看月影前辈的这篇文章

本文前端(javascript)相关术语:javascript是什么意思 javascript下载 javascript权威指南 javascript基础教程 javascript 正则表达式 javascript设计模式 javascript高级程序设计 精通javascript javascript教程

主题: jQuery360其实变量
分页:12
转载请注明
本文标题:曲线轨迹动画原理
本站链接:http://www.codesec.net/view/520919.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 前端(javascript) | 评论(0) | 阅读(47)