企鵝電競weex實踐——UI開發篇
騰訊DeepOcean原創文章: ofollow,noindex">dopro.io/egame-weex-…
隨著電競業務的不斷髮展,頁面功能越來越多,互動邏輯更加複雜,類似無限滾動、上拉重新整理、橫豎切換滾動等形式在業務中已是標配,經過重重優化後在H5中的體驗一直達不到理想狀態,沒錯,種種卡,H5的效能太差! 是持續優化還是破而後立選擇新的技術方向呢?我們選擇了更有效的後者。
為什麼選擇weex
相對H5來說,weex帶來的使用者體驗更好,它結合了H5和Native各自的優勢,既能像H5一樣快速迭代,又能和Native一樣流暢。
H5 | Weex | Native | |
---|---|---|---|
開發成本 | 低 | 中 | 高 |
維護更新 | 簡單 | 簡單 | 複雜 |
使用者體驗 | 差 | 優 | 優 |
發版稽核 | 不需要 | 不需要 | 需要 |
跨平臺性 | 優 | 優 | 差 |
專案中每一次嘗試新方案、新技術時都將面臨著許多問題,企鵝電競接入weex也不例外,我們在使用weex進行設計還原時並不是像H5一樣順利,為了避免小夥伴重複踩坑,本文將主要圍繞H5與weex的區別以及weex ui開發時遇到的問題進行說明。
H5與Weex的區別
- 專案結構
- 標籤
- 引入sass
- sass變數
weex ui開發踩坑
- 通用樣式
- 佈局
- 元件
- 動畫
- UI效能
H5與Weex的區別
專案結構
由於weex和H5是兩套不同的技術方案,程式碼組織方式、構建工具、和開發側對接方式都會不同。
下圖是電競重構稿H5與weex目錄結構對比,之前H5開發是基於jinja模版,採用grunt構建,在release中生成相應的html檔案,而weex則主要在src中開發元件,採用webpack編譯,最終會在dist中生成相對應地web和weex版jsbundle檔案,再由weex.html生成的二維碼檢視weex版頁面效果。
此外weex下的src目錄內容是與開發側保持一致的,這樣的好處在於開發人員只需要關注元件的結構變化,其它資源直接更新替換即可。

標籤
weex只提供了17個元件,如div、text、image等,其中text和H5中p標籤等同,文字只能放到text下,text中不能巢狀其他標籤。
引入sass
1、安裝sass依賴的npm包:sass-loader、node-sass、sass-loader、stylus-loader
2、在build資料夾下webpack.base.conf.js的rules裡面新增配置
rules:{ { test: /\.(scss|css)$/, loader: 'vue-style-loader!css-loader!sass-loader!stylus-loader?indentedSyntax', } }er-width:$border.width; }
3、style中修改lang="stylus"
sass變數
weex中的sass變數類似於物件的寫法
//variable.scss $border={ color: #E9E9E9 width:2px }
@import "./variable.scss"; .border{ border-color:$border.color; border-width:$border.width; }
weex ui開發踩坑
通用樣式
1、border
weex不支援使用border建立三角形,web可以正常顯示,而ios和android上顯示的是矩形,建議使用圖片代替
web: ios、android:
2、transform
1、rotate角度儘量避免設定負數,某些部分安卓機型會不生效
2、不支援transform:skew 對於這一類角標需要做傾斜處理可以採用圖片加 漸變程式碼處理
3、圖片
1、weex提供了image元件,但只支援遠端圖片連結
2、避免在image標籤上使用v-for,否則會導致安卓上圖片渲染異常(如slider中的圖片)
4、透明度
以下是涉及到顏色的相關屬性對透明度的支援度列表,
注意:box-shadow (本身不支援android),background-image不支援IOS透明
屬性 | IOS | Android | H5 |
---|---|---|---|
color | 支援 | 支援 | 支援 |
opacity | 支援 | 支援 | 支援 |
border-color | 支援 | 支援 | 支援 |
box-shadow | 支援 | 不支援 | 支援 |
background-color | 支援 | 支援 | 支援 |
background-image | 不支援 | 支援 | 支援 |
5、點選態
專案比較常見的點選態多半是透明度的變化,如按鈕、列表、連結等,css的做法是新增偽類 (:active),weex中也同樣支援,但是weex需要在原樣式中新增 opacity:1,否則點選後回不到初始狀態;此外,:active使用時,background-image在ios下會失效。
<template> <div class="ui-btn"> <text class="ui-btn-text">下載</text> </div> </template> <style scoped> .ui-btn{ opacity: 1; /*必須新增*/ } .ui-btn:active{ opacity: .5; } </style>
6、文字截斷
文字從限制1行到不限制可以用lines:0;
<template> <text class="info-text" @click="textClick" :style="textStyle">城市賽戰報,《王者榮耀》城市賽鄭州站歡樂落幕城市賽戰報,《王者榮耀》城市賽鄭州站歡樂落幕城市賽戰報,《王者榮耀》城市賽鄭州站歡樂落幕城市賽戰報,《王者榮耀》城市賽鄭州站歡樂落幕</text> </template> <style scoped> .info-text{ lines:1; text-overflow:ellipsis; } </style> <script> export default { data(){ return { textStyle:{} }; }, methods:{ textClick(){ this.textStyle = { lines:0 } } } } </script> </style>
7、層級問題
例如有a、b、c、d 四層結構,其中a、b、c均為absolute定位,z-index由大到小,d為普通結構,我們知道在css中a層應該是處於最上方,d在最下方,那麼在weex中表現如何呢?
<div class="wrapper"> <div class="box a">a</div> <div class="box b">b</div> <div class="box c">c</div> <div class="d">d</div> </div>web

native
可以看到web和ios、android的表現不一致,ios、android中是以程式碼中dom順序來依次新增的,和z-index無關,後面載入的檢視會覆蓋前面的檢視。
所以要保證web、ios、android三端表現一致,改變dom書寫順序即可。
<div class="d"></div> <div class="box c"></div> <div class="box b"></div> <div class="box a"></div>
8、安卓下遮擋問題
安卓下容器如果設定了寬高,那麼子元素不能超出容器範圍
[cc lang="html"]
[/cc]
web、ios android
Line"/>
建議:fixed定位不會受父容器影響,如果需要超出限制,子元素可以設定fixed
9、v-if問題
在做一些操作切換狀態時(如按鈕點選置灰),應儘量避免使用v-if,使用v-if會閃,且部分安卓機子會發生不可描述的事情(如部分三星機型會出現按鈕文字居頂),可採用新增class的方式
佈局
1、單行文字與圖片並排方案
目前專案中存在這樣的情形,暱稱與直播標籤並排,暱稱文字短時直播要跟隨,暱稱很長時要做溢位截斷(超出時加...)

這種佈局方式在css中要做到很容易,而在weex中利用提供的flex佈局確很難實現,最後的解決方案是通過js動態設定文字與標籤父級的寬度,從而控制文字的溢位
<template> <div class="wrapper" @appear="onappear"> <div class="info-container" ref='info-container'> <div class="info" ref='info' :class="isFullText?['info-full']:[]"> <text class="nick-text">{{isFullText}}企鵝電競企鵝電競企鵝電競企鵝電競</text> <img src="http://119.29.8.64/vipstyle/egame/app/weex/tab/ERICKCHEN-MC0/dist/static/img/live.b467410.png" class="live-image"> </div> </div> </div> </template> <style scoped lang="stylus"> .info-full{ width:300px; } </style> <script> const dom = weex.requireModule('dom') export default { data(){ return { isFullText:false }; }, methods:{ onappear(){ dom.getComponentRect(this.$refs['info'],option1=>{ dom.getComponentRect(this.$refs['info-container'],option2=>{ if(option1.size.width>=option2.size.width){ this.isFullText=true; } }) }); } } } </script>
2、多行文字與圖片並排方案
場景一:圖片位於段落左側

css的float可以做到圖文混排,而weex只提供了flex佈局,並且text元件之間也不能進行巢狀,無法做到這種圖文混排效果,不過weex的text元件比較奇特,那就是text元件中的空格是照程式碼原樣輸出的,如
<text> 戰國鬼才傳,這個名字想必很多人聽都沒有聽過吧,這個名字說實話真的不是很吸引人…</text>文案效果:

所以解決的方案可以利用填充空格給圖片預留位置,先計算一個空格的寬度,再計算這張圖片所需要的空格數量,最後空格連線字串輸出。
ios、android效果如下(紅色色塊為圖片區域):
結構
<template> <div class="wrapper"> <scroller class="scroller"> <div @appear="handleAppear"> <text>空格寬度:{{spaceWidth}}-空格數量:{{spaceNum}}</text> <text class="demo-text" ref="demo-text1"> test</text> <text class="demo-text" ref="demo-text2">test</text> </div> <div class="rich"> <div class="rich-icon"></div> <text class="rich-text" :style="textStyle">{{content}}</text> </div> </scroller> </div> </template>
樣式
<style scoped> .demo-text{ position: absolute; font-size: 32px;/*文字大小與需要加空格文字大小保持一致*/ opacity: 0; } .rich{ position: relative; } .rich-icon{ position: absolute; left:0; top:4px; width: 120px; height: 32px; background-color: red; } .rich-text{ font-size: 32px; } </style>
核心程式碼
<script> const dom = weex.requireModule('dom'); export default { data(){ return { spaceWidth:0,//空格寬度 spaceNum:0,//所需空格數量 opacity:0,//初始透明度為0,避免文案抖動 content:'王者榮耀遊戲中的鑽石用來做什麼最合算?王者榮耀鑽石用來幹什麼最好?在王者榮耀中鑽石並不是唯一通用的貨幣,在遊戲中還有金幣和點券,小編個人覺得鑽石在遊戲中並沒有其他兩種貨幣有優勢。' }; }, computed:{ textStyle(){ return { opacity:this.opacity } } }, methods:{ handleAppear(){ setTimeout(()=>{ this.setTextContent(); },30) }, async setTextContent(){ const text1El = this.$refs['demo-text1']; const text2El = this.$refs['demo-text2']; let textSize1,textSize2; await this.getSpaceSize(text1El,(data)=>{ textSize1 = data; }); await this.getSpaceSize(text2El,(data)=>{ textSize2 = data; }); this.spaceWidth=Math.abs(textSize1-textSize2)/10; this.content=this.getSpaceNum(); this.opacity=1; }, getSpaceSize(el,callback){ return new Promise(function (resolve) { dom.getComponentRect(el, option => { if(option.result){ resolve(callback(option.size.width)); } }); }) }, getSpaceNum(){ this.spaceNum = Math.ceil(120 / this.spaceWidth);//120為紅色區塊寬度 return new Array(this.spaceNum).join(' ')+ this.content; } } } </script>
場景二:圖片位於段落末尾

很遺憾,目前這種特殊文字以及圖片置於段落末尾並沒有找到相應的解決方案,只能依賴終端新增相應的富文字功能。
元件
1、命名
元件命名應避免使用JS關鍵字和保留字,以及weex提供的元件名稱,如用loading作為元件名稱,在ios與android中將呈現空白。
錯誤示例:
<template> <div class="wrapper"> <Loading></Loading>/*改用其它名稱*/ </div> </template> <script> import Loading from './demo' export default { components:{ Loading } } </script>
2、自定義slider元件
weex本身提供了slider元件,但輪播圖指示器(indicator)只能修改顏色與位置,大小卻無法更改,所以需要自定義slider元件
weex輪播圖指示器效果:

電競專案輪播圖指示器效果

weex slider提供了change事件,可以獲取到當前播放的序號,從而做到自定義輪播指示器。 但是當中遇到一個詭異問題:如果“div.indicator-item”的內容為空的話,H5中的指示器並不會隨著圖片切換而變化(樣式不生效),div中需要新增內容才行
<div class="indicator-item" v-for="(item,index) in data" ...> {{index}}/*新增內容,解決H5中class切換樣式不生效問題*/ </div>
完整結構如下:
<template> <div class="slider-container" :style="sliderStyle"> <slider class="slider" :interval="interval" @change="change" :auto-play="autoPlay"> <div class="slider-item" v-for="img in data" > <image class="slider-image" :style="sliderStyle" resize="cover" :src="img.src"></image> </div> </slider> <div class="slider-indicator"> <div class="indicator-item" v-for="(item,index) in data" :class="[current == index ? 'indicator-active' : '']"> {{index}} </div> </div> </div> </template>
動畫
關鍵幀動畫是很常見的一種動畫,css3中可以利用@keyframes規則達到動畫效果
css3: [cc lang="css"]
[/cc] 效果如圖:
weex中提供了transition,可以傳入相應的style,通過setInterval控制動畫迴圈播放,但setInterval比較耗效能,建議終端對weex sdk進行改造,加入相應的迴圈播放功能
<template> <div class="wrapper"> <div class="demo" ref="demo"></div> </div> </template> <style scoped> .demo{ width: 200px; height: 200px; background-color: gold; } </style> <script> import * as animation from './animation.js' export default { mounted() { setTimeout(()=>{ setInterval(() => { animation.run(this.$refs.demo); }, 2100); },200) } } </script>
animation.js
const animation = weex.requireModule('animation'); export function transition(el, opts,dd) { let duration = dd || 400 if (!el) { return Promise.resolve(); } return new Promise(function (resolve) { animation.transition(el, { duration: duration, timingFunction: 'linear', delay: 0, ...opts }, resolve); }) } export async function run(el, obj) { await transition(el, { styles: { backgroundColor: 'red', } },0.0001) await transition(el, { styles: { backgroundColor: 'purple', } },1000) await transition(el, { styles: { backgroundColor: 'lime', } },1000) }
UI效能
安卓下開啟“除錯GPU過度繪製”選項,開啟之後選擇“顯示過度區域繪製”後,會發現手機介面基本被藍色,淡綠,淡紅,深紅所填充,這幾種顏色代表了不同程度的繪製情況,其中藍色繪製最少,而深紅色繪製最多,可能會造成頁面卡頓,應避免出現大面積紅色區域。
優化建議:
1、儘量不要設定背景色
2、不要過度巢狀,結構儘量扁平化
最後
weex目前仍在不斷的完善中,過程中遇到的問題將不斷被修復,後續將持續更新。
歡迎關注"騰訊DeepOcean"微信公眾號,每週為你推送前端、人工智慧、SEO/ASO等領域相關的原創優質技術文章:
看小編搬運這麼辛苦,關注一個唄:)
