一個輪顯插件的嘗試、思考和擴展

"解決一個問題,最重要的收穫並非獲得的答案。而是在尋找答案的過程當中,學到的其它東西和見識的延伸。"javascript

---《反正我從中學到很多東西》css


」我敬你是條漢子!「html

---《論如何回答女友問爲何對她那麼好》java


但願可以把文章大體掃一遍,裏面有一些有趣的demo哦......噠


 

css3

          //---------------初始化輪顯的方法-----------------------
          $("#modelSlider").slider({
            imgs: [  //圖片的地址
                "http://images.cnblogs.com/cnblogs_com/lianmin/713650/o_0f8738e9cfbb9485.png",
                "http://images.cnblogs.com/cnblogs_com/lianmin/713650/o_89e657a08f9f13f6.png",
                "http://images.cnblogs.com/cnblogs_com/lianmin/713650/o_884e69c2eb02316b.png",
                "http://images.cnblogs.com/cnblogs_com/lianmin/713650/o_462506e0ed7b0c25.png",
                "http://images.cnblogs.com/cnblogs_com/lianmin/713650/o_e5859ff3e6487575.png",
                "http://images.cnblogs.com/cnblogs_com/lianmin/713650/o_f07bd295f4cdbd7a.png"
            ],
            urls: [  //點擊圖片跳轉到的地址,也能夠以下放一段js
                'javascript:makeDialog("輪顯提示或者地址","第1個圖",function(){},3)',
                'javascript:makeDialog("輪顯提示或者地址","第2個圖",function(){},3)',
                'javascript:makeDialog("輪顯提示或者地址","第3個圖",function(){},3)',
                'javascript:makeDialog("輪顯提示或者地址","第4個圖",function(){},3)',
                'javascript:makeDialog("輪顯提示或者地址","第5個圖",function(){},3)',
                'javascript:makeDialog("輪顯提示或者地址","第6個圖",function(){},3)'
            ],
            scale: 5 / 2,   //圖片寬高比
            border: false,   //是否顯示分界線
            showBar: true,    //是否能夠人工切換
            x: 4,      //橫向格子數
            y: 3       //縱向格子數
        });

        //---------------控制輪顯的方法-----------------------
        $("#modelSlider").slider("begin");  //開始            執行某個方法
        $("#modelSlider").slider("stop");    //中止

        $("#modelSlider").slider("choseImg",3); //切換到索引爲3的項      執行某個方法,傳參

        $("#modelSlider").slider("resize",{border:true,showBar:false}); //對初始化的屬性進行修改     

        //-----------試試把上面這4行依次放到控制檯執行一下?------

1、照着效果分析原理

最初是在一個叫《琉璃神社》的地方看到:http://hacg.club/。觀察了不少次,以爲重點在這裏:格子內的效果切換
因而就扒開看看嘍...ajax

 

觀察了整個輪顯好久以後,我初步得出如下幾個想法。
  • 總效果 => 格子特效 + 效果產生順序
  • 格子 => 2個div + 經過背景圖切換
  • 經過背景圖切換 || 效果產生順序 => 能夠用jQuery的隊列處理Σ( ゚д゚))
  • 具體的邏輯算法 => 邊寫邊想吧,寫完重構
  • ......

 

2、搭建jQuery插件結構

我以爲應該寫成插件方式,畢竟要多處用。如下是我寫插件通常的格式,歡迎拍磚:正則表達式

    // jQuery 插件的通常寫法,自執行匿名方法
    // 好處是這樣的:1.避免其它插件也用了$作關鍵字;2.避免插件內部方法污染全局
    // 實際工做中我一個js文件也許會寫大幾十個function,,,沒辦法,需求是一點一點加
    (function ($) {
        function Slider(option) {
            //深拷貝,修改每一個對象的屬性只能經過對象實例,避免初始化時候外部引用對象的影響
            this.opt = $.extend(true, {}, option);
        }

        $.fn.slider = function (option) {
            var defaults = {  //默認設置
                // 獨立寫出來,也許未來就用獲得呢,也說不定...
            };

            //最終配置
            var opt = $.extend({}, defaults, option);


            //jQuery對象是一個僞數組對象,可能有多個元素
            return $.each(this, function (index, ele) {
                // code...
                var slider = new Slider(opt);
                $(ele).data("slider", slider);
            });
        };
    })(jQuery);

總結:

  • 最基本的面向對象思想,寫個類吧,在類中實現全部功能.
  • 對jQuery.fn進行擴展,能夠用 $.fn.method = function ,或者 $.fn.extend({}) .
  • jQuery對象是一個僞數組對象,裏面可能含有多個元素,須要針對每個元素實例化一個對象進行緩存.
  • 說到data方法,js對象的 $({}).data ,是把數據放到對象自己上,而元素的 data 方法,是把數據放到 $.cache 中 。因此如何跨框架使用easyui等報錯的解決方式,是須要用所屬iframe內的jQuery對象,不然取不到。
  • 將全部代碼放到匿名自執行方法中.

 

3、對將要用到的幾個主要方法方法進行擴展

1.確定要拼接html的,"<div id='"+theId+"' >....... 這種寫法簡直弱爆了,之前也常常這麼寫,可是某天就忽然擴展了,,,果真多讀讀別人的文章對本身有好處.算法

    //擴展 string.format
    String.prototype.format = function () {
        var args = arguments;
        var reg = /\{(\d+)\}/g;
        return this.replace(reg, function (g0, g1) {
            return args[+g1];
        });
    };
    //用法:
    "hello {0},your age is {1},so {0}'s age is {1}".format("tom",12);
    //"hello tom,your age is 12,so tom's age is 12"

說明:

  • 對String原型進行擴展: String.prototype.methodName=function...
  • 正則表達式: /\{(\d+)\}/g ;取"{0}"這種格式的佔位符,並對裏面的數字放入子組
  • js 的 replace 方法有一種重載, string.format(regex , function(group0【匹配項】,group1【子組第一個】...){  //code...  }) ;對於每次匹配到的一個佔位符,都從參數相應的位置取得替換項。



2.圖片預加載 編程

    (function ($) {  // 圖片預加載
        $.preLoad = function (urlArr) {
            $.each(urlArr, function (index, url) {
                var img = document.createElement("img");
                img.src = url;
            });
        }
    })(jQuery);
    //上來就加載大量圖片會佔用大量帶寬,影響用戶體驗
    //可是若是輪顯這裏不提早加載,只在顯示當前圖片的時候去下載當前圖片,對於通常網速的用戶來講,可能不太好看
    //根據實際狀況使用吧...噠?



3.jQuery隊列封裝 api

關於jQuery隊列的一點認識:

衆所周知,使用jQuery給元素添加一連串的動畫效果,元素並不會將動畫同時執行,而是按照添加的順序,依次在上一個動畫結束以後纔開始下一個動畫。

我瞭解的狀況就是,jQuery使用了一種叫作「隊列」(queue)的方式將動畫效果依次加進去,在上一個隊列中的動畫執行完畢,經過deferred通知下一個動畫執行。

    var ele = $("#id"); //某個jQuery對象

    //爲jQuery對象的叫「queueName」的隊列上面添加處理事件
    ele.queue("queueName", function (next) {
        //your code...  do something
        next(); //next() 是執行下一個隊列中要處理的事件,若是沒有next隊列就沒法依次處理
    });

    //延時
    ele.delay(1000, "queueName");

    //執行隊列
    ele.dequeue("queueName");

這裏要說一下的是,jQuery的動畫默認是把處理事件放在了叫「fx」的隊列中。所以,我進行了如下簡單的封裝:

    $.fn.will = function (callback, type) {
        //這裏的this,表示jQuery對象
        this.queue(type || "fx", function (next) {  // fx 表示默認的隊列
            //這裏的this,是原生的對象
            callback && typeof callback == "function" && callback.call($(this)); //使用call,方便回調函數使用this
            next();
        });
        return this; //返回this,方便進行鏈式調用
    }

    //試試在控制檯這麼用
    var ele = $({});
    for (var i = 0; i < 10; i++) {
        ele.will(function () {
            console.log(+new Date);
        }).delay(1000);
    }

使用隊列,能夠直觀清晰,方便地將異步操做表示出來。就像 Thread.Sleep(1000) 那樣明瞭。

關於Deferred我就不贅述了,本文沒有直接使用到,本身也只是知其然而不知其因此然,僅僅會用。

隊列的做用,就是將輪顯中的格子,一個一個進行處理,避免了大量的setTimeout,使用callback的形式進行替換。

4、緩動

先看例子,沒有效果圖吸引不了人



更詳細的緩動介紹請參見:http://www.cnblogs.com/cloudgamer/archive/2009/01/06/Tween.html

這裏我介紹一下jQuery.animate的一種重載

jQuery的animate動畫,我之前不知道在哪裏看到的:只能實現能夠用數字表示的動畫。也就是說css3的transform是不行的。可是animate有一種重載!

經常使用的方式

$("html,body").animate({ scrollTop: "0px" }, 1000);

另外一種重載

    $({ num: 32 }).animate({ num: 64 }, {
        duration:1000,
        step: function () {
            console.log("當前的num是:" + this.num);
        },
        complete: function () {
            console.log("結束了,num是:" + this.num);
        }
    });


看到上面的寫法,就應該大體瞭解了demo中動畫的原理:

【根據要改變的樣式定義一個對象,利用animate改變這個對象,監聽step和complete事件來拼接新的樣式賦值給你要執行動畫的元素!】

如下是【發射憤怒的小鳥】的具體實現

    jQuery.extend(jQuery.easing, {
        easeOutBounce: function (x, t, b, c, d) {
            if ((t /= d) < (1 / 2.75)) {
                return c * (7.5625 * t * t) + b;
            } else if (t < (2 / 2.75)) {
                return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
            } else if (t < (2.5 / 2.75)) {
                return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
            } else {
                return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
            }
        },easeOutCubic: function (x, t, b, c, d) {
           return c * ((t = t / d - 1) * t * t + 1) + b;
       }
    })

    function sendBirld() {
        var ele = $("#birld").stop(true, false);
        ele.css({ "left": "0", "top": "0", "transform": "rotateZ(0deg)" });
        $({ left: 0, top: 0, tran: 0 }).animate({
            left: 800, top: 180, tran: 360
        }, {
            duration: 2500,
            specialEasing: {
                left: 'easeOutCubic',
                top: 'easeOutBounce'
            },
            step: function () {
                ele.css({ "left": this.left + "px", "top": this.top + "px", "transform": "rotateZ(" + this.tran + "deg)" });
            },
            complete: function () {
                ele.css({ "left": "800px", "top": "180px", "transform": "rotateZ(360deg)" });
            }
        });
    }

5、對js異步編程的思考學習

     我目前瞭解到3種js異步的方式:setTimeout,setInterval,回調函數,事件觸發

setTimeout和setInterval太醜陋了...雖然大部分偏差能夠接受,可是總歸併非賞心悅目。並且有些東西並不清楚會執行多久,因此捨棄。

事件觸發,讓我想到了 C# 中的事件與委託,確實不錯,可是一長串任務要定義多少事件?感受沉重。 這一塊瞭解的不深,可能說的不正確。

我偏向於第二種回調。

    function work(callback) {
        //do something...
        typeof callback == "function" && callback();
    }

可是天啊,若是一長串方法須要依次執行,這回調要有多難看?

咱們看看jQuery是怎麼作的(只說用法,實現過程沒研究):

jQuery.Deferred()的一點了解

$.Deferred()是jQuery1.5開始加進去的,並重寫了 ajax和animate(基於queue,queue基於Deferred)

使用示例:

    function work() {
        var dfd = $.Deferred();
        setTimeout(function () {
            //do something...
            dfd.resolve();
        },1000);

        return dfd;
    }
    work().done(function () {
        window.console && console.log("結束了");
    });

使用方式:1.建立deferred對象;2.爲該對象暴漏的事件綁定方法;3.對象執行動做,觸發綁定的方法

對應的動做和事件(我目前經常使用的):

  • deferred.resolve() => deferred.done(callback) //完成
  • deferred.reject() => deferred.fail(callback) //失敗
  • deferred.then(cb1,cb2) //cb1完成,cb2失敗,固然有cb3,這個沒作具體瞭解
  • deferred.always(callback) //不管完成或者失敗
  • jQuery.when(dfd1,dfd2...)的用法:
        //參數是若干個deferred或者promise對象
        $.when(dfd1, dfd2, dfd3...).done(function () {
            //全都完成時候觸發
        }).fail(function () {
            //有一個失敗則斷定所有失敗
        }).always(function () {
            //全都是完成或者失敗狀態時候觸發
        });
    
  • //注:動做中的參數,會自動帶入到事件回調的參數位置,如:
        var dfd = $.Deferred();
        dfd.done(function (str) {
            console.log(str);
        });
        dfd.resolve("lalala"); //會打印出 "lalala"
    
  • ......

相似於C#中的委託與事件:委託的發佈者不該該將委託的操控權暴漏給訂閱者,最好用事件對委託進行安全的封裝。 直接返回deferred彷佛也不太好,對deferred仍然能夠操做,因此封裝一下:返回deferred.promise(),該對象只暴漏了事件的訂閱方法,而不能操做。

看看 jQuery.ajax

傳統的jQuery.ajax

    $.ajax({
        //...
        success: function () {
            //...
        },
        error: function () {
            //...
        },
        complete: function () {
            //...
        }
    });

ajax方法返回的仍然是一個promise對象,提供事件訂閱的方法:done,fail,then...爲了與原方法名對應,看下圖,也額外提供了原來的名稱:

另外一種用法

    $.ajax({
        //...
    }).success(function (data) {
        //原success 或使用 done
    }).error(function () {
        //原error 或使用 fail
    }).complete(function () {
        //原complete 或相似用使用 always
    });

這時候我想了想,我要在函數裏面返回deferred對象麼?有點麻煩...

忽然想到jQuery的動畫添加進去就會依次執行。因而瞭解到動畫是基於隊列的:

6、總結,下載

總結:

本文不少地方介紹的可能都不是很詳細,畢竟我以一個「過來人」的思考方式,對於初次接觸人的想法不匹配,相信那些已經掌握的人來講就是:「對啊,就這樣子啊。」,初接觸:「啥?你到底想說啥?」

彷佛有點倉促,可是我以爲了解以上內容就已經夠了。且不行,就當作目錄看,對於各個知識點再去找專門的文章進行深刻學習。

按照最開始的分析,已經解決了:按順序依次延時處理(queue隊列,js異步),jQuery插件的寫法,格子切換時的緩動處理,其中間雜着一些在學習過程當中的思考和擴展(我以爲在尋求答案中的擴展很重要)。 剩下的大概就是具體實現了...無非就是用絕對定位,在切換的時候使用緩動作特效,用queue來進行異步處理,須要一些想法,簡單的算法,html和css基礎。

下面附上下載地址:slider1.2.js ,slider.min.css

我在製做這個插件的過程當中,鞏固和學習了很多知識,實現一個功能,重要的不只僅是實現,更多的是在實現過程當中的自我擴展和見識的延伸。

忽然想到了一個廣告詞:人生就是一部旅行,重要的不是目的地,而是沿途的風景,以及看風景的心情。

下面是厚顏無恥的求贊時間

您有沒有對這篇文章感興趣呢?

.

相關文章
相關標籤/搜索