摘要:動(dòng)畫(huà)庫(kù)學(xué)習(xí)筆記可以很方便的用做下拉刷新,抽獎(jiǎng)轉(zhuǎn)盤(pán)等效果,我一直很好奇他是如何工作的,尤其是它能完美模擬原生的平滑滾動(dòng)和慣性回彈等效果,而且體積小,速度快。當(dāng)軸逐漸增加到達(dá)時(shí),當(dāng)前值軸會(huì)到達(dá)目標(biāo)值。類(lèi)似的還有,屬性值的排序會(huì)造成影響。
AlloyTouch動(dòng)畫(huà)庫(kù)學(xué)習(xí)筆記
alloyTouch可以很方便的用做下拉刷新,抽獎(jiǎng)轉(zhuǎn)盤(pán)等效果,我一直很好奇他是如何工作的,尤其是它能完美模擬原生的平滑滾動(dòng)和慣性回彈等效果,而且體積小,速度快。
閱讀代碼前,我的思考拖拽的慣性效果實(shí)現(xiàn),看上去這種效果的原理很簡(jiǎn)單,但是真正實(shí)踐的時(shí)候還是有疑問(wèn):
怎么檢測(cè)到松開(kāi)鼠標(biāo)那一刻的速度(初速)呢?
假設(shè)我拖拽的中途突然停止,再松開(kāi),要怎么處理?
拖拽力度很大的情況如何處理?
如果慣性滾動(dòng)的移動(dòng)的距離超出邊界,回彈效果怎么做?
拖拽超出邊界的橡皮筋效果怎么做?
源代碼閱讀帶著這些疑問(wèn),我開(kāi)始了代碼閱讀之旅,下面的筆記沒(méi)有完完全全的講解整個(gè)框架,只是挑了我覺(jué)得(辣雞如我)容易疑惑的地方。
初始化this.isTouchStart = false;
這個(gè)變量是為了判斷用戶是否是從目標(biāo)DOM觸摸開(kāi)始,有可能是先在wrapper觸摸,再移動(dòng)至目標(biāo)DOM,如果是這種情況,不觸發(fā)滾動(dòng)。
bind(this.element, "touchstart", this._start.bind(this)); bind(this.eventTarget, "touchend", this._end.bind(this)); bind(this.eventTarget, "touchcancel", this._cancel.bind(this));
接下來(lái)重點(diǎn)就在于這3個(gè)函數(shù)了,初始化綁定DOM的事件。
touchstart對(duì)應(yīng)AlloyTouch.prototype._start。
_start: function (evt) { this.isTouchStart = true; this.touchStart.call(this, evt, this.target[this.property]); cancelAnimationFrame(this.tickID); this._calculateIndex(); this.startTime = new Date().getTime(); //起始時(shí)間 this.x1 = this.preX = evt.touches[0].pageX; this.y1 = this.preY = evt.touches[0].pageY; this.start = this.vertical ? this.preY : this.preX; //startPoint this._firstTouchMove = true; //這里才是判斷是否初次觸摸 this._preventMove = false; },touchmove
對(duì)應(yīng)AlloyTouch.prototype._move。
這里有段代碼一直讓我疑惑很久,為什么touchstart和touchmove間隔大于300ms時(shí),startTime和start(startPoint)要重新設(shè)置呢?
按我的理解,為了方便檢測(cè)速度,當(dāng)此次touchmove事件觸發(fā)時(shí)間比startTime大于300ms時(shí),重新設(shè)定計(jì)算速度的startPoint,這樣可以在拖拽軌跡中截取合理的起止長(zhǎng)度和時(shí)間間隔,計(jì)算初速,一般拖拽過(guò)程有以下3中情況:
假設(shè)拖拽的時(shí)長(zhǎng)小于300ms,startPoint則用touchstart時(shí)設(shè)置的,
假設(shè)拖拽時(shí)長(zhǎng)大于300ms,startPoint用滿足條件的新touchmove點(diǎn)。
拖拽中途停止,不產(chǎn)生慣性效果(一般情況下,鼠標(biāo)停止的時(shí)間會(huì)大于300ms)
這里我有個(gè)疑問(wèn),為什么不直接用最后一個(gè)touchmove點(diǎn)作為startPoint呢?
我的理解是,最后觸發(fā)的touchmove事件和touchend事件間隔時(shí)間很短,雖然間隔時(shí)間(dt)可以取得的精度很高,但是,移動(dòng)的距離差(dv)的單位是px,假設(shè)物體移動(dòng)了1.999px,最后瀏覽器還是按1px計(jì)算,在dt很小的的情況下,誤差就變大了。
對(duì)于問(wèn)題5:橡皮筋效果的實(shí)現(xiàn):拖拽時(shí),如果超出邊界,則增加移動(dòng)的阻力,即用outerFactor。
touchend對(duì)應(yīng)AlloyTouch.prototype._end。
var dt = new Date().getTime() - this.startTime; if (dt < 300) {...}
對(duì)于問(wèn)題4:判斷時(shí)間間隔是否小于300ms,如果大于,則判定是拖著不動(dòng),再松開(kāi),此時(shí)沒(méi)有慣性效果。
對(duì)于問(wèn)題3:慣性滾動(dòng)的距離destination超出邊界max且大于最大值maxRegion(默認(rèn)600px)時(shí),則慣性滾動(dòng)的最大距離為max + springMaxRegion(默認(rèn)60px),如下圖。
_to的實(shí)現(xiàn)alloyTouch內(nèi)部所有的動(dòng)畫(huà)執(zhí)行都交給_to完成,類(lèi)似$.fn.animate,實(shí)現(xiàn)如下
/** * 執(zhí)行過(guò)度效果 * @param value 目標(biāo)值 * @param time 過(guò)渡時(shí)間 * @param ease 緩動(dòng)函數(shù) * @param onChange * @param onEnd * @private */ _to: function (value, time, ease, onChange, onEnd) { if (this.fixed) return; var el = this.target, property = this.property; var current = el[property]; var dv = value - current; var beginTime = new Date(); var self = this; var toTick = function () { var dt = new Date() - beginTime; if (dt >= time) { el[property] = value; onChange && onChange.call(self, value); onEnd && onEnd.call(self, value); return; } el[property] = dv * ease(dt / time) + current; el[property] = a; self.tickID = requestAnimationFrame(toTick); //cancelAnimationFrame必須在 tickID = requestAnimationFrame(toTick);的后面 onChange && onChange.call(self, el[property]); }; toTick(); },
我們替換一下原有的ease函數(shù),也可以達(dá)到同樣效果,這里我使用TweenJS提供的緩動(dòng)函數(shù)
let a = Tween.Quad.easeOut(dt, current, dv, time); // console.log(a); // el[property] = dv * ease(dt / time) + current; el[property] = a; self.tickID = requestAnimationFrame(toTick); //cancelAnimationFrame必須在 tickID = requestAnimationFrame(toTick);的后面 onChange && onChange.call(self, el[property]);總結(jié) 初速計(jì)算
alloyTouch的大體思路就是在一段拖拽軌跡上,以touchend作為endPoint,再向前300ms內(nèi)選取一個(gè)startPoint,由兩點(diǎn)計(jì)算出初速。
緩動(dòng)函數(shù)相關(guān)知識(shí)var Tween = { Quad: { /** * * @param t 時(shí)間(x軸) * @param b 初始值 * @param c 改變的大小 * @param d 持續(xù)時(shí)間 * @return {*} */ easeOut: function(t,b,c,d){ return -c *(t/=d)*(t-2) + b; } } }
x軸是時(shí)間,y軸是當(dāng)前值,b是y軸的初始值,x軸的初始值是0,t是當(dāng)前時(shí)間。當(dāng)t(x軸)逐漸增加到達(dá)d時(shí),當(dāng)前值(y軸)會(huì)到達(dá)目標(biāo)值(b+c)。
查看展示
擴(kuò)展使用alloyTouch可以很方便的做出類(lèi)似IOS的select效果
做3D效果就更方便啦
要注意的問(wèn)題CSS3中transform:rotateX(30px) translateZ(50px)和transform: translateZ(50px) rotateX(30px)的效果是不一樣的?。?!
前者是先旋轉(zhuǎn)(此時(shí)Z軸的方向已經(jīng)發(fā)生改變),再往Z軸偏移50px,后者是先往Z軸偏移50px,并以當(dāng)前為點(diǎn)基準(zhǔn),再旋轉(zhuǎn)。類(lèi)似的還有perspective,屬性值的排序會(huì)造成影響。
對(duì)此,我提了一個(gè)issue,大家有興趣可以去看看
參考文獻(xiàn)
JavaScript Tween算法及緩動(dòng)效果
緩動(dòng)函數(shù)速查表
alloyTouch
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/88074.html
摘要:不論實(shí)在應(yīng)用游戲操作系統(tǒng)等許多層面,監(jiān)聽(tīng)用戶觸摸,給用戶真實(shí)的運(yùn)動(dòng)反饋是很常見(jiàn)的應(yīng)用場(chǎng)景。正是為了解決這類(lèi)問(wèn)題而生。版本不支持該事件運(yùn)動(dòng)結(jié)束比如上面是運(yùn)動(dòng)的屬性,必須要擁有屬性才能正常工作。 傳送門(mén) Github地址:https://github.com/AlloyTeam/... 簡(jiǎn)介 AlloyTouch的本質(zhì)是運(yùn)動(dòng)一個(gè)數(shù)字,把數(shù)字的物理變化映射到你想映射的任何屬性上。所以帶來(lái)了廣...
摘要:同行這么做使用實(shí)現(xiàn)圓形進(jìn)度條前端掘金在開(kāi)發(fā)微信小程序的時(shí)候,遇到圓形進(jìn)度條的需求。實(shí)現(xiàn)也談數(shù)組去重前端掘金的數(shù)組去重是一個(gè)老生常談的話題了。百度前端技術(shù)學(xué)院自定義前端掘金一標(biāo)簽概念元素表示用戶界面中項(xiàng)目的標(biāo)題。 閑話圖片上傳 - 掘金作者:孫輝,美團(tuán)金融前端團(tuán)隊(duì)成員。15年畢業(yè)加入美團(tuán),相信技術(shù),更相信技術(shù)只是大千世界里知識(shí)的一種,個(gè)人博客: https://sunyuhui.com ...
摘要:同行這么做使用實(shí)現(xiàn)圓形進(jìn)度條前端掘金在開(kāi)發(fā)微信小程序的時(shí)候,遇到圓形進(jìn)度條的需求。實(shí)現(xiàn)也談數(shù)組去重前端掘金的數(shù)組去重是一個(gè)老生常談的話題了。百度前端技術(shù)學(xué)院自定義前端掘金一標(biāo)簽概念元素表示用戶界面中項(xiàng)目的標(biāo)題。 閑話圖片上傳 - 掘金作者:孫輝,美團(tuán)金融前端團(tuán)隊(duì)成員。15年畢業(yè)加入美團(tuán),相信技術(shù),更相信技術(shù)只是大千世界里知識(shí)的一種,個(gè)人博客: https://sunyuhui.com ...
摘要:是整個(gè)圖片在屏幕上的區(qū)域,圖片顯示區(qū)域會(huì)根據(jù)的不同而所不同,通過(guò)的方式,計(jì)算出最終顯示區(qū)域。到達(dá)邊界滾動(dòng)上下一個(gè)圖片有了之前縮放拖拽的基礎(chǔ),這部分就比較簡(jiǎn)單了。 GestureConfig 參數(shù)說(shuō)明參數(shù)描述默認(rèn)值minScale縮放最小值0.8animationMinScale縮放動(dòng)畫(huà)最小值,當(dāng)縮放結(jié)束時(shí)回到m...
閱讀 2701·2021-11-19 09:56
閱讀 963·2021-09-24 10:25
閱讀 1765·2021-09-09 09:34
閱讀 2286·2021-09-09 09:33
閱讀 1117·2019-08-30 15:54
閱讀 636·2019-08-29 18:33
閱讀 1333·2019-08-29 17:19
閱讀 568·2019-08-29 14:19