jQuery-1.9.1源码分析系列(十五) 动画处理——缓动动画核心Tween
在jQuery内部函数Animation中调用到了createTweens()来创建缓动动画组,创建完成后的结果为:
可以看到上面的缓动动画组有四个原子动画组成。每一个原子动画的信息都包含在里面了。
仔细查看createTweens函数,实际上就是遍历调用了tweeners ["*"]的数组中的函数(实际上就只有一个元素)。
function createTweens( animation, props ) {
jQuery.each( props, function( prop, value ) {
var collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
index = 0,
length = collection.length;
for ( ; index < length; index++ ) {
if ( collection[ index ].call( animation, prop, value ) ) { // we're done with this property
return;
}
}
});
}
再次查看这个tweeners ["*"][0]函数,主要代码如下
function( prop, value ) {
var end, unit,
//根据css特征值获取缓动动画结构
tween = this.createTween( prop, value ),
parts = rfxnum.exec( value ),
target = tween.cur(),
start = +target || 0,
scale = 1,
maxIterations = 20; if ( parts ) {
end = +parts[2];
unit = parts[3] || ( jQuery.cssNumber[ prop ] ? "" : "px" ); //非像素单位的属性
if ( unit !== "px" && start ) {
// 从一个非零起点开始迭代,
//对于当前属性,如果它使用相同的单位这一过程将是微不足道
// 后备为end,或一个简单的常量
start = jQuery.css( tween.elem, prop, true ) || end || 1; do {
//如果前一次迭代为零,加倍,直到我们得到*东西*
//使用字符串倍增因子,所以我们不会偶然看到scale不改变
scale = scale || ".5"; // 调整和运行
start = start / scale;
jQuery.style( tween.elem, prop, start + unit ); // 更新scale, 默认0或NaN从tween.cur()获取
// 跳出循环,如果scale不变或完成时, 或者我们已经觉得已经足够了
} while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
} tween.unit = unit;
tween.start = start;
//如果提供了+=/-=记号,表示我们正在做一个相对的动画
tween.end = parts[1] ? start + ( parts[1] + 1 ) * end : end;
}
return tween;
}]
};
可以看出除了hide/show两种动画外的其他动画都经过tweeners ["*"][0]这个函数封装了动画组。其中有几个关键的数组start/end/unit。特别是对非像素单位的动画start值获取费了一番功夫。
还有一个比较关键的地方是都用了this.createTween获取单个css特征的基础的动画特征。而animation. createTween中直接调用jQuery.Tween来处理。接下来我们详解之。
a.jQuery.Tween
jQuery.Tween的结构和jQuery类似
function Tween( elem, options, prop, end, easing ) {
return new Tween.prototype.init( elem, options, prop, end, easing );
}
jQuery.Tween = Tween;
Tween.prototype = {
constructor: Tween,
init: function( elem, options, prop, end, easing, unit ) {
this.elem = elem;
this.prop = prop;
this.easing = easing || "swing";
this.options = options;
this.start = this.now = this.cur();
this.end = end;
this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
},
cur: function() {...},
run: function( percent ) {...}
}; Tween.prototype.init.prototype = Tween.prototype;
是不是有一种很熟悉的赶脚。
里面cur函数用来获取当前的css特征值
cur: function() {
var hooks = Tween.propHooks[ this.prop ]; return hooks && hooks.get ?
hooks.get( this ) :
Tween.propHooks._default.get( this );
},
而run函数则会在每个动画时间点上对正在进行的动画的每个特征值进行处理。
主要是两个步骤:
1.计算动画当前进度pos和动画当前位置now
//如果有动画时长则使用jQuery.easing计算出缓动动画进度eased,否则进度eased为percent
//并根据进度得到当前动画位置now
if ( this.options.duration ) {
this.pos = eased = jQuery.easing[ this.easing ](
percent, this.options.duration * percent, 0, 1, this.options.duration
);
} else {
this.pos = eased = percent;
}
this.now = ( this.end - this.start ) * eased + this.start;
2.根据当前进度情况设置css特征值
//设置css特征值
if ( hooks && hooks.set ) {
hooks.set( this );
} else {
Tween.propHooks._default.set( this );
}
return this;
可见生成缓动动画这一步处理才是整个动画的核心:
创建缓动动画组,每一个原子动画都包含了每一个原子css属性动画的各种必要参数以及动画函数
不同的是hide/show直接在defaultPrefilter中创建了这个缓动动画组(所有的属性都默认是px单位),其他的动画在调用createTweens时创建缓动动画组。
还记不记得在创建动画的时候有个tick函数,这个tick函数会在每隔一个步长的时间调用一次
tick = function() {
...
length = animation.tweens.length; for ( ; index < length ; index++ ) {
animation.tweens[ index ].run( percent );
}
...
}
看到没,每一个原子动画有自己的run函数来执行自己的动画,这在创建缓动动画组的时候就建好了的。
好了,整理一下动画的整个核心流程:
1.先根据参数调用jQuery.speed获取动画相关参数,得到一个类似如下的对象;并且生成动画执行函数doAnimation使用.queue压入队列并马上执行
opt = { complete: fnction(){...},//动画执行完成的回调 duration: 400,//动画执行时长 easing: "swing",//动画效果 queue: "fx",//动画队列 old: false/fnction(){...}, }
2.doAnimation中调用创建一个延时对象,使用延时对象的promise方法构造一个动画对象animation(延时对象+动画特征列表),最后给animation添加动画执行完成后的回调函数。
3.调用jQuery内部函数proFilter修正css特征名以便能被当前浏览器识别,并将某些复合css特征分解(比如padding分解成paddingTop / Right/ Bottom/ Left).
4.调用jQuery内部函数defaultPrefilter做动画能够正常运行前提条件修正:比如对height/width动画display和overflow需要特定的值。特别需要注意的是
对于show/hide动画,在之前就调用genFx将需要执行动画的css特征提取了出来,在defaultPrefilter函数里直接调用动画对象animation.createTween给每一个CSS动画属性添加对应的缓动动画对象(包括动画参数和动画函数如run)压入缓动动画组animation.tweens中
5.调用jQuery内部函数createTweens将除开show/hide之外的动画每一个css动画特征使用animation.createTween创建缓动动画对象(包括动画参数和动画函数如run),压入缓动动画组animation.tweens中
6.启动动画计时,在每个时间点上执行tick函数来给相应的css特征值设置运动值。
其中css特征值运动的进度百分比是
remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), temp = remaining / animation.duration || 0, percent = 1 - temp
得到的percent是符合时间规律的。代入这个percent设置准确的css特征值,以刷新动画显示。
8.动画完成后调用动画完成回调。
如果觉得本文不错,请点击右下方【推荐】!
jQuery-1.9.1源码分析系列(十五) 动画处理——缓动动画核心Tween的更多相关文章
- ABP源码分析二十五:EventBus
IEventData/EventData: 封装了EventData信息,触发event的源对象和时间 IEventBus/EventBus: 定义和实现了了一系列注册,注销和触发事件处理函数的方法. ...
- ABP源码分析三十五:ABP中动态WebAPI原理解析
动态WebAPI应该算是ABP中最Magic的功能之一了吧.开发人员无须定义继承自ApiController的类,只须重用Application Service中的类就可以对外提供WebAPI的功能, ...
- ABP源码分析四十五:ABP ZERO中的EntityFramework模块
AbpZeroDbContext:配置ABP.Zero中定义的entity的Dbset EntityFrameworkModelBuilderExtensions:给PrimitiveProperty ...
- Android源码分析(十五)----GPS冷启动实现原理分析
一:原理分析 主要sendExtraCommand方法中传递两个参数, 根据如下源码可以知道第一个参数传递delete_aiding_data,第二个参数传递null即可. @Override pub ...
- spark 源码分析之十五 -- Spark内存管理剖析
本篇文章主要剖析Spark的内存管理体系. 在上篇文章 spark 源码分析之十四 -- broadcast 是如何实现的?中对存储相关的内容没有做过多的剖析,下面计划先剖析Spark的内存机制,进而 ...
- Vue.js 源码分析(二十五) 高级应用 插槽 详解
我们定义一个组件的时候,可以在组件的某个节点内预留一个位置,当父组件调用该组件的时候可以指定该位置具体的内容,这就是插槽的用法,子组件模板可以通过slot标签(插槽)规定对应的内容放置在哪里,比如: ...
- jQuery-1.9.1源码分析系列(五) 回调对象
jQuery.Callbacks()提供的回调函数队列管理本来是延时回调处理的一部分,但是后面将其独立出来作为一个模块.jQuery就是这样,各个模块间的代码耦合度是处理的比较好的,值得学习.虽然是从 ...
- Spring源码分析(十五)获取单例
本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 之前我们讲解了从缓存中获取单例的过程,那么,如果缓存中不存在已经加载的单例be ...
- Thinkphp源码分析系列(五)–系统钩子实现
Thinkphp的插件机制主要依靠的是Hook.class.php这个类,官方文档中在行为扩展也主要依靠这个类来实现.下面我们来具体看看tp是怎么利用这个类来实现行为扩展的. 首先,行为扩展是什么?有 ...
- 介绍开源的.net通信框架NetworkComms框架 源码分析(十五 ) CommsThreadPool自定义线程池
原文网址: http://www.cnblogs.com/csdev Networkcomms 是一款C# 语言编写的TCP/UDP通信框架 作者是英国人 以前是收费的 目前作者已经开源 许可是 ...
随机推荐
- VS加入全局缓存
1. 加载 gacutil.exe /i <DLL Name>.dll 2. 卸载 gacutil.exe /u <DLL Name>
- 安卓调用百度地图api 错误 mcode参数不存在
自己的手机app里用到了百度地图sdk,希望根据手机获得的坐标来逆向到百度地图的坐标. 根据api文档拼写了url,因为是移动端,说是要添加mcode参数,然后我的url看起来如下: http://a ...
- 分享我们项目中基于EF事务机制的架构
写在前面: 1. 本文中单元测试用到的数据库,在执行测试之前,会被清空,即使用空数据库. 2. 本文中的单元测试都是正确通过的. 要理解EF的事务机制,首先要理解这2个类:TransactionSco ...
- iOS开发系列--视图切换
概述 在iOS开发中视图的切换是很频繁的,独立的视图应用在实际开发过程中并不常见,除非你的应用足够简单.在iOS开发中常用的视图切换有三种,今天我们将一一介绍: UITabBarController ...
- Redis系列(六)-SortedSets设计技巧
阅读目录: 介绍 Score占位 更多位信息 总结 介绍 Redis Sorted Sets是类似Redis Sets数据结构,不允许重复项的String集合.不同的是Sorted Sets中的每个成 ...
- 为什么google bazel构建工具流行不起来
作者Jack47 转载请保留作者和原文出处 之前博主写了系列文章Google软件构建工具Bazel原理及使用方法介绍.最近使用了一段时间后,觉得这个东西不是一种通用的构建工具,很难对接到情况复杂的大的 ...
- Python调用C++的DLL
import os import sys from ctypes import * test = cdll.LoadLibrary('D:\Python27\py.dll') print test.A ...
- 让Chrome 接管邮件连接,收发邮件更方便了
页面中除了传统的超链接外,还可以将邮箱地址写入<a>标签,意思不表自明,当然是用户点击后就会打开相应的邮件客户端向这个连接指向的邮件地址发邮件. <a href="mail ...
- Azure SQL Database (22) 迁移部分数据到Azure Stretch Database
<Windows Azure Platform 系列文章目录> Azure SQL Database (19) Stretch Database 概览 Azure SQL Da ...
- 《Entity Framework 6 Recipes》中文翻译系列 (21) -----第四章 ASP.NET MVC中使用实体框架之在页面中创建查询和使用ASP.NET URL路由过虑
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 4.2. 构建一个搜索查询 搜索数据是几乎所有应用的一个基本功能.它一般是动态的,因 ...