阅读 3863

企鹅电竞weex实践——UI开发篇

腾讯DeepOcean原创文章: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里面添加配置

  1. rules:{
  2. {
  3. test: /\.(scss|css)$/,
  4. loader: 'vue-style-loader!css-loader!sass-loader!stylus-loader?indentedSyntax',
  5. }
  6. }er-width:$border.width;
  7. }
3、style中修改lang="stylus"

sass变量

weex中的sass变量类似于对象的写法
  1. //variable.scss
  2. $border={
  3. color: #E9E9E9
  4. width:2px
  5. }
  1. @import "./variable.scss";
  2. .border{
  3. border-color:$border.color;
  4. border-width:$border.width;
  5. }
  6.  

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下会失效。
  1. <template>  
  2.    <div class="ui-btn">
  3.        <text class="ui-btn-text">下载</text>
  4.    </div>
  5. </template>
  6. <style scoped>
  7.    .ui-btn{
  8.        opacity: 1; /*必须添加*/
  9.    }
  10.    .ui-btn:active{
  11.        opacity: .5;
  12.    }
  13. </style>
  14.  

6、文本截断

文本从限制1行到不限制可以用lines:0;
  1. <template>
  2.    <text class="info-text"
  3.          @click="textClick"
  4.          :style="textStyle">城市赛战报,《王者荣耀》城市赛郑州站欢乐落幕城市赛战报,《王者荣耀》城市赛郑州站欢乐落幕城市赛战报,《王者荣耀》城市赛郑州站欢乐落幕城市赛战报,《王者荣耀》城市赛郑州站欢乐落幕</text>
  5. </template>
  6. <style scoped>
  7.    .info-text{
  8.        lines:1;
  9.        text-overflow:ellipsis;
  10.    }
  11. </style>
  12. <script>
  13.    export default {
  14.        data(){
  15.            return {
  16.                textStyle:{}
  17.            };
  18.        },
  19.        methods:{
  20.            textClick(){
  21.                this.textStyle = {
  22.                    lines:0
  23.                }
  24.            }
  25.        }
  26.    }
  27. </script>
  28. </style>
  29.  

7、层级问题

例如有a、b、c、d 四层结构,其中a、b、c均为absolute定位,z-index由大到小,d为普通结构,我们知道在css中a层应该是处于最上方,d在最下方,那么在weex中表现如何呢?
  1. <div class="wrapper">
  2. <div class="box a">a</div>
  3. <div class="box b">b</div>
  4. <div class="box c">c</div>
  5. <div class="d">d</div>
  6. </div>
  7. &nbsp;
  8.  
web  native

可以看到web和ios、android的表现不一致,ios、android中是以代码中dom顺序来依次添加的,和z-index无关,后面加载的视图会覆盖前面的视图。

所以要保证web、ios、android三端表现一致,改变dom书写顺序即可。

  1. <div class="d"></div>
  2. <div class="box c"></div>
  3. <div class="box b"></div>
  4. <div class="box a"></div>
  5.  

8、安卓下遮挡问题

安卓下容器如果设置了宽高,那么子元素不能超出容器范围

[cc lang="html"]

 

[/cc]

web、ios android 

建议:fixed定位不会受父容器影响,如果需要超出限制,子元素可以设置fixed

9、v-if问题

在做一些操作切换状态时(如按钮点击置灰),应尽量避免使用v-if,使用v-if会闪,且部分安卓机子会发生不可描述的事情(如部分三星机型会出现按钮文字居顶),可采用添加class的方式

布局

1、单行文本与图片并排方案

目前项目中存在这样的情形,昵称与直播标签并排,昵称文字短时直播要跟随,昵称很长时要做溢出截断(超出时加...)

这种布局方式在css中要做到很容易,而在weex中利用提供的flex布局确很难实现,最后的解决方案是通过js动态设置文字与标签父级的宽度,从而控制文字的溢出

  1. <template>
  2.    <div class="wrapper"  @appear="onappear">
  3.        <div class="info-container" ref='info-container'>
  4.            <div class="info" ref='info' :class="isFullText?['info-full']:[]">
  5.                <text class="nick-text">{{isFullText}}企鹅电竞企鹅电竞企鹅电竞企鹅电竞</text>
  6.                <img src="http://119.29.8.64/vipstyle/egame/app/weex/tab/ERICKCHEN-MC0/dist/static/img/live.b467410.png" class="live-image">
  7.            </div>
  8.        </div>
  9.    </div>
  10. </template>
  11.  
  12. <style scoped lang="stylus">
  13.    .info-full{
  14.        width:300px;
  15.    }
  16. </style>
  17.  
  18. <script>
  19. const dom = weex.requireModule('dom')
  20.    export default {
  21.        data(){
  22.            return {
  23.                isFullText:false
  24.            };
  25.        },
  26.        methods:{
  27.            onappear(){
  28.                dom.getComponentRect(this.$refs['info'],option1=>{
  29.  
  30.                        dom.getComponentRect(this.$refs['info-container'],option2=>{
  31.  
  32.                            if(option1.size.width>=option2.size.width){
  33.                                this.isFullText=true;
  34.                            }
  35.                        })
  36.  
  37.                    });
  38.  
  39.  
  40.            }
  41.        }
  42.    }
  43. </script>
  44.  

2、多行文本与图片并排方案

场景一:图片位于段落左侧

css的float可以做到图文混排,而weex只提供了flex布局,并且text组件之间也不能进行嵌套,无法做到这种图文混排效果,不过weex的text组件比较奇特,那就是text组件中的空格是照代码原样输出的,如

  1. <text>              战国鬼才传,这个名字想必很多人听都没有听过吧,这个名字说实话真的不是很吸引人…</text>
  2.  
文案效果:

所以解决的方案可以利用填充空格给图片预留位置,先计算一个空格的宽度,再计算这张图片所需要的空格数量,最后空格连接字符串输出。

ios、android效果如下(红色色块为图片区域):

结构

  1. <template>
  2.    <div class="wrapper">
  3.      <scroller class="scroller">
  4.        <div @appear="handleAppear">
  5.            <text>空格宽度:{{spaceWidth}}-空格数量:{{spaceNum}}</text>
  6.            <text class="demo-text" ref="demo-text1">          test</text>
  7.            <text class="demo-text" ref="demo-text2">test</text>
  8.        </div>
  9.  
  10.        <div class="rich">
  11.          <div class="rich-icon"></div>
  12.          <text class="rich-text" :style="textStyle">{{content}}</text>
  13.        </div>
  14.      </scroller>  
  15.  
  16.    </div>
  17. </template>
  18.  
样式
  1. <style scoped>
  2.    .demo-text{
  3.        position: absolute;
  4.        font-size: 32px;/*文字大小与需要加空格文字大小保持一致*/
  5.        opacity: 0;
  6.    }
  7.    .rich{
  8.      position: relative;
  9.    }
  10.    .rich-icon{
  11.      position: absolute;
  12.      left:0;
  13.      top:4px;
  14.      width: 120px;
  15.      height: 32px;
  16.      background-color: red;
  17.    }
  18.    .rich-text{
  19.      font-size: 32px;
  20.    }
  21. </style>
  22.  
核心代码
  1. <script>
  2.    const dom = weex.requireModule('dom');
  3.    export default {
  4.        data(){
  5.            return {
  6.                spaceWidth:0,//空格宽度
  7.                spaceNum:0,//所需空格数量
  8.                opacity:0,//初始透明度为0,避免文案抖动
  9.                content:'王者荣耀游戏中的钻石用来做什么最合算?王者荣耀钻石用来干什么最好?在王者荣耀中钻石并不是唯一通用的货币,在游戏中还有金币和点券,小编个人觉得钻石在游戏中并没有其他两种货币有优势。'
  10.            };
  11.        },
  12.        computed:{
  13.                textStyle(){
  14.                  return {
  15.                    opacity:this.opacity
  16.                  }
  17.                }
  18.        },
  19.        methods:{
  20.            handleAppear(){
  21.                setTimeout(()=>{
  22.                    this.setTextContent();
  23.                },30)
  24.            },
  25.            async setTextContent(){
  26.                const text1El = this.$refs['demo-text1'];
  27.                const text2El = this.$refs['demo-text2'];
  28.                let textSize1,textSize2;
  29.                await this.getSpaceSize(text1El,(data)=>{
  30.                    textSize1 = data;
  31.                });
  32.                await this.getSpaceSize(text2El,(data)=>{
  33.                    textSize2 = data;
  34.                });
  35.  
  36.                this.spaceWidth=Math.abs(textSize1-textSize2)/10;
  37.                this.content=this.getSpaceNum();
  38.                this.opacity=1;
  39.  
  40.            },
  41.            getSpaceSize(el,callback){
  42.              return new Promise(function (resolve) {
  43.                  dom.getComponentRect(el, option => {
  44.                     if(option.result){
  45.                          resolve(callback(option.size.width));
  46.                     }
  47.                  });
  48.  
  49.              })
  50.            },
  51.            getSpaceNum(){
  52.              this.spaceNum = Math.ceil(120 / this.spaceWidth);//120为红色区块宽度
  53.              return new Array(this.spaceNum).join(' ')+ this.content;
  54.            }
  55.        }
  56.    }
  57. </script>
  58.  
场景二:图片位于段落末尾

很遗憾,目前这种特殊文本以及图片置于段落末尾并没有找到相应的解决方案,只能依赖终端添加相应的富文本功能。

组件

1、命名

组件命名应避免使用JS关键字和保留字,以及weex提供的组件名称,如用loading作为组件名称,在ios与android中将呈现空白。

错误示例:

  1. <template>
  2.    <div class="wrapper">
  3.        <Loading></Loading>/*改用其它名称*/
  4.    </div>
  5. </template>
  6. <script>
  7. import Loading from './demo'
  8.    export default {
  9.        components:{
  10.            Loading
  11.        }
  12.    }
  13. </script>    
  14.  

2、自定义slider组件

weex本身提供了slider组件,但轮播图指示器(indicator)只能修改颜色与位置,大小却无法更改,所以需要自定义slider组件

weex轮播图指示器效果:

电竞项目轮播图指示器效果

weex slider提供了change事件,可以获取到当前播放的序号,从而做到自定义轮播指示器。 但是当中遇到一个诡异问题:如果“div.indicator-item”的内容为空的话,H5中的指示器并不会随着图片切换而变化(样式不生效),div中需要添加内容才行

  1. <div class="indicator-item"  v-for="(item,index) in data" ...>
  2. {{index}}/*添加内容,解决H5中class切换样式不生效问题*/
  3. </div>
  4.  
完整结构如下:
  1. <template>
  2.    <div class="slider-container" :style="sliderStyle">
  3.        <slider class="slider" :interval="interval" @change="change" :auto-play="autoPlay">
  4.            <div class="slider-item" v-for="img in data" >
  5.                <image class="slider-image" :style="sliderStyle"  resize="cover" :src="img.src"></image>
  6.            </div>
  7.        </slider>
  8.        <div class="slider-indicator">
  9.            <div class="indicator-item"  v-for="(item,index) in data" :class="[current == index ? 'indicator-active' : '']">
  10.                {{index}}
  11.            </div>
  12.        </div>
  13.    </div>
  14. </template>
  15.  

动画

关键帧动画是很常见的一种动画,css3中可以利用@keyframes规则达到动画效果

css3: [cc lang="css"]

[/cc] 效果如图:

weex中提供了transition,可以传入相应的style,通过setInterval控制动画循环播放,但setInterval比较耗性能,建议终端对weex sdk进行改造,加入相应的循环播放功能

  1. <template>
  2.    <div class="wrapper">
  3.        <div class="demo" ref="demo"></div>
  4.    </div>
  5. </template>
  6.  
  7. <style scoped>
  8.    .demo{
  9.        width: 200px;
  10.        height: 200px;
  11.        background-color: gold;
  12.    }
  13. </style>
  14. <script>
  15. import * as animation from './animation.js'
  16.    export default {
  17.        mounted() {
  18.            setTimeout(()=>{
  19.                setInterval(() => {
  20.                    animation.run(this.$refs.demo);
  21.                }, 2100);
  22.            },200)
  23.        }
  24.    }
  25. </script>
  26.  
animation.js
  1. const animation = weex.requireModule('animation');
  2.  
  3. export function transition(el, opts,dd) {
  4.  let duration = dd || 400
  5.  if (!el) {
  6.    return Promise.resolve();
  7.  }
  8.  return new Promise(function (resolve) {
  9.      animation.transition(el, {
  10.          duration: duration,
  11.          timingFunction: 'linear',
  12.          delay: 0,
  13.          ...opts
  14.      }, resolve);
  15.  })
  16. }
  17.  
  18. export async function run(el, obj) {
  19.  
  20.  await transition(el, {
  21.      styles: {
  22.          backgroundColor: 'red',
  23.      }
  24.  },0.0001)
  25.  
  26.  await transition(el, {
  27.      styles: {
  28.          backgroundColor: 'purple',
  29.      }
  30.  },1000)
  31.  
  32.  await transition(el, {
  33.      styles: {
  34.          backgroundColor: 'lime',
  35.      }  
  36.  },1000)
  37.  
  38. }
  39.  

UI性能

安卓下打开“调试GPU过度绘制”选项,打开之后选择“显示过度区域绘制”后,会发现手机界面基本被蓝色,淡绿,淡红,深红所填充,这几种颜色代表了不同程度的绘制情况,其中蓝色绘制最少,而深红色绘制最多,可能会造成页面卡顿,应避免出现大面积红色区域。

优化建议:

1、尽量不要设置背景色

2、不要过度嵌套,结构尽量扁平化

最后

weex目前仍在不断的完善中,过程中遇到的问题将不断被修复,后续将持续更新。

欢迎关注"腾讯DeepOcean"微信公众号,每周为你推送前端、人工智能、SEO/ASO等领域相关的原创优质技术文章:

看小编搬运这么辛苦,关注一个呗:)

关注下面的标签,发现更多相似文章
评论