博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
mass Framework fx模块 v3
阅读量:6184 次
发布时间:2019-06-21

本文共 23196 字,大约阅读时间需要 77 分钟。

留作备份,作为承诺,它已为框架实现对css3 transform2D的支持,尽管IE下对位移不那么完美。

它完美地实现了jquery动画模块95%的功能,而且天然支持颜色渐变,支持动画回放。

//=========================================// 动画模块v3//==========================================$.define("fx", "css",function(){    //$.log("已加载fx模块");    var types = {        color:/color/i,        transform:/rotate|scaleX|scaleY|translateX|translateY/i,        scroll:/scroll/i,        _default:/fontSize|fontWeight|opacity|width|height|top$|bottom$|left$|right$/i    },    rfxnum = /^([+\-/\*]=)?([\d+.\-]+)([a-z%]*)$/i;    var adapter = $.fxAdapter = {        _default:{            get:function(el, prop) {                return $.css(el,prop);            },            tween :function(form,change,name,per) {                var a = (form + change * $.easing[name](per)).toFixed(3);                return isNaN(a) ? 0 : a;            }        },        type: function (attr){//  用于取得适配器的类型            for(var i in types){                if(types[i].test(attr)){                    return i;                }            }            return "_default";        }    }    var tween = adapter._default.tween;    $.mix(adapter,{        scroll : {            get: function(el, prop){                return el[prop];            },            tween: tween        },        transform:{            get: function(el, prop){                return $.transform(el)[prop]            },            set:function(el,t2d,isEnd,per){                var obj = {}                for(var name in t2d){                    obj[name] = isEnd ? t2d[name][1] : tween(t2d[name][0],t2d[name][2],t2d[name][3],per);                }                $.transform(el,obj);            }        },        color : {            get:function(el,prop){                return  $.css(el,prop);            },            tween:function(f0,f1,f2,c0,c1,c2,name,per,i){                var delta = $.easing[name](per), ret = [];                for(i = 0;i < 3;i++){                    ret[i] = Math.max(Math.min((arguments[i] +arguments[i+3] * delta)|0, 255), 0);                }                return "rgb("+ret+")";            }        }    } );    //中央定时器,可以添加新节点到中央列队,然后通过setInterval方法不断调用nextTick处理所有节点的动画    function heartbeat( node) {        heartbeat.nodes.push( node);        if (heartbeat.id === null) {            heartbeat.id = setInterval(nextTick, 13);//开始心跳        }        return true;    }    heartbeat.nodes = []; //中央列队    heartbeat.id = null;  //原始的setInterval id    //驱动中央列队的元素节点执行它们的动画,如果执行完毕就把它们从列队中剔除,如果列队为空则中止心跳    function nextTick() {        var nodes = heartbeat.nodes, i = 0, n = nodes.length;        for (; i < n; i++) {            if ( animate(nodes[i]) === false) {//在这里操作元素的样式或属性进行渐变                nodes.splice(i, 1);                i -= 1;                n -= 1;            }        }        nodes.length || (clearInterval(heartbeat.id), heartbeat.id = null);    }    var keyworks = $.oneObject("before,frame,after,easing,revert,record");    //处理特效的入口函数,用于将第二个参数,拆分为两个对象props与config,然后再为每个匹配的元素指定一个双向列队对象linked    //linked对象包含两个列队,每个列队装载着不同的特效对象    $.fn.fx = function( duration, hash ){        var props = hash ||{}, config = {}, p        if(typeof duration === "function"){            props.after = duration;            duration = null;        }        for( var name in props){            p = $.cssName(name) || name;            if( name != p ){                props[ p ] = props[ name ];                delete props[ name ];            }else if(  keyworks[name] ){                config[ name ] =  props[ name ]                 delete props[ name ];            }        }        var easing = (config.easing || "swing").toLowerCase() ;        config.easing = $.easing[ easing ] ? easing : "swing";        config.duration = duration || 500;        config.method = "noop";        return this.each(function(node){            var linked = $._data(node,"fx") || $._data( node,"fx",{                positive: [], //正向列队                negative: [], //负向列队                run: false            });            linked.positive.push({//fx对象                startTime:  0,//timestamp                config:   $.mix({}, config),//各种配置                props:    $.mix({}, props)//用于渐变的属性            });            if(!linked.run){                linked.run = heartbeat( node);            }        });    }    function animate( node ) {//linked对象包含两个列队(positive与negative)        var linked = $._data( node,"fx") ;        if(!linked){//如果在动画过程中,元素被移除掉            return false;        }        var fx = linked.positive[0],  now, isEnd, mix;        if( isFinite( fx ) ){//如果此时调用了delay方法,fx肯定是整型            setTimeout(function(){                linked.positive.shift();                linked.run = heartbeat( node);            },fx)            return (fx.run = false)        }        if (!fx) { //这里应该用正向列队的长度做判定            linked.run = false;        } else {            var config = fx.config, props = fx.props;            if (fx.startTime) { // 如果已设置开始时间,说明动画已开始                now = +new Date;                switch(linked.stopCode){//如果此时调用了stop方法                    case 0:                        fx.render = $.noop;//中断当前动画,继续下一个动画                        break;                    case 1:                        fx.gotoEnd = true;//立即跳到最后一帧,继续下一个动画                        break;                    case 2:                        linked.positive  = linked.negative = [];//清空该元素的所有动画                        break;                    case 3:                        for(var ii=0, _fx; _fx= linked.positive[ii++]; ){                            _fx.gotoEnd = true;//立即完成该元素的所有动画                        }                        break;                }                delete linked.stopCode;                isEnd = fx.gotoEnd || (now >= fx.startTime + config.duration);                //node, 是否结束, 进度                fx.render(node, isEnd, (now - fx.startTime)/config.duration, $); // 处理渐变                if(fx.render === $.noop) { //立即开始下一个动画                    linked.positive.shift();                }else{                    if( (mix = config.frame ) && !isEnd ){                        mix.call(node, node, props, fx ) ;                    }                }                if (isEnd) {//如果动画结束,则做还原,倒带,跳出列队等相关操作                    if(config.method == "hide"){                        for(var i in config.orig){//还原为初始状态                            $.css( node, i, config.orig[i] )                        }                    }                    linked.positive.shift(); //去掉播放完的动画                    mix = config.after;                    mix &&  mix.call( node, node, fx.props, fx ) ;                    if (config.revert && linked.negative.length) {                        //开始倒带,将负向列队的动画加入播放列表中                        [].unshift.apply(linked.positive, linked.negative.reverse())                        linked.negative = []; // 清空负向列队                    }                    if (!linked.positive.length) {                        linked.run = false;                    }                }            } else { // 初始化动画                fx.render = fxBuilder(node, linked, props, config); // 生成补间动画函数                mix = config.before                mix && (mix.call( node, node, fx.props, fx ), config.before = 0);                $[ config.method ].call(node, node, props, fx );//供show, hide 方法调用                fx.startTime = now = +new Date;            }        }        return linked.run; // 调用 clearInterval方法,中止定时器    }    function visible(node) {        return  $.css(node, "display") !== 'none';    }    function fxBuilder( node, linked, props, config ){        var ret = "var style = node.style,t2d = {}, adapter = $.fxAdapter, _defaultTween = adapter._default.tween;",        revertConfig = $.Object.merge( {}, config ),        transfromChanged = 0,        revertProps = {};        var orig = config.orig = {}, parts, to, from, val, unit, easing, op, type        for(var name in props){            val = props[name] //取得结束值            if( val == null){                continue;            }            easing = config.easing;//公共缓动公式            type = $.fxAdapter.type(name);            from = $.fxAdapter[ type ].get(node,name);            //用于分解属性包中的样式或属性,变成可以计算的因子            if( val === "show" || (val === "toggle" && !visible(node))){                val = $._data(node,"old"+name) || from;                config.method = "show";                from = 0;            }else if(val === "hide" || val === "toggle" ){//hide                orig[name] =  $._data(node,"old"+name,from);                config.method = "hide";                val = 0;            }else if(typeof val === "object" && isFinite(val.length)){// array                parts = val;                val = parts[0];//取得第一个值                easing = parts[1] || easing;//取得第二个值或默认值            }            //开始分解结束值to            if(type != "color" ){//如果不是颜色,则需判定其有没有单位以及起止值单位不一致的情况                from = from == "auto" ? 0 : parseFloat(from)//确保from为数字                if( (parts = rfxnum.exec( val )) ){                    to = parseFloat( parts[2] ),//确保to为数字                    unit = $.cssNumber[ name ] ? "" : (parts[3] || "px");                    if(parts[1]){                        op = parts[1].charAt(0);                        if (unit && unit !== "px" && (op == "+" || op == "-")  ) {                            $.css(node, name, (to || 1) + unit);                            from = ((to || 1) / parseFloat($.css(node,name))) * from;                            $.css( node, name, from + unit);                        }                        if(op){//处理+=,-= \= *=                            to = eval(from+op+to);                        }                    }                    var change = to - from;                }else{                    continue;                }            }else{                from = color2array(from);                to   = color2array(val);                change = to.map(function(end,i){                    return end - from[i]                });            }            if(from +"" === to +"" ){//不处理初止值都一样的样式与属性                continue;            }            var hash = {                name: name,                to: to,                from: from ,                change: change,                type: type,                easing: easing,                unit: unit            };            switch( type ){                case "_default":                    if(name == "opacity" && !$.support.cssOpacity){                        ret += $.format('$.css(node,"opacity", (isEnd ? #{to} : _defaultTween(#{from},#{change},"#{easing}", per )));;', hash);                    }else{                        ret += $.format('style.#{name} = ((isEnd ? #{to} : _defaultTween(#{from}, #{change},"#{easing}",per )))+"#{unit}";', hash);                    }                    break;                case "scroll":                    ret += $.format('node.#{name} = (isEnd ? #{to}: _defaultTween(#{from}, #{change},"#{easing}",per ));',hash);                    break;                case "color":                    ret += $.format('style.#{name} = (isEnd ? "rgb(#{to})" : adapter.#{type}.tween(#{from}, #{change},"#{easing}",per));', hash);                    break;                case "transform":                    transfromChanged++                    ret +=  $.format('t2d.#{name} = [#{from},#{to}, #{change},"#{easing}"];',hash);                    break            }            if(type == "color"){                from = "rgb("+from.join(",")+")"            }            revertProps[ name ] = [ from , easing ];        }               if( transfromChanged ){            ret += 'adapter.transform.set(node, t2d, isEnd, per);'        }        if ( config.record || config.revert ) {            delete revertConfig.record;            delete revertConfig.revert;            linked.negative.push({                startTime: 0,                reverting: 1,//标识正在倒带                config: revertConfig,                props: revertProps            });        }        //生成补间函数        return Function( "node,isEnd,per,$",ret );    }    $.easing = {        linear: function( pos ) {            return pos;        },        swing: function( pos ) {            return (-Math.cos(pos*Math.PI)/2) + 0.5;        }    }    var colorMap = {        "black":[0,0,0],        "silver":[192,192,192],        "gray":[128,128,128],        "white":[255,255,255],        "maroon":[128,0,0],        "red":[255,0,0],        "purple":[128,0,128],        "fuchsia":[255,0,255],        "green":[0,128,0],        "lime":[0,255,0],        "olive":[128,128,0],        "yellow":[255,255,0],        "navy":[0,0,128],        "blue":[0,0,255],        "teal":[0,128,128],        "aqua":[0,255,255]    };    var sandbox,sandboxDoc;    function callSandbox(parent,callback){        if ( !sandbox ) {            sandbox = document.createElement( "iframe" );            sandbox.frameBorder = sandbox.width = sandbox.height = 0;        }        parent.appendChild(sandbox);        if ( !sandboxDoc || !sandbox.createElement ) {            sandboxDoc = ( sandbox.contentWindow || sandbox.contentDocument ).document;            sandboxDoc.write( ( document.compatMode === "CSS1Compat" ? "" : "" ) + "" );            sandboxDoc.close();        }        callback(sandboxDoc);        parent.removeChild(sandbox);    }    function parseColor(color) {        var value;        callSandbox( $.html, function(doc){            var range = doc.body.createTextRange();            doc.body.style.color = color;            value = range.queryCommandValue("ForeColor");        });        return [value & 0xff, (value & 0xff00) >> 8,  (value & 0xff0000) >> 16];    }    function color2array(val) {//将字符串变成数组        var color = val.toLowerCase(),ret = [];        if (colorMap[color]) {            return colorMap[color];        }        if (color.indexOf("rgb") == 0) {            var match = color.match(/(\d+%?)/g),            factor = match[0].indexOf("%") !== -1 ? 2.55 : 1            return (colorMap[color] = [ parseInt(match[0]) * factor , parseInt(match[1]) * factor, parseInt(match[2]) * factor ]);        } else if (color.charAt(0) == '#') {            if (color.length == 4)                color = color.replace(/([^#])/g, '$1$1');            color.replace(/\w{2}/g,function(a){                ret.push( parseInt(a, 16))            });            return (colorMap[color] = ret);        }        if(window.VBArray){            return (colorMap[color] = parseColor(color));        }        return colorMap.white;    }    var cacheDisplay = $.oneObject("a,abbr,b,span,strong,em,font,i,img,kbd","inline");    var blocks = $.oneObject("div,h1,h2,h3,h4,h5,h6,section,p","block");    $.mix(cacheDisplay ,blocks);    function parseDisplay( nodeName ) {        if ( !cacheDisplay[ nodeName ] ) {            var body = document.body, elem = document.createElement(nodeName);            body.appendChild(elem)            var display = $.css( elem, "display" );            body.removeChild(elem);            // 先尝试连结到当前DOM树去取,但如果此元素的默认样式被污染了,就使用iframe去取            if ( display === "none" || display === "" ) {                callSandbox(body, function(doc){                    elem = doc.createElement( nodeName );                    doc.body.appendChild( elem );                    display = $.css( elem, "display" );                });            }            cacheDisplay[ nodeName ] = display;        }        return cacheDisplay[ nodeName ];    }    //show 开始时计算其width1 height1 保存原来的width height display改为inline-block或block overflow处理 赋值(width1,height1)    //hide 保存原来的width height 赋值为(0,0) overflow处理 结束时display改为none;    //toggle 开始时判定其是否隐藏,使用再决定使用何种策略    $.mix( $, {        fx:  function ( nodes, duration, hash, effects ){            nodes = nodes.mass ? nodes : $(nodes);            var props =  hash ||  duration ;            props = typeof props === "object" ? props : {}            if(typeof duration === "function"){// fx(obj fn)                hash = duration;               // fx(obj, 500, fn)                duration = 500;            }            if(typeof hash === "function"){   //  fx(obj, num, fn)                props.after = hash;           //  fx(obj, num, {after: fn})            }            if(effects){                for(var i in effects){                    if(typeof effects[i] === "function"){                        var old = props[i];                        props[i] = function(node, props, fx ){                            effects[i].call(node, node, props, fx);                            if(typeof old === "function"){                                old.call(node, node, props, fx);                            }                        }                    }else{                        props[i] = effects[i]                    }                }            }            return nodes.fx(duration, props);        },        show: function(node, props){            if(node.nodeType == 1 && !visible(node)) {                var old =  $._data(node, "olddisplay"),                _default = parseDisplay(node.nodeName),                display = node.style.display = (old || _default);                $._data(node, "olddisplay", display);                //  node.style.visibility = "visible";                if(props && ("width" in props || "height" in props)){//如果是缩放操作                    //修正内联元素的display为inline-block,以让其可以进行width/height的动画渐变                    if ( display === "inline" && $.css( node, "float" ) === "none" ) {                        if ( !$.support.inlineBlockNeedsLayout ) {//w3c                            node.style.display = "inline-block";                        } else {//IE                            if ( _default === "inline" ) {                                node.style.display = "inline-block";                            }else {                                node.style.display = "inline";                                node.style.zoom = 1;                            }                        }                    }                }            }        },        hide: function(node, props, fx){            if(node.nodeType == 1 && visible(node)){                var config = fx && fx.config;                var display = $.css( node, "display" );                if ( display !== "none" && !$._data( node, "olddisplay" ) ) {                    $._data( node, "olddisplay", display );                }                if( config ){//缩小                    if("width" in props || "height" in props){//如果是缩放操作                        //确保内容不会溢出,记录原来的overflow属性,因为IE在改变overflowX与overflowY时,overflow不会发生改变                        config.overflow = [ node.style.overflow, node.style.overflowX, node.style.overflowY ];                        node.style.overflow = "hidden";                    }                    var after = config.after;                    config.after = function( node, fx, props ){                        node.style.display = "none";                        //   node.style.visibility = "hidden";                        if ( config.overflow != null && !$.support.keepSize  ) {                            [ "", "X", "Y" ].forEach(function (postfix,index) {                                node.style[ "overflow" + postfix ] = config.overflow[index]                            });                        }                        if(typeof after == "function"){                            after.call( node, node, props, fx );                        }                    };                }else{                    node.style.display = "none";                }            }        },         toggle: function( node ){            $[ visible(node) ? "hide" : "show" ]( node );        }    });    //如果clearQueue为true,是否清空列队    //如果jumpToEnd为true,是否跳到此动画最后一帧    $.fn.stop = function( clearQueue, jumpToEnd ){        clearQueue = clearQueue ? "1" : ""        jumpToEnd = jumpToEnd ? "1" : "0"        var stopCode = parseInt( clearQueue+jumpToEnd,2 );//返回0 1 2 3        return this.each(function(node){            var linked = $._data( node,"fx");            if(linked && linked.run){                linked.stopCode = stopCode;            }        });    }    // 0 1    $.fn.delay = function(ms){        return this.each(function(node){            var linked = $._data(node,"fx") || $._data( node,"fx",{                positive:[], //正向列队                negative:  [], //负向列队                run: false //            });            linked.positive.push(ms);        });    }    var fxAttrs = [    [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],    [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],    ["opacity"]    ]    function genFx( type, num ) {//生成属性包        var obj = {};        fxAttrs.concat.apply([], fxAttrs.slice(0,num)).forEach(function(name) {            obj[ name ] = type;        });        return obj;    }    var effects = {        slideDown: genFx( "show", 1 ),        slideUp: genFx( "hide", 1 ),        slideToggle: genFx( "toggle", 1 ),        fadeIn: {            opacity: "show"        },        fadeOut: {            opacity: "hide"        },        fadeToggle: {            opacity: "toggle"        }    }    Object.keys(effects).forEach(function( method ){        $.fn[ method ] = function( duration, hash ){            return $.fx( this, duration, hash, effects[method] );        }    });      "show,hide".replace( $.rword, function( method ){        $.fn[ method ] = function(duration, hash){            if(!arguments.length){                return this.each(function(){                    $[ method ]( this );                })            }else{                             return $.fx( this, duration, hash, genFx( method , 3) );            }        }    })    var _toggle = $.fn.toggle;    $.fn.toggle = function(duration,hash){        if(!arguments.length){            return this.each(function(node) {                $.toggle( node );            });        }else if(typeof duration === "function" && typeof duration === "function" ){            return _toggle.apply(this,arguments)        }else{            return $.fx(this, duration, hash, genFx("toggle", 3));        }    }    function beforePuff( node, props, fx ) {        var position = $.css(node,"position"),        width = $.css(node,"width"),        height = $.css(node,"height"),        left = $.css(node,"left"),        top = $.css(node,"top");        node.style.position = "relative";        $.mix(props, {            width: "*=1.5",            height: "*=1.5",            opacity: "hide",            left: "-=" + parseInt(width) * 0.25,            top: "-=" + parseInt(height) * 0.25        });        var after = fx.config.after;        fx.config.after = function( node, props, fx ){            node.style.position = position;            node.style.width = width;            node.style.height = height;            node.style.left = left;            node.style.top = top;            if(typeof after === "function"){                after.call( node, node, props, fx );            }        }    }    //扩大1.5倍并淡去    $.fn.puff = function(duration, hash) {        return $.fx( this, duration, hash, {            before:beforePuff        });    }});//2011.10.10 改进$.fn.stop//2011.10.20 改进所有特效函数,让传参更加灵活//2011.10.21 改进内部的normalizer函数//2012.2.19 normalizer暴露为$.fx 改进绑定回调的机制

转载地址:http://evsda.baihongyu.com/

你可能感兴趣的文章
玩转小程序转发——小程序探索
查看>>
【基础】小程序实现聊天气泡样式
查看>>
Docker入门(三)使用Docker Compose
查看>>
CDN知识详解
查看>>
Python爬虫:学了requests库和re库之后能做的事情
查看>>
天下无难试之HashMap面试刁难大全
查看>>
IP地址自动封与解封的shell脚本
查看>>
ubuntu 系统环境配置文件的区别
查看>>
精通visual c++指纹模式识别系统算法及实现
查看>>
博客园自定义页面风格设计 后续篇(页面设计模式及代码高亮 鼠标点击效果升级)...
查看>>
[知识盲点] 为function添加值为function的属性
查看>>
Emacs for Windows use TRAMP
查看>>
ssh登录的调试方法和常见问题
查看>>
PHP 7.3 比 PHP 7.0 快 22%,即将进入特性冻结阶段
查看>>
Java 9 文章集锦
查看>>
案例丨数据驱动的发现页最低成本改版
查看>>
普通用户竟这样执行xp_cmdshell存储过程!
查看>>
Visualize Famous Campus in China
查看>>
C#给PDF文档添加文本和图片页眉
查看>>
为Exchange Server 2010服务器申请证书
查看>>