小程序监听滚动时,更优雅的修改页面样式
场景
在项目中有一个需求,监听页面滚动,动态修改组件的背景色,之前是在监听以后,计算颜色,通过
this.setData({
bgColor: newBgColor
})
方式修改背景色,但是在快速滚动情况下,会疯狂修改,一直调用setData()
方法,性能很糟糕,会出现卡顿情况
核心就是减少setData()
方法调用
解决思路
方案1
可以使用函数节流
,防止调用太多,减少调用频率
方案2
具体分析,我们只是单纯的修改样式,不涉及逻辑,可以将修改的样式的动作放到渲染层去处理,不在通过逻辑层,直接将setData()
次数减低到零
- 要点
- 小程序的运行环境分成渲染层和逻辑层
- 在渲染层,可以监听事件,修改元素的样式
实现
方案1
定义一个函数节流工具
/**
* @description 函数节流: 每隔一段时间,只执行一次函数
* @param { Function } fn 需要延迟执行的函数
* @param { Number } interval 延迟执行的时间,默认值500ms
*/
export function throttle(fn, interval = 500) {
// 记录定时器id
let timer = null
// 是否是第一次调用
let isFirstTime = true
return function () {
const args = arguments
const _me = this
// 第一次直接执行,改变标志,无需延迟
if (isFirstTime) {
fn.apply(_me, args)
return (isFirstTime = false)
}
// 不是第一次
// 存在定时器,前面的延迟操作没有完成,直接返回,拒绝调用请求
if (timer) {
return false
}
// 延迟执行
timer = setTimeout(() => {
clearTimeout(timer)
timer = null
fn.apply(_me, args)
}, interval)
}
}
页面中使用节流函数
// index.js
import { throttle } from '../utils/util';
// 创建一个长度100的数组
const list = [...new Array(100).keys()].map(i => {
return { name: 'lisr' + (i + 1), age: i + 1}
})
Page({
data: {
list: list
},
onScroll: throttle(function(e) {
console.log(e)
const { scrollTop: top } = e.detail
let channelColor = top * 1 // 任意通道颜色
if(channelColor > 255) {
channelColor = channelColor%255
}
// 样式
const style = `background-color: rgb(${channelColor}, ${channelColor},${channelColor});`
this.setData({
style // 设置背景色
}, () => {
this.countStatistics()
})
}, 300),
// 调用次数
count: 0,
// 更新调用次数和并打印出来
countStatistics() {
++this.count
console.log(this.count)
}
})
页面结构
<!-- index.html -->
<!-- 一个大盒子包住所有的元素 -->
<view class="container-fill">
<!-- 包住 swiper 组件 -->
<view class="scroll-fullpage" style="height: 100%">
<scroll-view scroll-y="true" style="height: 100%;" bindscroll="onScroll">
<view class="item" hover-class="none" hover-stop-propagation="false" style="{{style}}" wx:for="{{ list }}" wx:key="index">
{{item.name}} - {{item.age}}
</view>
</scroll-view>
</view>
</view>
页面样式
/* index.wxss */
.container-fill {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
overflow: hidden;
z-index: 10;
}
.container-fill .scroll-fullpage {
height: 100%;
transition: all 0.3s;
}
.scroll-fullpage .item {
height: 30%;
width: 80%;
background-color: skyblue;
border-radius: 10rpx;
margin: 50rpx auto;
display: flex;
justify-content: center;
align-items: center;
font-size: 50rpx;
}
演示效果 TODO
方案2
定义wxs, 用于修改元素的样式
// 调用次数
var count = 0
function handleScroll(e, ownerInstance) {
var top = e.detail.scrollTop
var channelColor = top * 1 // 任意通道颜色
if (channelColor > 255) {
channelColor = channelColor % 255
}
var instances = ownerInstance.selectAllComponents('.item') // 获取类名为item的组件
for(var i = 0; i < instances.length; i++) {
var instance = instances[i]
instance.setStyle({
'background-color':
'rgb(' + channelColor + ',' + channelColor + ',' + channelColor + ')', // 设置背景色
})
}
countStatistics()
}
// 更新调用次数和并打印出来
function countStatistics() {
++count
console.log(count)
}
module.exports = {
handleScroll: handleScroll
}
页面逻辑层代码
// 创建一个长度100的数组
const list = [...new Array(100).keys()].map(i => {
return { name: 'lisr' + (i + 1), age: i + 1}
})
Page({
data: {
list: list
}
})
页面渲染层
- 需要引入写好的wxs模块
<wxs module="module" src="./index.wxs"></wxs>
- 监听滚动事件,在里面直接修改元素样式
<scroll-view scroll-y="true" style="height: 100%;" bindscroll="{{module.handleScroll}}">
注意引入模块使用{{}}
包裹
完整代码如下:
<!-- index.html -->
<wxs module="module" src="./index.wxs"></wxs>
<!-- 一个大盒子包住所有的元素 -->
<view class="container-fill">
<!-- 包住 swiper 组件 -->
<view class="scroll-fullpage" style="height: 100%">
<scroll-view scroll-y="true" style="height: 100%;" bindscroll="{{module.handleScroll}}">
<view class="item" hover-class="none" hover-stop-propagation="false" style="{{style}}" wx:for="{{ list }}" wx:key="index">
{{item.name}} - {{item.age}}
</view>
</scroll-view>
</view>
</view>
页面样式
/* index.wxss */
.container-fill {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
overflow: hidden;
z-index: 10;
}
.container-fill .scroll-fullpage {
height: 100%;
transition: all 0.3s;
}
.scroll-fullpage .item {
height: 30%;
width: 80%;
background-color: skyblue;
border-radius: 10rpx;
margin: 50rpx auto;
display: flex;
justify-content: center;
align-items: center;
font-size: 50rpx;
}
转载自:https://juejin.cn/post/6845166890659086349