原生JS运动框架

旧文一篇,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中,真的适用吗?

如果这篇文章对你有用,可以点击下面的按钮告诉我

0

发表回复