uni-app提升HTML5的性能体验系列之二 列表流畅滑动
uni-app提升HTML5的性能体验系列之二 列表流畅滑动
本系列文章是针对5+App的,与uni-app无关
系列文章目录导航:
- 提升HTML5的性能体验系列之一 避免切页白屏
- 提升HTML5的性能体验系列之二 列表流畅滑动
- 提升HTML5的性能体验系列之三 流畅下拉刷新
- 提升HTML5的性能体验系列之四 使用原生UI(nativeUI)
- 提升HTML5的性能体验系列之五 webview启动速度优化及事件顺序解析
- 提升HTML5的性能体验系列之六 降低内存占用
App的顶部一般有titlebar,下面是list。
常见的一个需求是要在list滚动时,titlebar不动。
这个简单的需求,但在HTML世界里实现起来其实并不简单。因为普通的HTML方式做不到60fps的无掉帧平滑滚动。
在pc web上的做法是使用div的区域滚动,把list放到div里。
但div滚动条在Android4.4以下的手机上不可商用,如果dom复杂,不管多高端的Android手机,滚动效果也惨不忍睹。
还有一种做法是把titlebar使用fix来固顶。
但此时手机浏览器的滚动条会通到顶部,当titlebar的右侧出现滚动条时,看起来非常怪异。
还有一个副作用是滚动太快时,titlebar的div会下来一下又弹上去。这是因为滚动时触发页面重绘,dom很复杂时重绘来不及,绝对定位的titlebar就会先下来后上去。
还有一种方法是使用自绘的滚动条,即不用浏览器的滚动条,自己画一个滚动条,iScroll等方案是采取这种思路。但模拟的始终是模拟的,和原生的滚动条效果还是差很多,在列表头和尾的拉动回弹的表现不佳,而且列表长了、内容复杂了,非常卡顿。
想要解决这个问题其实也不难,既然div的滚动条卡,而浏览器body的整体滚动条不卡,那就老老实实使用浏览器的body滚动。至于title区域的滚动条通顶和固定方案,使用原生title来解决。
HBuilder8.0起,支持了原生的titleNView,创建webview对象时,在style参数里配置titleNView就可以在Webview上加一个原生的title。
plus.webview.create('new.html', 'new', {'titleNView':{'backgroundcolor':'#FFFFFF','titletext':'标题栏','titlecolor':'#FF0000'}});
HTML页面的滚动部分会自动在这个原生title下面。
titleNView的具体api见,功能还是比较丰富的:http://html5plus.org/doc/zh_cn/webview.html#plus.webview.WebviewTitleNViewStyles。
titleNView属于Webview的style,自然也可以通过Webview的setstyle方法来修改titleNView的参数,比如用js动态修改titleNView上的文字,可以使用setstyle方法给titleNView重新配置参数即可。修改是增量的,不是全量重写。
提到原生封装的title,人们的直觉一般是自定义性差。
但其实不然,DCloud始终重视开发者的自定义性。
titleNView的父类是plus.nativeObj.view,这是一块原生画布,可以自由的在上面写字绘图、设编辑框,拥有良好的自定义性。
通过Webview的getTitleNView()方法,可以得到标题栏的NView对象,然后参考nview的api,自由定制界面。http://html5plus.org/doc/zh_cn/nativeobj.html#plus.nativeObj.View
当然90%的使用场景,在titleNView自带的封装参数里就能完成。个别情况下开发者才需要研究nview的api手绘界面。
配合nativeObj下面的原生imageslider,还可以实现沉浸式题图效果,题图开始通顶到状态栏上,在向下滚动后渐变消失,而title渐变出现,非常动感。
关于沉浸式,是另外的话题,此处不展开。有兴趣可参考http://ask.dcloud.net.cn/article/1150
使用titleNView还有一个好处,在新窗体动画时,不管内容页面渲染多么慢,原生title在动画期间一定是渲染好的,这样页面不会整屏白屏。
mui对这个效果进行了封装,使用mui.openWindowWithTitle即可打开一个带原生头的webview。
在Hello mui里nav bar中点“native title”,可看到效果和源码。
已过期历史:在HBuilder8.0以前,官方曾推荐过双Webview式title。
即把屏幕切分成2个webview,把titlebar的HTML装载到一个webview里,list页面是另一个webview,list页面直接使用浏览器的滚动条而不是div滚动条,这样也可以得到一个流畅的列表滑动体验。
在mui框架里,通过mui的init里的subpage,可以通过配置而不是编码的方式更加简单的开发这种双webview界面。
但在nview成熟后,官方已经废弃了双Webview模式。目前是不再主推双Webview,但开发者的老代码仍然可以兼容。
关于bounce回弹效果
HTML5+提供了原生的bounce回弹效果,默认是开启的。
但也可以关闭和进行参数设置,具体见http://html5plus.org/doc/zh_cn/webview.html#plus.webview.WebviewBounceStyle
如果你的App滚动不流畅,可能的原因:
- 有人仍然使用div滚动然后抱怨滚动不流畅,注意检查自己的代码,一定要用body滚动而不是div滚动。
mui为了多端发布兼容和部分选择器项目滚动,是提供了div滚动的,尤其是在iOS上因为性能较高使用面更大,但这些都是可配置的,觉得不流畅时注意检查配置是不是变成div滚动了。 - 不管是mui提供的div滚动还是plus.webview的body滚动,都是可以设滚动加速度值的,可以根据自己的页面情况设置不同的加速度值。默认其实不用设置,但如果你设置了错误的值,请检查并修复。
- 你可能关闭了硬件加速。Android上如果不开启硬件加速,很难做到60fps的平滑滚动。具体参考Android硬件加速详解
- 可能是因为js监听了滚动。js监听滚动,并实时计算和修改dom,会导致卡顿。如果你需要做下拉到底自动翻下一页,那么只需要监听HTML5+提供的页面到底事件,而不是任意滚动事件都监听。具体参考提升HTML5的性能体验系列之三 流畅下拉刷新
在提升uni-app中HTML5页面的列表流畅滑动体验方面,关键在于优化渲染性能、减少重排和重绘、以及合理使用内存。以下是一些通过代码实现性能优化的示例:
1. 使用虚拟列表(Virtual List)
虚拟列表是一种只渲染可视区域内数据项的技术,可以大大减少DOM节点的数量,从而提升滑动性能。
<template>
<view class="container">
<scroll-view scroll-y="true" :scroll-into-view="toView">
<view v-for="(item, index) in visibleItems" :key="item.id" :id="'item-' + index" class="item">
{{ item.text }}
</view>
<!-- 占位元素,保证滚动高度正确 -->
<view v-for="i in placeholderCount" :key="i" class="placeholder"></view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
items: Array.from({ length: 1000 }, (_, i) => ({ id: i, text: `Item ${i}` })),
startIndex: 0,
endIndex: 20, // 可视区域显示的项数
};
},
computed: {
visibleItems() {
return this.items.slice(this.startIndex, this.endIndex);
},
placeholderCount() {
return this.items.length - (this.endIndex - this.startIndex);
},
toView() {
return `item-${this.startIndex}`;
}
},
methods: {
onScroll(e) {
const scrollTop = e.detail.scrollTop;
const itemHeight = 50; // 假设每项高度固定
this.startIndex = Math.floor(scrollTop / itemHeight);
this.endIndex = this.startIndex + 20;
}
}
};
</script>
<style>
.container {
height: 100vh;
}
.item, .placeholder {
height: 50px;
}
.placeholder {
visibility: hidden;
}
</style>
2. 使用requestAnimationFrame
进行动画控制
requestAnimationFrame
可以确保动画的流畅性,因为它与浏览器的刷新频率同步。
export default {
data() {
return {
scrollY: 0
};
},
methods: {
onScroll(e) {
cancelAnimationFrame(this.frameId);
this.frameId = requestAnimationFrame(() => {
this.scrollY = e.detail.scrollTop;
// 其他需要在滚动时执行的逻辑
});
}
},
beforeDestroy() {
cancelAnimationFrame(this.frameId);
}
};
3. 懒加载图片
懒加载图片可以推迟加载不在视口内的图片,减少初始加载时间和内存占用。
<image v-if="inViewport(index)" :src="item.image" class="item-image" />
methods: {
inViewport(index) {
const rect = this.$refs[`item-${index}`][0].getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
}
通过上述方法,你可以有效提升uni-app中HTML5页面的列表流畅滑动体验。