在nvue中使用BindingX实现高性能滚动条

四糸乃赛高 Lv.5 冰封大地
48 2021/6/30笔记

场景:nvue中的list组件没有滚动条,自己写了一个,实现方法:通过监听容器的滚动然后使用绝对定位改变滚动条的y值,但是列表在加载时的一些操作会阻塞线程,导致滚动条不能及时滚动到相应位置。

实现代码:

template:

<view class="scrollBar" :style="{height:`${appHeight}px`}">
    <view class="bar" :style="{top:`${scrollBarY}px`></view>
</view>

script 监听滚动事件片段:

// listHeight为列表的总高度,appHeight为显示区域的高度
handleScroll(e){
    let y = Math.abs(e.contentOffset.y);
    this.scrollBarY = y / this.listHeight * this.appHeight;
}

第一个想到的解决方法是使用Worker,把加载的操作放到其他线程去执行,然后去看乐一下文档

App:App的js是在独立的jscore运行的,如果需要在另一个线程运行js,可以使用web-view组件或renderjs,这样的js运行在webview里,和jscore里的js是两个线程。但注意多个webview之间的js是一个进程,使用webview里的js时注意会影响视图层的渲染。

虽然App里不能使用Worker,但得到一个非常有用的信息,主线程和视图层是不同的线程,那么问题就好解决了,让改变视图的操作在视图层运行就好了。

关于WXS的介绍:https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxs/

看了一下renderjs,他与WXS类似,都能降低逻辑层和视图层的通讯损耗,提供高性能视图交互能力。

官方的描述:renderjs是一个运行在视图层的js。它比WXS更加强大。它只支持app-vue和h5。

不支持nvue,然后去找WXS的文档,也不支持nvue,但是得到了其他的解决方案:App端nvue解决此类需求,不应该使用wxs,而是使用bindingx。

uni-app 是逻辑层和视图层分离的。此时会产生两层通信成本。比如拖动视图层的元素,如果在逻辑层不停接收事件,因为通信损耗会产生不顺滑的体验。BindingX 是weex提供的一种预描述交互语法。由原生解析BindingX规则,按此规则处理视图层的交互和动效。不再实时去js逻辑层运行和通信。BindingX类似一种强化版的css,运行性能高,但没有js那样足够强的编程灵活性。uni-app 内置了 BindingX,可在 nvue 中使用 BindingX 完成复杂的动画效果。

然后找到BindingX官方文档:https://alibaba.github.io/bindingx/guide/cn_introduce

BindingX 的核心思想就是将"交互行为"以表达式的方式描述,并提前预置到Native,避免在行为触发时JS与native的频繁通信。

一句话概括使用方法:给指定DOM绑定需要监听的事件(pan/timing/scroll/orientation),通过表达式改变指定DOM的样式。

uniapp官方文档中的案例:https://uniapp.dcloud.io/nvue-api?id=nvue-%e9%87%8c%e4%bd%bf%e7%94%a8-bindingx


最后结合在BindingX文档中的内容,改变场景中滚动条的实现方式,完整代码:

<template>
	<view class="wrapper">
		<view class="scrollBar" :style="{height:`${barWrapHeight}px`}">
			<view id="bar" class="bar" :style="{height:`${barHeight}px`}" ref="scrollBar" @touchmove="barMove" @touchstart="barClick" ></view>
		</view>
		<list id="list" class="list" ref="list" :style="{height:`${appHeight}px`}" :show-scrollbar="false">
			<cell class="item" v-for="i in len" :key="i" :ref="`item${i}`">
				<text>{{ i }}</text>
			</cell>
		</list>
		
	</view>
</template>

<script>
	const Binding = uni.requireNativePlugin('bindingx')
	const dom = uni.requireNativePlugin('dom')
	export default {
		data() {
			return {
				len:100, // 列表的长度
				barHeight:60, // 滚动条的高度
				barWrapHeight:0, // 滚动条容器的高度
				listHeight:0, // 列表的高度 包括可滚动区域
				scrollBarD:0, 
			}
		},
		mounted() {
			this.barWrapHeight = this.appHeight;
			this.listHeight = 100 * this.len;
			this.$nextTick(()=>{
				let barWrapHeight = this.barWrapHeight - this.barHeight,
					scrollHeight = this.listHeight - this.appHeight;
				let bar = this.getEl(this.$refs.scrollBar)
				this.binding()
			})
		},
		methods: {
			binding(){
				let barWrapHeight = this.barWrapHeight - this.barHeight,
					scrollHeight = this.listHeight - this.appHeight;
				let bar = this.getEl(this.$refs.scrollBar)
				// 监听容器的滚动
				this.scrollToken = Binding.bind({
					anchor: this.getEl(this.$refs.list),
					eventType:'scroll',
					props:[
						{
							element: bar,
							property: 'transform.translateY',
							expression: `y / ${scrollHeight} * (${barWrapHeight})`
						}
					]
				})
			},
			getEl(el) {
				if (typeof el === 'string' || typeof el === 'number') return el;
				if (WXEnvironment) {
					return el.ref;
				} else {
					return el instanceof HTMLElement ? el : el.$el;
				}
			},
			barClick(e) {
				let query = uni.createSelectorQuery().in(this);
				query.select('#bar').boundingClientRect(data=>{
					this.scrollBarD = e.touches[0].screenY - data.top;
				}).exec()
			},
			barMove(e) {
				let top = e.touches[0].screenY - this.scrollBarD; // 鼠标移动到的位置
				if (top >= 0 && top <= this.barWrapHeight) {
					let index = parseInt(top / this.barWrapHeight * this.len);
					this.scrollList(index)
				}
			},
			scrollList(index){
				if(this.wait) return;
				else{
					this.wait = true;
					setTimeout(()=>{
						this.wait = false;
					},100)
					dom.scrollToElement(this.$refs[`item${index}`][0],{})
				}
			}
		},
		computed:{
			appHeight(){
				return this.$store.state.appHeight;
			}
		}
	}
</script>

<style>
.item{
	color: white;
	height: 100px;
	background-color: #007AFF;
	align-items: center;
	justify-content: center;
}
.scrollBar {
	position: fixed;
	right: 0;
	top: 0;
	z-index: 10;
	width: 20px;
}
.bar {
	position: absolute;
	background-color: #232323;
	width: 15px;
	right: 3px;
	border-radius: 20px;
}
</style>


# uni-app

评论

后参与评论
还没有评论,来说点什么吧~