欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

js运动(四)——链式运动及完美运动框架(元素多属性同时运动)

程序员文章站 2022-03-27 13:06:32
...

1.链式运动

所谓链式运动,也即元素的可运动属性一个接着一个的发生变化,有其一定的需求性。前面所学的缓动函数无法满足链式运动的需求,缓动函数的叠加与定时器分配(一个元素只有一个定时器)之间构成了矛盾,造成只有最后一个缓动函数有效的局面。

为了能够实现链式运动,需要重新封装缓动函数。整体的思路是,在缓动函数中加一个可选的函数参数,当传入该函数时,则在一个属性变化完成后调用。

这样之后,每个缓动函数后面都可以再跟一个缓动函数,类似于递归,直至不再传递缓动函数。

缓动函数修改如下。

function chainingMoveTowards(obj, attribute, iTarget, /*optional*/func){
	var speed;
	clearInterval(obj.timer);	
	obj.timer = setInterval(function(){
		var cur = 0;
		if(attribute == 'opacity'){
			cur = Math.round(parseFloat(getStyleOf(obj, attribute) * 100));
		}else {
			cur = parseInt(getStyleOf(obj, attribute));
		}
		speed = (iTarget - cur) / 6;
        speed=speed>0?Math.ceil(speed):Math.floor(speed);
		if(cur == iTarget){
			clearInterval(obj.timer);
			if(func) func();
		}else{
			if(attribute == 'opacity'){
				obj.style.filter = 'alpha(opacity:' + (cur + speed) + ")";
				obj.style.opacity = (cur + speed) / 100;
			}else{
				obj.style[attribute] = cur + speed + "px";	
			}
		}
	},30);	
}

调用方法如下。

window.onload = function(){
	var oDiv = document.getElementById("div1");

	oDiv.onmouseover = function(){
		chainingMoveTowards(this, 'width', 400, function(){
			chainingMoveTowards(oDiv, 'height', 400);
		})
	};
	
	oDiv.onmouseout = function(){
		chainingMoveTowards(this, 'height', 50, function(){
			chainingMoveTowards(oDiv, 'width', 100);
		})
	};

};

以上有几点需要注意:

  1. 因为给缓动参数的函数参数是可选的,所以从代码可读性来说,应当增加/*optional*/标志;
  2. 在链式调用的过程中,谨慎使用this,第一次调用的时候this代表元素,第二次再调用时就不是了;
  3. 一般老说,链式运动的进行与恢复之间的顺序应该颠倒,如先变长再变宽,恢复时应该先恢复宽度再恢复长度。

链式运动有其优点,但是仍然无法满足一些特有的运动情况,如它只能使得元素的属性一个接着一个变化,但不能使得属性们同时变化。

作为进阶,需要向元素多属性同时运动方向上思考,这就要说道下面的完美运动框架了。

2.完美运动框架

纵观前面学习的运动知识,因为只给缓动函数传递了一个属性和一个目标值,所以只能对一个属性进行变化,有没有办法传入多个属性和目标值呢?

有!选项对象。

选项对象可以定义多个键值对。在缓动函数中对选项对象进行遍历,并一一作出相应动作,即可实现多个元素同时运动。
(原教程中把这个选项对象说成是json,个人觉得有误,json的键必须带双引号,而选项对象可以没有,所以传入的应该是一个对象而不是json)。

所以缓动函数如下。

// not final one
function multiAttrMoveTowards(elt, obj, /*optional*/func){
	var speed;
	clearInterval(elt.timer);
			
	elt.timer = setInterval(function(){

		for (var attr in obj){
			var cur = 0;
			if(attr == 'opacity'){
				cur = Math.round(parseFloat(getStyleOf(elt, attr) * 100));
			}else {
				cur = parseInt(getStyleOf(elt, attr));
			}
		
			speed = (obj[attr] - cur) / 6;
        	speed=speed>0?Math.ceil(speed):Math.floor(speed);
		
			if(cur == obj[attr]){
				clearInterval(elt.timer);
				if(func) func();
			}else{
				if(attr == 'opacity'){
					elt.style.filter = 'alpha(opacity:' + (cur + speed) + ")";
					elt.style.opacity = (cur + speed) / 100;
				}else{
					elt.style[attr] = cur + speed + "px";	
				}
			}
		}
	},30);	
}

这个缓动函数完成了多个属性的同时变化。但当其中一个属性变化很小时,属性们就有可能无法运行到终点。因为看似没有问题的程序,其实仔细一看,当有其中一个属性到达的时候,就关闭了定时器,其他属性自然无法再运动。

if(cur == obj[attr]){
    				clearInterval(elt.timer);
    				if(func) func();
    			}

因此,需要一个判断。一旦有一个没完成运动,则不关闭定时器。则,缓动函数及其应用如下。

window.onload = function (){
	var oDiv = document.getElementsByTagName("div")[0];
	
	oDiv.onmouseover = function(){
		multiAttrMoveTowards(this, {width: 101, height: 400, opacity:100});
	};
	
	oDiv.onmouseout = function(){
		multiAttrMoveTowards(this, {width: 100, height: 50 , opacity:30} );
		
	};
};


function multiAttrMoveTowards(elt, obj, /*optional*/func){
	var speed;
	clearInterval(elt.timer);
			
	elt.timer = setInterval(function(){
		var allArrive = true;
		for (var attr in obj){
			var cur = 0;
			if(attr == 'opacity'){
				cur = Math.round(parseFloat(getStyleOf(elt, attr) * 100));
			}else {
				cur = parseInt(getStyleOf(elt, attr));
			}
		
			speed = (obj[attr] - cur) / 6;
        	speed=speed>0?Math.ceil(speed):Math.floor(speed);
		
			if(cur != obj[attr]){
				allArrive = false;
			}
			
			if(attr == 'opacity'){
				elt.style.filter = 'alpha(opacity:' + (cur + speed) + ")";
				elt.style.opacity = (cur + speed) / 100;
			}else{
				elt.style[attr] = cur + speed + "px";	
			}
		}
		
		if (allArrive){
			clearInterval(elt.timer);
			if(func) func();
		}
	},30);	
}

为了规范化,将缓动函数里面的参数进行了更改。elt为元素(Element), obj为选项对象(Object)。最后一个可选的函数参数可以不要,但为了与链式运动的缓动函数兼容,可保留。

3.总结

以上即为今日所学。原教程中有几个对应的例子,但其只针对js进行讲说,忽略了元素布局,恰恰布局是其中比较重要的部分,因此,例子部分需时间专研,延后实施。

参考文献

【1】智能社:JavaScript教程——从入门到精通 ( https://ke.qq.com/course/152997?taid=766917950461349
【2】js运动(三)—— 多元素的运动)( https://blog.csdn.net/qq_31305965/article/details/89917314