旧文一篇,2014年发布于前端乱炖中,是当时学习的整理,现在拿过来备份凑个篇数;
和大家分享一个原生的运动框架。下面的框架,我加了参数过滤,略微完善了该运动框架,是今天刚发现的问题,和大家一块儿学习下。
/// <summary>
/// 运动框架,支持匀速、缓冲,加速等运动
/// </summary>
/// <param name="obj"> 需要移动的对象 </param>
/// <param name="json"> 参数 </param>
/// <param name="options"> 自定义设置 </param>
function startMove(obj, json, options) {
options = options || {};
options.time = options.time || 3000;
options.type = options.type || "linear";
var count = parseInt(options.time / 30);
var n = 0;
var start = {};
var distance = {};
json = formatStyleName(json);
for (var name in json) {
if (name == "opacity") {
start[name] = parseInt(100 * Math.round(getStyle(obj, "opacity")));
} else {
start[name] = parseInt(getStyle(obj, name));
}
//解决属性在CSS中未赋值的问题,像默认的 auto 等
if (isNaN(start[name]))
{
var initParams = {
'left': obj.offsetLeft,
'top': obj.offsetTop,
'width': obj.offsetWidth,
'height': obj.offsetHeight,
'margin': 0,
'marginLeft': 0,
'marginTop': 0,
'marginRight': 0,
'marginBottom': 0,
'padding': 0,
'paddingLeft': 0,
'paddingTop': 0,
'paddingRight': 0,
'paddingBottom': 0,
'borderWidth': 0,
'borderLeftWidth': 0,
'borderRightWidth': 0,
'borderTopWidth': 0,
'borderBottomWidth': 0,
'opacity': 100,
'zIndex': 0
}
start[name] = initParams[name] || 0; //当initParams中没有匹配的属性时,可以做错误处理,直接抛出异常
}
distance[name] = json[name] - start[name];
}
clearInterval(obj.timer);
obj.timer = setInterval(function () {
n++;
for (var name in json) {
switch (options.type) {
case "linear":
var cur = start[name] + n * distance[name] / count;
break;
case "buffer":
var a = 1 - n / count;
var cur = start[name] + distance[name] * (1 - a * a * a);
break;
case "speed":
var a = n / count;
var cur = start[name] + distance[name] * a * a * a;
break;
}
if (name == "opacity") {
obj.style.filter = "alpha(opacity:" + cur + ")";
obj.style.opacity = cur / 100;
} else {
obj.style[name] = cur + "px";
}
}
if (n == count) {
clearInterval(obj.timer);
options.end && options.end();
}
}, 30);
}
/// <summary>
/// 格式化参数中的属性
/// </summary>
/// <param name="json"> </param>
function formatStyleName(json) {
for (var name in json) {
var attr = transformStyleName(name);
if (attr != name) {
json[attr] = json[name];
delete json[name];
}
}
return json;
}
/// <summary>
/// 将CSS属性转换成JS中的属性,即将类似 "margin-left" 的属性转换成 "marginLeft"
/// </summary>
/// <param name="name"> 属性名称 </param>
function transformStyleName(name) {
if (name == "float") {
return obj.currentStyle ? "styleFloat" : "cssFloat";
} else if (name.indexOf("-") > -1) {
name = name.replace(/-(\w)/, function () {
return arguments[1].toUpperCase();
});
}
return name;
}
/// <summary>
/// 取属性所对应的样式值
/// </summary>
/// <param name="name"> 属性名称 </param>
function getStyle(obj, name) {
name = transformStyleName(name);
return obj.currentStyle ? obj.currentStyle[name] : getComputedStyle(obj, false)[name];
}
使用方法:
window.onload = function(){
var oDiv1 = document.getElementById("div1");
startMove(oDiv1, {"left" : 500}, {time : 4000, type : "buffer"});
}
有几个需要注意的地方:
1、getStyle()中,obj.currentStyle[name] 这个是兼容IE的,getComputedStyle(obj,false)[name] 这个是兼容 FF 和 Chrome 的,name一定是JS中的style属性才能够兼容,像 “marginLeft” 这种;
2、JS 的 style 属性中,float 是比较特殊的,IE 中是这样取:obj.style.styleFloat,其他浏览器是这样取:obj.style.cssFloat;
剩下的,对于没有中划线的css属性一般直接使用style.属性名即可,对于含有中划线的css属性,将每个中划线去掉并将每个中划线后的第一个字符换成大写;
3、getStyle()中,第一句 name = transformStyleName(name); 在这个例子中是多余的,因为传入的 name 是已经格式化好的,在项目中用的时候,最好是加上,因为其他地方可能直接调用;
4、getStyle()中的代码可以利用惰性载入的技巧进行改进,只在第一次调用的时候判断:
function getStyle(obj, name) {
if (obj.currentStyle) {
getStyle = function (obj, name) {
name = transformStyleName(name);
return obj.currentStyle[name];
}
} else {
getStyle = function (obj, name) {
name = transformStyleName(name);
return getComputedStyle(obj, false)[name]
}
}
return getStyle(obj, name);
}
5、三种运动方式:
5.1、匀速运动,这个没什么好说的吧;
5.2、加速运动,加速度越来越大,这个可以通过数学知识推出来
第一象限中,0<=x<=1,y=x² 或者三次方/四次方,或者其他斜率不断变大公式;
5.3、减速运动/缓冲运动,同5.2中的原理;
* 2014/04/01 添加(下面一条不是愚人节玩笑,是真的)
6、上面代码中的定时器用的是 setInterval(),这个会存在隐藏的bug,执行的次数可能会少于预想次数,导致元素到不了目标位置(产生的原因是JavaScript引擎对定时器的解析方式)。可以用链式 setTimeout() 来解决该隐藏bug;
*缺乏专业术语,导致很多意思说的不是很明白,大家见谅吧;
*可以根据上面的六条对运动框架再做一次升级,这儿我就不发改后的了;
*太容易得到的东西最可能不会珍惜;
*不要相信我所说的话以及代码,一定要亲自实践。实践是检验真理的唯一标准,包括在网上看到的所有文章,不要轻易相信,实践出真理。
像很多关于JS优化的文章,都会提 for(var i=0; i< arr.length, i++){ …… },要把 arr.length 先取出来,优化的效果真的有那么明显么?
还有什么字符串拼接尽量用数组代替,因为这样效率高;在C#这种高级语言中,思路是完全没有问题的(C#中的StringBuilder),但在JS中,真的适用吗?