
前言
這個小項目(卡片秀)是一個卡片抽獎特效插件,用開源項目這樣的詞語讓我多少有些羞愧,畢竟作為一個涉世未深的小伙子,用項目的標准衡量還有很大差距。不過該案例采用 jQuery 插件方式編寫,提供配置參數並且做了瀏覽器兼容優化,整體而言作為一個小項目也不為過。目前正在持續更新。
當然,博主寫這篇文章不是為了炫耀這個 Demo,而是交流 jQuery 插件的編寫以及這一項目中遇到的各種問題。現在的插件還有很多 bug 以及不完善的地方包括一些代碼的冗余,后期會進行更細致的拆分組裝等。
首先說一下這個項目的起因,博主最近接到了公司安排的一個抽獎頁面,因為時間倉促以及其它原因,最后簡單實現了功能並且添加了一些動畫效果。但是最初看到設計圖以卡片形式展示用戶數據的時候,我就想到了能否做的稍微炫酷一點,隨后便一直在構思。比如卡片的飛出、隨機排列、自動及手動抽取以及翻轉顯示等效果。事實證明,把想法變現實會遇到很多的問題。大家可以點擊 CardShow 查看自動抽卡的效果。目前的效果基本實現了我當初的構思。卡片的抽取效果主要分為自動抽及手動抽兩種。后期會添加卡片拖動的功能。我希望大家能夠下載源碼修改參數來查看效果,並提出寶貴意見,以便博主可以及時作出修改,大家的支持就是我前進的最大動力。
以下是我在寫插件時遇到的問題以及解決的問題,大概包括 jQuery 插件編寫、modernizr 使用、css3 動畫、transitionend 事件、洗牌算法、相鄰不重復隨機數、獲取 transform 的值、call() 的深入理解、setTimeout 的堵塞等等,每一塊拿出來都可以單獨寫一篇文章,這篇文章只是簡單介紹,之后也會就某一部分做深入探討。
jQuery 插件的編寫
話說很多事情看着簡單,做起來很難。如果不理解原生 js 的對象、函數、原型、作用域以及 jQuery 的核心思想及方法,想寫一個插件可能真的非常困難。對於 jQuery 插件的編寫我就不多說了,網上一搜很多,比我寫的好的更多,我只貼一下自己寫的插件的結構。對於代碼的結構我思考了很久也研究了很久,最后借鑒了一些案例,感覺以下結構更清晰,更簡潔,更易懂。
;(function(window, $) { // 插件主體 $.plugin = function(el,options){ } // 默認配置 $.plugin.defaults = { } // 原型方法 $.plugin.prototype = { } // 設置 jQuery 插件 $.fn.plugin = function(options) { return instance; } })(window, jQuery);
Modernizr 的使用
modernizr 也算是一個老牌的瀏覽器兼容方案了,相信大家也早已使用或者早有耳聞。之前雖然早就知道這個小東西,應該是接觸 bootstrap 時了解的,但一直未有機會使用,直到現在寫插件才發現,用它檢測 css3 的屬性並做兼容方案真的是爽歪歪。網上關於 modernizr 的文章並不多,這是官網文檔:https://modernizr.com/docs ,已經說得很詳細,我之后會翻譯該文。
相鄰不重復隨機數
解決這個問題多少讓我有一些成就感,雖然還不是很完美。這個問題簡單說就是:寫一個函數,使其可以持續輸出隨機數,而相鄰位置的隨機數不相同。對於這個問題我沒有搜索到答案,搜到最多的是產生不重復的隨機數。這完全是兩個問題,這個問題看起來不難,無非定義對比變量,但問題就在於怎么對比,怎么寫函數。之前看到有人說“算法就像窗戶紙”,現在深有體會。解決這個問題多少有些運氣的成分,想了很久,最后隨手一寫,竟然成功了。可能這個問題本身真的不難。我建議大家先不要展開代碼,自己寫一個函數,用 setInterval 持續輸出隨機數,能否做到相鄰不重復。也希望大家給我一個更完美的方案,歡迎留言。
// 產生相鄰不重復的隨機數,n 為隨機數個數 // 定義比較變量,能否將其封裝在函數內? var b = 0; function random(n) { var a = Math.floor(Math.random() * n); if (a == b) { return random(n); } else { b = a; return b; } };
該問題已經得到解決,具體請參考相鄰不重復隨機數的生成及優化
洗牌算法
洗牌算法的原始方法由 Ronald Fisher 和 Frank Yates 提出,網上可以搜到很多,以下是常見的 JS 方法:
// 數組隨機變換函數 function shuffleArr(array) { var m = array.length, t, i; // While there remain elements to shuffle… while (m) { // Pick a remaining element… i = Math.floor(Math.random() * m--); // And swap it with the current element. t = array[m]; array[m] = array[i]; array[i] = t; } return array; };
如何獲取 transform 的值
因為動畫以 transition 為主,所以要持續操作元素的 transform 的值。但是 transform 是一個復合值,取出特定數值有一定困難。我的解決辦法簡單粗暴。我在控制台打印 transform 的值得時候發現是一個 matrix 的東西。關於 transform 的矩陣知識大家自行搜索。其實就是一個字符串,只要 split() 方法取出特定值即可。但是這里面有個瀏覽器兼容的問題。眾所周知,transform2d 是 3X3 矩陣,而 transform3d 是 4X4 矩陣,如果使用 transform3d 屬性而沒有給出第三個值,在火狐及谷歌瀏覽器會輸出 3X3 矩陣,而在 IE 及 Edge 輸出 4X4 矩陣。
Transitionend 事件
transitionend 事件是在 transition 動畫結束之后執行的函數。說到 transitionend 事件,我有很多感想。首先大家可以看一下我的 github 中的兩個關於慕課網仿寫 https://github.com/codrops/ScatteredPolaroidsGallery 的 DEMO,當卡片翻轉過來,然后直接移動卡片,這時卡片是邊移動邊翻轉,效果不理想。當初學這個 DEMO 的時候就研究了很久,沒找到答案,直到現在也沒做修改。直到寫此插件才發現一切優化都要基於 transitionend 事件。
Chrome 中的 onresize 事件
關於該問題的詳情及解決方法請參考 這篇文章 !
本文持續更新~
