Dom元素變換效果(工具函數)
2019-11-21 00:11:53
供稿:網友
用js來控制行為,控制頁面表現。恐怕是我們用它來做的最多的事情了... jQuery為什么這么受歡迎,我想,一方面是它的選擇器[selector]足夠牛B,另一方面應該要歸功于它在dom變換上做足了功夫。 于是,秉承著一個宅男的宗旨,周末花了兩個晚上稍微總結了一下,寫了一段控制dom元素變換的代碼,與目前強大的框架比起來,確實顯得有點拙劣,不過作為日常開發的工具函數,我想還是蠻實用的。(這也是我最初的目的 ^_^)。 開門見山,先把源碼完整的貼出來:(本著分享學習的原則,源代碼你可以任意修改)
//////////////* = Hongru.anim.js =*/////////// // @author: hongru.chen // // @date: 2010-10-17 // //////////////////////////////////////////// var Hongru = { get : function (id) {return document.getElementById(id)}, tween : { linear : function (f, t, d) { return t * d + f; }, ease : function (f, t, d) { return -t * .5 * (Math.cos(Math.PI * d) - 1) + f; } }, tweens: [], tweensCnt: 0, anim : function (obj, params) { var self = this, objList = [], objReturn = []; var animimg = function () { var i = -1, o; while( o = self.tweens[++i] ) { var cTime = (new Date()).getTime() - o.start; if (cTime < o.duration) { for (var k in o.val) { var m = o.val[k]; o.obj[k] = Math.ceil(o.ease(m.from, m.diff, cTime/o.duration)) + (o.cssMode ? 'px' : 0); } } else { cTime = o.duration; for (var k in o.val) { var m = o.val[k]; o.obj[k] = Math.ceil(o.ease(m.from, m.diff, cTime/o.duration)) + (o.cssMode ? 'px' : 0); } self.tweens.splice(i, 1); self.tweensCnt--; if (o.onFinish) o.onFinish (o.params); self.stop(); } if (!o.cssMode) o.obj.onTween(); } }; if (typeof obj == "string" || !obj.length) objList.push(obj); else objList = obj; for (var il = 0, l = objList.length; il < l; il++) { var obj = this.get(objList[il]) || objList[il]; var o = {}; o.val = {}; o.cssMode = true; o.o = obj; o.obj = obj.style; for (var k in params) { var p = params[k]; if (k === "cssMode") { if (p === false) { o.cssMode = false; o.obj = obj; } } else if (k === "onTween") { o.obj.onTween = p; } else if (k === "from") { for (var i in p) o.obj[i] = o.cssMode ? (Math.ceil(p[i]) + 'px') : p[i]; } else if (k === "to") { for (var i in p) { var from = o.cssMode ? (parseInt(o.obj[i]) || 0) : (o.obj[i] || 0); o.val[i] = { from: from, diff: p[i] - from }; } } else o[k] = params[k]; } if (!o.ease) o.ease = this.tween.ease; if (!o.cssMode && params['from']) o.obj.onTween(); o.duration = params.duration ? params.duration : 1000; o.start = (new Date()).getTime(); this.tweens.push(o); this.tweensCnt++; if (!this.running) this.running = window.setInterval(animimg, 10); objReturn.push(o); } return objReturn.length === 1 ? o : objReturn; }, stop : function () { if (!this.tweensCnt) { window.clearInterval(this.running); this.running = false; } }, kill : function (obj) { if (obj) { for (var i = 0; i < this.tweensCnt; i++) { if (this.tweens[i] === obj || this.tweens[i].o === obj || this.tweens[i].o === this.get(obj)) { this.tweensCnt--; this.tweens.splice(i, 1); this.stop(); } } } }, reset : function () { this.tweensCnt = 0; this.stop(); while( this.tweens.length ) { this.tweens.stop(); } } }
從調用方式來看,其實我的實現方法有點類似于YUI,以下是個最簡單的例子: Hongru.anim(id, { from: { top: 0, left: 100 }, to: { left: 500, } });
以下是調用這段代碼的效果: 當然,這是最簡單的方式,此外,我在參數id上,花了點小心思,anim()的第一個參數不僅可以是我們常見的字符串"id",也可以是HtmlObject,比如同樣是上面的代碼: function tween1 (id) { //此id參數可以是obj,也可以是字符串id,還可以是數組['id1','id2','id3'] Hongru.anim(id, { from: { top: 0, left: 100 }, to: { left: 500, } }); }
可以對一個元素這樣來調用 onclick="tween1(this)" this直接指向元素本身,演示如下。 [Ctrl+A 全選 注:如需引入外部Js需刷新才能執行 ] 另外,還可以控制多個元素同時變換,也就是說,id可以是個數組如['id1', 'id2', 'id3'].演示如下:
[Ctrl+A 全選 注:
如需引入外部Js需刷新才能執行 ]
以上可以支持object和數組selector的功能,暫可以稱為【功能一】,
【功能二】
支持回調函數callback,也就是可以實現咱們常說的chain-animate,鏈式變換。具體是通過onFinish參數來實現的。核心源碼很簡單,就是在一個變換完畢之后判斷有沒有onFinish,如果有就調用:
var animimg = function () { var i = -1, o; while( o = self.tweens[++i] ) { var cTime = (new Date()).getTime() - o.start; if (cTime < o.duration) { for (var k in o.val) { var m = o.val[k]; o.obj[k] = Math.ceil(o.ease(m.from, m.diff, cTime/o.duration)) + (o.cssMode ? 'px' : 0); } } else { cTime = o.duration; for (var k in o.val) { var m = o.val[k]; o.obj[k] = Math.ceil(o.ease(m.from, m.diff, cTime/o.duration)) + (o.cssMode ? 'px' : 0); } self.tweens.splice(i, 1); self.tweensCnt--; if (o.onFinish) o.onFinish (o.params); self.stop(); } if (!o.cssMode) o.obj.onTween(); } };
下面是通過遞歸調用實現chain-animate的一個演示:
到這里,相信以前做過類似工作的同學們都覺得這還不夠,畢竟如果只實現所有和‘盒模型'沾邊的屬性的變換應該都不算難事。比如高寬,padding,margin,left,top等等這些可以用像素做單位的屬性。他們的變換都可以統一起來。但是如果要實現顏色變換,透明度變換呢,恐怕就不是我的短短幾十百來行代碼就可以做到的。
是的,我承認,要把顏色,透明度等等所有屬性變換都封裝起來,需要做的判斷就不是一點兩點了。所以呢,在這里,我也沒有對“盒模型”以外的屬性變換做封裝。但并不代表沒有想到他們的實現方法。
既然寫死在代碼里要費事,那就我們自己調用的時候多寫兩句代碼不就好了嗎,畢竟代碼是死的,人是活的。我這里做了個cssMode的參數判斷(思路是借鑒一個老外的)。代碼成本很低。如果不是可以用px做單位的屬性,我們可以自己控制它的樣式變換。比如實現背景色變換:
function test3 (id) { Hongru.anim(id, { cssMode: false, // animation will be handled by an external function onTween : function () { // custom function, can be as simple or as complex as needed this.style.background = "RGB("+this.r+","+this.g+","this.b")"; }, from : { r: 0, g: 0, b: 0 }, to: { r: 255, g: 128, b: 100 } }); }
把cssMode設置為false之后,會執行onTween里的代碼,這樣就可以自行控制樣式的變換了,如上。演示如下:
同樣,透明度也可以自行設置,
好了,鑒于篇幅和時間,此文基本都在介紹功能,對代碼沒怎么細講,有興趣的同學可以自己看看,反正源碼也貼出來了,而且原理都不難。
另外,在這個函數使用中,Hongru.kill(id)是停止指定對象的變換。Hongru.reset()是停止所有正在變換的效果。至于Hongru本身,大家可以隨意更改。