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