摘要:效果需求很多場景都需要做各種活動,抽獎最是司空見慣了,跑馬燈的,轉(zhuǎn)盤的,下面先花幾分鐘擼出一個轉(zhuǎn)盤的吧,當(dāng)然網(wǎng)上至少有一打的可供參考。但是我們只差一個定時器循環(huán)了接下里實現(xiàn)這個主循環(huán),不斷更新值就可以了。
demo 效果
需求很多場景都需要做各種活動,抽獎最是司空見慣了,跑馬燈的,轉(zhuǎn)盤的,下面先花幾分鐘擼出一個轉(zhuǎn)盤的吧,當(dāng)然網(wǎng)上至少有一打的 demo 可供參考。
真的只需要一點點時間而已。
實現(xiàn)一個東西,一般都先寫偽代碼,這里也不例外。
初步想法功能主要有兩點:
實現(xiàn)一個類 class,只要傳入一個元素節(jié)點,就可以控制轉(zhuǎn)動,
轉(zhuǎn)動角度和時長以及動畫曲線 可 通過參數(shù)進(jìn)行配置
考慮一下,這個類需要什么方法功能?
根據(jù)上面的兩個需求,
第一 需要一個 設(shè)置參數(shù)的 方法
第二需要提供一個 開啟動畫的方法
第三,既然是動畫,脫不開定時器功能,所以需要一個動畫主循環(huán)
這里再提供一個 init 方法用作初始化操作,比如設(shè)置參數(shù)或者還有其他處理,增加兼容性。
下面是偽代碼
class RotatePlate { constructor(options) { this.init(); } /** * 初始化操作 */ init() { this.setOptions(); } /** * 啟動轉(zhuǎn)動函數(shù) */ rotate() {} /** * 設(shè)置配置參數(shù) */ setOptions() {} /** * 動畫主循環(huán) */ _animate() {} }實現(xiàn)參數(shù) options 配置方法
為了方便使用和兼容處理,我們開發(fā)者常用的做法就是配置默認(rèn)參數(shù),然后用 調(diào)用者的參數(shù)去覆蓋默認(rèn)參數(shù)。所以,先給類增加一些默認(rèn)配置,如下:
constructor(options) { // 緩存用戶數(shù)據(jù)參數(shù),稍后會進(jìn)行默認(rèn)參數(shù)覆蓋,之后再做重復(fù)初始化也會很方便 this.customOps = options; // 默認(rèn)參數(shù)配置 this._parameters = { angle: 0, // 元素初始角度設(shè)置 animateTo: 0, // 目標(biāo)角度 step: null, // 旋轉(zhuǎn)過程中 回調(diào)函數(shù) easing: function(t, b, c, d) { return -c * ((t = t / d - 1) * t * t * t - 1) + b; }, // 緩動曲線,關(guān)于動畫 緩動函數(shù)不太懂得可以自行搜索 duration: 3000, // 動畫旋轉(zhuǎn)時間 callback: () => {}, // 旋轉(zhuǎn)完成回調(diào) }; this._angle = 0; // 維護(hù)一個當(dāng)前時刻角度的私有變量,下面setOptions就知道如何使用了 } this.init(); // 調(diào)用初始化方法
接下來實現(xiàn) setOptions 方法,并且在 init 方法中進(jìn)行調(diào)用,這個方法實現(xiàn)沒什么難度,就是對象合并操作
init() { // 初始化參數(shù) this.setOptions(Object.assign({}, this.customOps)); } /** * 設(shè)置配置參數(shù) */ setOptions(parameters) { try { // 獲取容器元素 if (typeof parameters.el === "string") { this.el = document.querySelector(parameters.el); } else { this.el = parameters.el; } // 獲取初始角度 if (typeof parameters.angle === "number") this._angle = parameters.angle; // 合并參數(shù) Object.assign(this._parameters, parameters); } catch (err) {} }實現(xiàn)一個設(shè)置元素樣式的方法
上面設(shè)置完了參數(shù),我們還沒辦法驗證參數(shù)是否正確。
為了實現(xiàn)旋轉(zhuǎn)效果,我們有兩種方式可供選擇,第一種,利用 css3 的 transform,第二種利用 canvas 繪圖。其實兩種方法都比較簡單,這里先選擇 css3 實現(xiàn)一版,結(jié)尾再附上 canvas 版本的。
// 實現(xiàn)一個css3樣式,我們需要處理兼容性,確定瀏覽器類型,選擇對應(yīng)的屬性 // 這里添加一個輔助方法 /** * 判斷運行環(huán)境支持的css,用作css制作動畫 */ function getSupportCSS() { let supportedCSS = null; const styles = document.getElementsByTagName("head")[0].style; const toCheck = "transformProperty WebkitTransform OTransform msTransform MozTransform".split( " " ); for (var a = 0; a < toCheck.length; a++) { if (styles[toCheck[a]] !== undefined) { supportedCSS = toCheck[a]; break; } } return supportedCSS; } // 在constructor構(gòu)造函數(shù)里面增加一個屬性 this.supportedCSS = getSupportCSS();
然后 給類增加一個 設(shè)置樣式的方法_rotate
_rotate(angle) { const el = this.el; this._angle = angle; // 更新當(dāng)前角度 el.style[this.supportedCSS] = `rotate3d(0,0,1,${angle % 360}deg)`; } // 在 init里面增加 _rotate方法,初始化元素 初始角度 init() { // 初始化參數(shù) this.setOptions(Object.assign({}, this.customOps)); // 設(shè)置一次初始角度 this._rotate(this._angle); }
在這里,就可以寫一個 demo,進(jìn)行測試了,當(dāng)然還么有動畫,只能測試初始角度 angle 設(shè)置
demo 代碼,順便看看我們的腳本代碼變成了什么樣子:
實現(xiàn)動畫主循環(huán)Document
寫到這里,雖然說了一大推話,但是代碼去掉注釋,真的還沒有幾行。
但是我們只差一個定時器循環(huán)了,接下里實現(xiàn)這個主循環(huán),不斷更新 angle 值就可以了。
說起定時器,我們需要計算動畫時間來 判斷是否應(yīng)該取消定時器等等,一些附加操作,所以增加一個_animateStart 方法清理和計時, 下面直接上代碼,
_animateStart() { if (this._timer) { clearTimeout(this._timer); } this._animateStartTime = Date.now(); this._animateStartAngle = this._angle; this._animate(); } /** * 動畫主循環(huán) */ _animate() { const actualTime = Date.now(); const checkEnd = actualTime - this._animateStartTime > this._parameters.duration; // 判斷是否應(yīng)該 結(jié)束 if (checkEnd) { clearTimeout(this._timer); } else { if (this.el) { // 調(diào)用緩動函數(shù),獲取當(dāng)前時刻 angle值 var angle = this._parameters.easing( actualTime - this._animateStartTime, this._animateStartAngle, this._parameters.animateTo - this._animateStartAngle, this._parameters.duration ); // 設(shè)置 el 元素的樣式 this._rotate(~~(angle * 10) / 10); } if (this._parameters.step) { this._parameters.step.call(this, this._angle); } // 循環(huán)調(diào)用 this._timer = setTimeout(() => { this._animate(); }, 10); } // 完成回調(diào) if (this._parameters.callback && checkEnd) { this._angle = this._parameters.animateTo; this._rotate(this._angle); this._parameters.callback.call(this); } }
然后再 rotate 方法調(diào)用_animateStart 就好了
rotate() { if (this._angle === this._parameters.animateTo) { this._rotate(this._angle); } else { this._animateStart(); } }
至此,一個利用 css3 實現(xiàn)的腳本就完成了,有木有很簡單,下面貼上完整代碼.
/** * 功能: 開發(fā)一個旋轉(zhuǎn)插件,傳入一個元素節(jié)點即可控制旋轉(zhuǎn) * 轉(zhuǎn)動角度和時長以及動畫曲線 可 通過參數(shù)進(jìn)行配置 * * 參數(shù)列表: * - dom 需要一個容器 必選 * - angle 初始角度 非必選 * - animateTo 結(jié)束角度 非必選 * - duration 動畫時長 非必選 * - easing 緩動函數(shù) 非必選 * - step 角度每次更新調(diào)用 非必選 * - callback 動畫結(jié)束回調(diào) 非必選 */ class RotatePlate { constructor(options) { // 獲取當(dāng)前運行環(huán)境支持的樣式屬性 this.supportedCSS = getSupportCSS(); // 緩存用戶數(shù)據(jù) this.customOps = options; // 私有參數(shù) this._parameters = { angle: 0, animateTo: 0, step: null, easing: function(t, b, c, d) { return -c * ((t = t / d - 1) * t * t * t - 1) + b; }, duration: 3000, callback: () => {}, }; this._angle = 0; // 當(dāng)前時刻角度 this.init(); } /** * 初始化操作 */ init(newOps = {}) { // 初始化參數(shù) this.setOptions(Object.assign({}, this.customOps, newOps)); // 設(shè)置一次初始角度 this._rotate(this._angle); } /** * 啟動轉(zhuǎn)動函數(shù) */ rotate() { if (this._angle === this._parameters.animateTo) { this._rotate(this._angle); } else { this._animateStart(); } } /** * 設(shè)置配置參數(shù) */ setOptions(parameters) { try { // 獲取容器元素 if (typeof parameters.el === "string") { this.el = document.querySelector(parameters.el); } else { this.el = parameters.el; } // 獲取初始角度 if (typeof parameters.angle === "number") this._angle = parameters.angle; // 合并參數(shù) Object.assign(this._parameters, parameters); } catch (err) {} } _rotate(angle) { const el = this.el; this._angle = angle; // 更新當(dāng)前角度 el.style[this.supportedCSS] = `rotate3d(0,0,1,${angle % 360}deg)`; } _animateStart() { if (this._timer) { clearTimeout(this._timer); } this._animateStartTime = Date.now(); this._animateStartAngle = this._angle; this._animate(); } /** * 動畫主循環(huán) */ _animate() { const actualTime = Date.now(); const checkEnd = actualTime - this._animateStartTime > this._parameters.duration; if (checkEnd) { clearTimeout(this._timer); } else { if (this.el) { var angle = this._parameters.easing( actualTime - this._animateStartTime, this._animateStartAngle, this._parameters.animateTo - this._animateStartAngle, this._parameters.duration ); this._rotate(~~(angle * 10) / 10); } if (this._parameters.step) { this._parameters.step.call(this, this._angle); } this._timer = setTimeout(() => { this._animate(); }, 10); } if (this._parameters.callback && checkEnd) { this._angle = this._parameters.animateTo; this._rotate(this._angle); this._parameters.callback.call(this); } } } /** * 判斷運行環(huán)境支持的css,用作css制作動畫 */ function getSupportCSS() { let supportedCSS = null; const styles = document.getElementsByTagName("head")[0].style; const toCheck = "transformProperty WebkitTransform OTransform msTransform MozTransform".split( " " ); for (var a = 0; a < toCheck.length; a++) { if (styles[toCheck[a]] !== undefined) { supportedCSS = toCheck[a]; break; } } return supportedCSS; }
下面再補(bǔ)充一個 canvas 實現(xiàn)的動畫方法:
_rotateCanvas(angle) { // devicePixelRatio 是設(shè)備像素比,為了解決canvas模糊問題設(shè)置的 // 原理把 canvas畫布擴(kuò)大,然后縮小顯示在屏幕 this._angle = angle; const radian = ((angle % 360) * Math.PI) / 180; this._canvas.width = this.WIDTH * this.devicePixelRatio; this._canvas.height = this.HEIGHT * this.devicePixelRatio; // 解決模糊問題 this._cnv.scale(this.devicePixelRatio, this.devicePixelRatio); // 平移canvas原點 this._cnv.translate(this.WIDTH / 2, this.HEIGHT / 2); // 平移后旋轉(zhuǎn) this._cnv.rotate(radian); // 移回 原點 this._cnv.translate(-this.WIDTH / 2, -this.HEIGHT / 2); this._cnv.drawImage(this._img, 0, 0, this.WIDTH, this.HEIGHT); }源碼下載
完整源碼請到 github 下載,查看
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/96919.html
摘要:最近有個轉(zhuǎn)盤抽獎的需求,搜了一下現(xiàn)有的輪子,有的是用的動畫函數(shù)實現(xiàn)的,有的是用繪圖然后再用高頻率的調(diào)用旋轉(zhuǎn)方法,前者太老了沒法簡單移植到項目,后者感覺性能表現(xiàn)可能不會太好。核心思路是用以及實現(xiàn)旋轉(zhuǎn)動畫,使用和繪制出定位較為精確的輪盤獎項。 最近有個轉(zhuǎn)盤抽獎的需求,搜了一下現(xiàn)有的輪子,有的是用jQuery的動畫函數(shù)實現(xiàn)的,有的是用canvas繪圖然后再用高頻率的setTimeout調(diào)用旋...
摘要:自己很菜,不可否認(rèn)。所以上周日試試水,看看自己能否寫個圓盤抽獎的。效果圖代碼外部圓內(nèi)部園請輸入外數(shù)字開始基礎(chǔ)旋轉(zhuǎn)的圓以自己的寬度的一半為,以父盒子的高度一半為,作為旋轉(zhuǎn)點。 自己很菜,不可否認(rèn)。所以上周日試試水,看看自己能否寫個圓盤抽獎的demo。// github L6zt開發(fā)思路 布局 css rotate 布局; 抽獎過渡效果,采用css3 transition; 動態(tài)計算抽獎...
摘要:最近工作中重構(gòu)了抽獎轉(zhuǎn)盤,給大家提供一個開發(fā)轉(zhuǎn)盤抽獎的思路需求轉(zhuǎn)盤根據(jù)獎品數(shù)量不同而有變化目錄結(jié)構(gòu)由于業(yè)務(wù)需要所以開發(fā)了兩個版本抽獎,和,不過部分只能替換圖片,沒有功能邏輯。 最近工作中重構(gòu)了抽獎轉(zhuǎn)盤,給大家提供一個開發(fā)轉(zhuǎn)盤抽獎的思路 需求 1、轉(zhuǎn)盤根據(jù)獎品數(shù)量不同而有變化 2、canvas 目錄結(jié)構(gòu) showImg(https://segmentfault.com/img/bVbwL...
摘要:公司說要做個活動,迎接雙十一。。大概的思路就是頁面有個轉(zhuǎn)盤,然后轉(zhuǎn)盤是一個背景。轉(zhuǎn)盤的指針也是用圖片。如下圖然后第二步,翻查小程序文檔。最后根據(jù)小程序文檔說,這個參數(shù)需要輸出。為真的時候運行正常旋轉(zhuǎn)的方法,為假的時候。 公司說要做個活動,迎接雙十一。。然后最怕的事情出現(xiàn)了,就是做轉(zhuǎn)盤。以前沒怎么寫過動畫,特別怕這些東西。。。好了,廢話不說。直入正題。 首先,先構(gòu)圖。大概的思路就是頁面...
閱讀 2553·2021-11-23 09:51
閱讀 597·2019-08-30 13:59
閱讀 1897·2019-08-29 11:20
閱讀 2581·2019-08-26 13:41
閱讀 3302·2019-08-26 12:16
閱讀 788·2019-08-26 10:59
閱讀 3397·2019-08-26 10:14
閱讀 654·2019-08-23 17:21