標簽:eth nsf 等于 gif 設置 htm transform mes list
抽獎是各類營銷活動中最常見的一種形式,本産品需求大致如下:轉盤周圍跑馬燈交替閃爍,點擊抽獎,大轉盤旋轉,調用接口獲取抽獎結果,大轉盤指針指向對應的獎品。高保如下圖1
圖1-高保
本需求要求跑馬燈交替閃爍,那四周的跑馬燈就不能是死的圖片了,要用動畫來實現,並且第奇數,偶數交替變換,這個使用vue中的動態屬性可以實現。
其次小灯泡分布在四周,首先想到的是 transform: rotate(); 然后獎品圖片也是分布在圆形四周,这个也可以用 transform: rotate()。
最后点击立即抽奖,包含獎品圖片的整个dom旋转,这个使用animation+transform: rotate();可以实现。
各位看官請注意,這裏只介紹了關鍵實現過程,中間涉及到的布局是大多使用absolute定位來實現的,中獎彈框是另外一個組件,因爲不是關鍵,所以沒有具體介紹,也沒有貼出代碼。
這個背景一般是UI給個圖片出來,雖然使用css可以實現複雜的圖形,但是要花很長時間得不償失,一般給個背景圖片就可以了。注意這裏高保給的有問題,後面獎品的dom會用另外一張背景覆蓋。如下圖2
圖2-轉盤背景
跑馬燈是一個一個的燈泡,放在轉盤周圍,這個要根據高保尺寸來寫,不然距離有差,沒法落在四周邊緣的位置。還有小燈泡明暗交替放置,不然也沒有效果。html代碼如下:
<!-- 轉盤周圍跑馬燈 --> <div class="lightWrap"> <div v-for="i in 20" :key="i" :style="setLightRotate(i)" class="lightItem"> <img v-if="i % 2 == 0" :class="{active: !lightChange}" :src="lightChange ? lightGrey : lightActive" alt=""/> <img v-else :class="{active: lightChange}" :src="!lightChange ? lightGrey : lightActive" alt=""/> </div> </div>
注意周围有20个灯泡,所以使用v-for="i in 20",然后setLightToatate(i)是一个方法,用来設置每个灯泡的倾斜度,如下:
//20个灯泡設置倾斜 setLightRotate(index) { let lightRotate = (360 / 20) * index return { transform: ‘rotate(‘ + lightRotate + ‘deg)‘ } }
這個很容易理解了,整個圓是360度,除以20,是每兩個圓之間的間隔角度,再乘以數組20的下標,就是每個燈泡的偏移角度。
小灯泡的显示就需要交替显示了,计算奇偶使用表达式i%2 == 0,然后使用一个变量lightChange来判断当前这个灯泡是否是点亮,来区分显示不同的图片。这个变量是通过questAnimationFrame递归调用来修改。方法如下:
//浏覽器播放跑馬燈動畫 setTimeLine() { this.lightCount += 1 // 浏览器渲染频率 60帧/s 约等于 16.66ms 一次,取20的倍数,就是约300ms切换跑马灯一次 if ((this.lightCount % 20) == 0) { this.lightChange = !this.lightChange } requestAnimationFrame(this.setTimeLine) }
在mounted鈎子裏調用一次setTimeLine()方法,然後在方法裏調用requestAnimationFrame(),但是在requestAnimation()的回調函數裏又調用了自己。注意這種方式類似遞歸調用,但是不是遞歸調用,浏覽器的渲染評率是60幀每秒,也就是requestAnimationFrame()的回調函數在1000毫秒/60=16.66毫秒,也就是每16.66毫秒就執行一次setTimeLine()方法,在方法裏lightChange自增1,判斷lightCount是20的倍數切換lightChange變量,然後16.66毫秒*20=33.33毫秒切換一次。
lightChange变量还切换了当前灯泡的样式,点亮后还会設置灯泡变大一点。css如下:
.lightWrap { width: $turntableWrap_size; height: $turntableWrap_size; position: absolute; left: 50%; top: 50%; margin-left: calc(#{$turntableWrap_size} / -2); margin-top: calc(#{$turntableWrap_size} / -2); .lightItem { width: 22px; height: calc(#{$turntableWrap_size} / 2); position: absolute; left: 50%; top: 0%; transform-origin: 0 calc(#{$turntableWrap_size} / 2); img { width: 22px; height: 22px; position: absolute; top: 10px; left: 0; } img.active { width: 40px; height: 40px; position: absolute; top: 1px; left: -9px; } } }
最終效果如下圖3
圖3
接下来是要把獎品圖片放在转盘上,并且分布在转盘四周,原理还是使用transform: rotate();方法来設置倾斜。html代码如下:
<!-- 獎品圖片 --> <div class="circleMax" :class="{ani: runningLock}"> <div v-for="(item, i) in actPrizeList" :key="i" :style="setRotate(i)" class="spin"> <div :style="setSpinInner(i)" class="spinInner"> <div :style="spinCntDocObj" class="spinCntDoc"> <div class="spinImg"> <img :src="item.imgUrl" alt="" srcset=""> </div> </div> </div> </div> </div>
actPrizeList就是奖品信息了,这个是从接口获取,里面有配置好的獎品圖片连接。这里还是使用了一个方法setTotate(i)来动态設置样式,方法如下:
/* 獎品圖片倾斜 */ setRotate(index) { let spinRotate = this.jiaodu * index return { transform: ‘rotate(‘ + spinRotate + ‘deg)‘ } }
setSpinInner()方法的功能類似,如下:
setSpinInner(index) { return { transform: ‘rotate(-‘ + this.jiaodu + ‘deg)‘, borderLeft: 0 } }
这里的变量jiaodu是45,是根据360度/8个奖品的规则来的,用来调整奖品倾斜度。这里还有一个spinCntDocObj对象,用来設置獎品圖片容器的尺寸,这个是为了在某些需求不是显示獎品圖片,而是奖品名称的时候,或者即显示奖品名称,又显示獎品圖片的时候布局方便,当然在这里只显示了一个獎品圖片。如下圖4
圖4
具体設置方法根据内圈直径计算容器宽度,代码如下:
/* 圖片旋轉 */ setSpinCntDoc() { let spinCntDocWidth = (Math.sin((this.jiaodu / 2) * (Math.PI / 180)) * this.tableInnerSize) / 70 this.spinCntDocObj.width = spinCntDocWidth + ‘rem‘ this.spinCntDocObj.textAlign = ‘center‘ this.spinCntDocObj.transform = ‘rotate(‘ + (this.jiaodu / 2) + ‘deg)‘ }
跑马灯有了,奖品也有了,剩下就是要背景整体旋转起来了。细心的话,你会发现在所有奖品容器上有一个动态样式:class="{ani: runningLock}",变量runningLock这个变量是用来控制大轉盤旋轉的,大转盘中所有奖品容器如下圖5:
圖5
css類anti中包含一個animation動畫,如下:
.ani { animation: circle 3s ease forwards; }
因爲最後要根據中獎的獎品來計算大轉盤具體傾斜的角度,所以這個關鍵幀circle需要在請求接口之後,通過js代碼動態加載到頁面上,下面講抽獎按鈕的時候會具體的說明。
接下來需要把抽獎按鈕放在大轉盤正中間,還是使用相對定位absolute來實現,html代碼如下:
<!-- 抽獎按鈕 --> <div class="arrowBtn" :class="{btnShake: btnShakeShow}" @click="startClick"> <img src="../assets/images/summer/btn-draw.png" alt=""/> </div>
css代碼如下
.arrowBtn { width: 216px; height: 260px; border-radius: 95px; text-align: center; position: absolute; left: 50%; top: 50%; margin-left: -108px; margin-top: -152px; img { width: 100%; } }
在点击按钮的时候也有一个动画,就是按钮会变大然后变小,看起开是弹了一下,这个就简单了,使用animation动画就好,这里通过btnShakeShow变量来控制,css代碼如下:
.btnShake { animation: btnShakeAni 0.5s ease-out forwards; } @keyframes btnShakeAni { 0% { transform: scale(1); } 10% { transform: scale(1.1); } 30% { transform: scale(0.9); } 50% { transform: scale(1.1); } 70% { transform: scale(0.9); } 90% { transform: scale(1.1); } 100% { transform: scale(1); } }
最後效果如下圖6
圖6
上面3.4讲到在css类ani中使用animation动画circle来控制整个大轉盤旋轉,并且要根据接口返回的抽奖结果计算旋转角度动态設置rotate角度。代码如下
/* 點擊抽獎播放動畫 */ startClick() { //大轉盤旋轉 this.runningLock = true //抽獎按鈕弹一下 this.btnShakeShow = true //調接口 let data = {actCode: actCode} coc2.drawLottery(data).then(res => { if (res.code == 0) { if (res.data) { this.prizeNum = this.actPrizeList.findIndex((item, index) => item.pid == res.data.pid) this.prizeName = res.data.prizeName this.prizeImgSrc = res.data.litimgUrl } } //動態加載動畫關鍵幀 let targetDeg = 360 * this.defaultRunTimes + (this.spinNum - this.prizeNum) * this.jiaodu + this.jiaodu * 0.5 let runkeyframes = `@keyframes circle{ 0% {transform: rotate(0deg);} 100% {transform: rotate(${targetDeg}deg); }` document.getElementById(‘mystyle‘).innerHTML = runkeyframes setTimeout(() => { this.$refs.refAlert.show(‘getPrize‘) }, 3500) }) }
注意spinNum是转盘中所有奖品个数,prizeNum是中奖奖品在整个奖品中数组中的下标,二者做减法,然后头部加上一个转盘默认要转圈数,尾部加上一个偏移(360度/8=45度)就可以定位到相应的位置的角度。随后就是用这个角度拼接关键帧,最后动态設置这个css关键帧。注意要在index.html中加上一个id为mystyle的style元素,html如下:
圖7
整个动画部分已经完成,来看看整体效果是怎么样的,如下圖7
圖7
最后还有一个问题,抽奖之后需要无论是否中奖都需要将转盘复原到初始状态,这个动作的触发时机在中獎彈框弹出之后,这样方便下一次抽奖。实现这个功能需要在点击奖品弹框的时候使用回调的方式。最后中奖的獎品圖片和奖品名称也需要通过属性赋值传递给奖品弹框。上面代码中有的this.prizeName = res.data.prizeName;this.prizeImgSrc = res.data.litimgUrl就是在做这个事情。下面的html代码。
<!-- 中獎彈框 --> <dialog-alert ref="refAlert" :prize-img-src="prizeImgSrc" :prize-name="prizeName"></dialog-alert>
在dialog-alert组件中,会有一个事件回调,这里使用的是eventbus,原因是这个组件在多个地方调用,这个和本文的主题关系不大 ,只简单提一下。组件中回调方法如下:
EventBus.$emit("turntableReset")
當前抽獎組件中監聽方法如下:
mounted() { // 弹窗关闭 重置大转盘 EventBus.$on("turntableReset", () => this.turnTableReset()) }, methods{ //轉盤數據重置 turnTableReset() { this.startLock = false this.runningLock = false this.btnShakeShow = false }
}
最後看看整體效果,如下圖8
圖8
本功能還涉及到其他的功能,本功能實現的有些倉促,還有很多可以改進的地方。例如在播放動畫之前先請求了接口,等後端有了響應才開始播放動畫,這個不太合理,應該是先播放一個動畫,等有結果之後,再播放第二個動畫,讓指針指向中獎獎品。可以使用jquery動畫,或者tween.js,下次有時間再研究。
標簽:eth nsf 等于 gif 設置 htm transform mes list
原文地址:https://www.cnblogs.com/tylerdonet/p/15070165.html