一 專案背景
略 公司內部資料以及安全紅線 只討論技術細節 不透漏相關互動和設計
二 互動以及視覺
整體視覺:
略
抽獎模組:大概是以一個花朵的花瓣為抽獎背景,旋轉的時候不同的花瓣點亮,省略視覺稿,只討論技術實現細節
互動:略
三 分析需要解決的問題
當做一個沒有做過的東西的時候,這時候應該分析下完成這個效果需要考慮的問題,然後分解分析,然後逐步實施,分析總結遇到過的問題,是否有沒考慮到的。
其實這種抽獎模組在現在的活動頁中十分的常見,尤其是在活動頁中。
首先,整理了TODO List
- 需要寫成元件的形式,方便複用,需要考慮元件需要的傳遞的引數等等。
- 後端是介面的形式,也就是說,使用者點選抽獎的時候,已經獲得了中獎資訊,前端需要執行一下抽獎輪轉的動畫,這個動畫的時間、轉動速率、轉動期間禁止需要重複點選、初始的指標位置都需要前端來控制。
- 狀態管理,針對不同抽獎次數、區分是否登陸、是否在APP內等相應的狀態進行處理,彈窗或者跳轉相應的URL
- 前端埋點相關和資料上報相關。
- 樣式問題,設計互動的思路實際上是一個九宮格抽獎,但是視覺樣式卻是偏向指標類的輪盤抽獎,邏輯層面暫定的是按照九宮格的思路去實現,但是需要樣式方面進行調整
- 抽獎的結果也需要寫成一個彈窗元件,並且需要定義傳入的引數
- 異常展示,當介面呼叫失敗、後端資料庫庫存不足等等需要相應的warning彈窗元件
大致互動流程如下
四 具體設計
4.1 封裝成公共元件,主要是對元件傳遞的引數進行定義,首先在首頁需要呼叫抽獎元件,必傳的有三個引數
showLuckDraw // 是否展示抽獎模組,如果使用者已經抽過獎,預設不展示此模組,直接展示抽獎結果
userStatus // 使用者狀態:首頁介面中會返回此資訊,需要傳入到元件中
drawStatus // 抽獎狀態:
此外還需要給抽獎元件的子元件–抽獎結果元件傳遞引數
showSpecial&&type
props: { // 是否展示抽獎模組,如果使用者已經抽過獎,預設不展示此模組,直接展示抽獎結果 showLuckDraw: { type: Boolean }, // 使用者狀態 userStatus: { type: Number, required: true }, // 抽獎狀態 0: 跳轉活動頁 1: 需要通過活動增加抽獎次數 3: 不可抽獎 drawStatus: { type: Number, required: true }, // 是否已登陸 未登陸吊起登陸元件 hasLogin: { type: Boolean, required: true }, // 特殊彈窗 展示抽獎的結果 showSpecial: { type: Boolean, default: false }, // 向抽獎結果彈窗 傳的值 顯示對應的抽獎結果 type: { type: Number, required: true, } }, |
4.2 抽獎動畫(核心邏輯)
核心的思路是,當點選抽獎的時候,呼叫後端介面,並且設定轉盤所需要的初始引數,包括如下
- diff 速度累加變數,控制逐漸加快/逐漸減慢
- star 開始按鈕點選態,如果使用者已經無法抽獎,直接禁用
- speed 抽獎初始速度
-
isDisable 是否禁用開始抽獎按鈕,防止重複抽獎,保證在在抽獎動畫結束後才能再次抽獎,避免重複呼叫後端介面
-
drawStatus 設定抽獎狀態初始值
- startTime 記錄轉動開始時間,用來控制動畫時長
同時,設定一個定時器,輪盤的背景圖片依次切換來實現動畫的效果,利用定期器的milliseconds 時間間隔進行速度控制,當達到預設的轉動時間時,開始做減速運動,且如果後端返回的獎品id和前端設定的獎品id能對應上時,清除定時器,同時return中止函式,且重新設定轉盤的狀態值,完成抽獎動畫。
html 部分使用的簡潔的jade,雖然剛開始上手有些不適應,但是用慣了之後,感覺十分簡潔乾淨,不需要在大段的html程式碼裡尋找calss等
<template lang="jade"> .award-box(v-if="showLuckDraw") .award-bg .award(:class="'active' + (current)") button.start-btn( @click="start", :disabled="isDisable", :class="star ? 'star': '' " ) .award-num 當前抽獎次數 i.award-n {{ currentNum = +this.drawStatus === 2 ? 1 : 0 }} i 次 special-dialog(:show-special="showSpecial", :type="id") </template> |
move() { let timeout = setTimeout( () => { this.current++; // 切換背景圖,初始值可以隨意設定 if ( this.current > 4) { // 因為只有5個獎品,所以當current 從0開始大於4的時候,重新載入第一張圖片 this.current = 0; } // 若抽中的獎品id存在,且轉動時間大於2秒,則開始減速轉動 if ( this.award.id && ( Date.now() - this.startTime ) / 1000 > 2 ) { this.speed += this.diff; // 轉動減速 //若轉動超過4秒,並且獎品id等於格子的獎品id,則停下來 // this.awards 是從後端獲取的中獎結果,如果和前端對應的獎品id對應上的話則為中獎 if ( ( Date.now() - this.startTime ) / 1000 > 4 && this.award.id == this.awards[this.current].id ) { clearTimeout( timeout ); this.star = false; this.isDisable = false; this.showSpecial = true; // 呼叫抽獎結果元件 this.type = this.id; // 將中獎結果賦值給type,顯示對應的抽獎結果元件 setTimeout( () => { console.log(this.award.id); },0); return; // 抽中獎後停止定時器 } //若抽中的獎品不存在,則加速轉動 } else { // todo:如果後端介面200且返回的中獎結果是錯的,這裡可以處理下異常 this.speed -= this.diff; } this.move(); }, this.speed ); }, |
4.3 狀態管理,在呼叫move() 抽獎動畫的之前,就對相應的使用者狀態做處理,未登陸的吊起登陸,沒有次數的去獲取次數,不可抽獎呼叫彈窗等等
4.4 埋點這裡在工具函式裡封裝了一個公共的函式,直接呼叫傳參記錄埋點資訊,關於埋點的話有空再展開
4.5 樣式方面,遇到了一些問題
- 最開始的想法是隻改變花瓣的背景圖,但是最後發現,圖片是不規則的,是有角度的,難以定位準確。而且定位位置的計算在不同機型上有差異,所以採用整體圖片的替換
- 整體圖片的替換又遇到一個閃爍問題:
成因:因為當圖片花瓣旋轉時視覺上只是每個花瓣的變化,實際上是整張圖片的變化,通過動態的切換calss來完成背景圖片的替換。正因為這樣,新的問題就產生了,當程式碼構建部署打包發不到線上時,靜態資源一般會有單獨的伺服器,也就是說圖片的資源請求會有一定延遲,這就導致了第一次載入點選抽獎的時候,頁面花瓣轉動會有閃爍,而第二次圖片會走快取,所以這個問題只出現一次,但是卻是致命的
解決方法:把url改為base64的Data URL,實際上就是利用base64編碼把圖片資料翻譯成標準ASCII字元,Data URL是在本地直接繪製圖片,不是從伺服器載入,所以節省了HTTP連線,起到加速網頁的作用,但是弊端就是IE8以下瀏覽器不支援這種方法(當然app內肯定是支援的,絕大部分手機也是支援的)。用這種方法會加重客戶端的CPU和記憶體負擔,總之有利有弊,而且不適合大圖片,針對這個圖片找UI替換了體積更小的圖片,以減輕客戶端的記憶體負擔。
4.6 針對中獎結果的彈窗,封裝另一個元件,只需要傳遞type的引數就顯示不同的中獎結果,類似的還有掛在到全域性的彈窗元件,異常的時候及時丟擲錯誤彈窗
五 總結和反思
雖然說現在活動頁比較多且互動複雜,但是可以儘可能的封裝一些公共的元件,提供基本的骨架,只需要修改一些css就能快速完成一個功能。
還有對於沒有做過的東西要具體分析,拆解成步驟,一步一步的完成,同時預留好一定的buffer時間,最重要的,經常總結與反思,查詢自身的不足,在繁忙的工作中提升自己。