前言
jQuery动画是通过animate这个API设置执行的,其内部也是按照每一个animate的划分封装了各自动画组的行为,
包括数据过滤、缓动公式、一些动画默认参数的设置、元素状态的调整、事件的处理通知机制、执行等等
换句话说,我们可以把animate看作一个对象,对象封装自己的一系列属性与方法。
jQuery可以支持连续动画,那么animate与animate之间的切换就是通过队列.queue,这个之前就已经详细的解释过了
动画的参数
jQuery的内部的方法都是针对API的处理范围设计的
我们看看Animation方法的支持情况:
.animate( properties, options )
- 区别就与第二组数据的传递了,options是支持对象传参
- properties参数就是写一个CSS属性和值的对象,动画都是涉及变化的,那么什么值才能变化?
- 理论上来说有数值的属性都是可以变化的,
width
,height
或者left
可以执行动画,但是background-color
不能,但是也不是绝对的,主要看数据的解析度,可以用插件支持 - 除了样式属性, 一些非样式的属性,如
scrollTop
和scrollLeft
,以及自定义属性,也可应用于动画 - 除了定义数值,每个属性能使用
'show'
,'hide'
, 和'toggle'
。这些快捷方式允许定制隐藏和显示动画用来控制元素的显示或隐藏。为了使用jQuery内置的切换状态跟踪,'toggle'
关键字必须在动画开始前给定属性值
简单的来说,就是把一对的参数丢大animate方法里面,然后animate就开始执行你参数规定的动画了,
那么动画每执一次就会通过回调通知告诉开发者,具体有complete/done/fail/always/step接口等等
理解定义
style="background:red;opacity:1;position: relative; left: 500px;" />
book.animate({
opacity: 0.25,
left: '50',
height: 'toggle'
}, {
duration :1000,
specialEasing: {
height: 'linear'
},
step: function(now, fx) {
console.log('step')
},
progress:function(){
console.log('progress')
},
complete:function(){
console.log('动画完成')
}
})
首先,动画的参数都是最终值都是相对数据
如上img元素的起始
opacity是1,那么通过动画改成成0.25
left是500,那么通过动画改成成50
height为'toggle' 意味着如果是隐藏与显示的自动切换
step:是针对opacity/left/height各自动画,每次改变通知三次
progress 是把opacity/left/height看成一组了,每次改变只通知一次
动画的原理
jQuery动画的原理还是很简单的,靠定时器不断的改变元素的属性
我们模拟下animate的大致实现
让元素执行一个2秒的动画到坐标为left 50的区域
duration: '2000'
}
按照常规的思路,我们需要3个参数
- 动画开始位置
- 动画的结束位置
- 动画的运行时间
思路一:等值变化
我们在animate内部还需要计算出当然元素的初始化布局的位置(比如500px),那么我们在2秒的时间内需变换成50px,也就是运行的路劲长就是500-50 = 450px
那么算法是不是呼之欲出了?
每毫秒移动的距离 pos = 450/2000 = 0.225px
每毫秒移动left = 初始位置 (+/-) 每毫秒递增的距离(pos * 时间)
这样算法我们放到setInterval就会发现错的一塌糊涂,我们错最本质的东西:JS是单线程,定时器都是排队列的,理论上也达不到1ms绘制一次dom
所以每次产生的这个下一次绘制的时间差根本不是一个等比的,所以我们按照线性的等值递增是有误的
//动画初始值
var start = 500
//动画结束值
var end = options.left
//动画id
var timerId;
var createTime = function(){
return (+new Date)
}
var startTime = createTime();
//需要执行动画的长度
var anminLength = start - end;
//每13毫秒要跑的位置
var pos = anminLength/options.time * 13
var pre = start;
var newValue;
function tick(){
if(createTime() - startTime < options.time){
newValue = pre - pos
//动画执行
elem.style['left'] = newValue + 'px';
pre = newValue
}else{
//停止动画
clearInterval(timerId);
timerId = null;
console.log(newValue)
}
}
//开始执行动画
var timerId = setInterval(tick, 13);
}
思路一实现:
为避免看官乏味,本系列博客限定在较新的.Net framework 4.5.1,Asp.net MVC5,IIS 7.X集成模式。
对于微软应用层的技术。我向来不舍得花太多时间学习。但又由于公司现有的开发建立于Asp.net MVC5上,实在是有必要深入理解一下ASP.NET MVC5,故安排了约2-3周的时间摸摸脉,这个系列会是一边学习一边总结的产物。
从ASP.NET处理流程说起
如果自己去写一个Web服务器,最简单的方法就是:
1 监听请求-->2 获取请求信息-->3 处理请求-->4 发送响应
Asp.net大体也是做的,它干了这么点事:
1 HTTP.SYS 位于内核模式,监听请求。
2 一旦监听到请求,转给W3SRV。
3 W3SRV将请求转给WAS。
4 WAS创建并管理应用程序池进程W3WP.EXE。
5 W3WP.EXE创建运行时,处理请求。
6 发送响应。
拓展:
1 为什么需要WAS?
WAS在IIS7之前是没有的,之所以抽象出这一层,是因为WAS不再参与具体的应用层协议的解析。这么讲:IIS支持Http协议,同时也支持WCF寄宿,咋干的?我们知道WCF的协议和HTTP协议当然是不一样的,但端口只有一个,肿么办?计算机世界里有这么一句话:计算机世界的绝大部分问题都可以通过分层的方法来解决。在协议适配和数据处理之间增加一个虚拟层即可。具体的,让每一个协议都有它自己的协议解析模块,将解析出来的数据都转给WAS,最后让WAS包装成WorkerRequest封送入ASP.NET的请求管道。这里,与IIS6中不同,W3SRV不再和ASP.NET直接接洽,它只负责接受HTTP.SYS监听的HTTP协议的请求,解析成请求数据,转给WAS。那么WCF请求呢?
好的,了解得多一点的同学就知道windows上有这么个服务:NetTcpPortSharing,即端口共享服务。这么一说,好像一个端口可以被多应用程序监听一样。妈蛋,说好的世界观呢?多个进程可以共享一个端口,尼玛编程给我看看?
对Socket熟悉的同学可以自己编程试试。此处略去一千字。
好吧,先引入原文:
Internet Information Services (IIS) has a listener to share a port between multiple HTTP applications. IIS listens on the port directly and forwards messages to the appropriate application based on information inside the message stream. This allows multiple HTTP applications to use the same port number without having to compete to reserve the port for receiving messages.
这么回事。IIS有一个共享端口,这个端口在接收到基于HTTP协议请求之后,会将消息再转一份给其它同样需要处理这个请求的HTTP应用程序。我们知道WCF是有HTTPBinding嘛,那么接下来,画张图来看看:
具体细节可能不会是这样,这是我大概的一个猜测而已,但是思路定当如此。
ASP.NET处理管道
如果应用程序是第一次被访问,那么IIS在加载CLR之后,会创建应用程序域。一个特殊的运行时IsapiRuntime被加载,同时请求报文被封装为IsApiWorkerRequest.看过上篇的同学,应该知道怎么弄源码了。这里简单将源代码列出来。
{
public int ProcessRequest(IntPtr ecb, int iWRType) {
IntPtr pHttpCompletion = IntPtr.Zero;
if (iWRType == WORKER_REQUEST_TYPE_IN_PROC_VERSION_2) {
pHttpCompletion = ecb;
ecb = UnsafeNativeMethods.GetEcb(pHttpCompletion);
}
ISAPIWorkerRequest wr = null;
try {
bool useOOP = (iWRType == WORKER_REQUEST_TYPE_OOP);
wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, useOOP);
wr.Initialize();
// check if app path matches (need to restart app domain?)
String wrPath = wr.GetAppPathTranslated();
String adPath = HttpRuntime.AppDomainAppPathInternal;
if (adPath == null ||
StringUtil.EqualsIgnoreCase(wrPath, adPath)) {
HttpRuntime.ProcessRequestNoDemand(wr);
return 0;
}
else {
// need to restart app domain
HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged,
SR.GetString(SR.Hosting_Phys_Path_Changed,
adPath,
wrPath));
return 1;
}
}
catch(Exception e) {
try {
WebBaseEvent.RaiseRuntimeError(e, this);
} catch {}
// Have we called HSE_REQ_DONE_WITH_SESSION? If so, don't re-throw.
if (wr != null && wr.Ecb == IntPtr.Zero) {
if (pHttpCompletion != IntPtr.Zero) {
UnsafeNativeMethods.SetDoneWithSessionCalled(pHttpCompletion);
}
// if this is a thread abort exception, cancel the abort
if (e is ThreadAbortException) {
Thread.ResetAbort();
}
// IMPORTANT: if this thread is being aborted because of an AppDomain.Unload,
// the CLR will still throw an AppDomainUnloadedException. The native caller
// must special case COR_E_APPDOMAINUNLOADED(0x80131014) and not
// call HSE_REQ_DONE_WITH_SESSION more than once.
return 0;
}
// re-throw if we have not called HSE_REQ_DO
没有评论:
发表评论