[微信小程序]实现文字上下无缝滚动
UPDATE 2019/07/16:
下文中,data
的duration
不要setData
,因为页面并没有引用这个变量,setData
中会浪费性能,直接赋值就行了:this.data.duration = 0;
。如果是在Page
中,可在Page
的配置中自定义一个变量,但在组件中是不行的。文中用的是组件。
一、前言和效果图
可能方法比较原始,如果各位大神有高大上的方法,请不吝赐教。
效果图(可能录屏软件帧数低,实际是没有卡顿和跳动的。动画起始和结束位置都是2):
二、难点分析
获取相关元素的尺寸
小程序中和DOM打交道是费力的,如何布局并获取DOM尺寸,是一个难点。
实现无缝滚动
同样,由于DOM样式的改变都是由数据驱动的,不能像以前那样直接改变DOM,所以这一点也需要思考一下。
三、实现
1、布局
首先,需要一个overflow: hidden
的容器,然后需要一个盛放列表的容器,这个容器的高度会随着列表的数量变化。
<view class='scroll-box'>
<view class='item-box'>
<view class='item'>4每周精选上新:本周精选欢迎采购!</view>
......
</view>
</view>
这样的结构已经可以实现滚动了,但是,每一个item
的高度要设置和scroll-box
一样,所以文字就会从scroll-box
最底部出现,滚动到最上面,然后消失,这样不太美观,所以再加一层容器,设置合适的margin:
<view class='scroll-box'>
<view class='item-box'>
<view class='scroll'>
<view class='item'>1每周精选上新:本周精选欢迎采购!</view>
......
</view>
</view>
</view>
这样,最外层可以自由设置宽高,文字的滚动区域,只要设置item-box
就好了。下面是样式:
.scroll-box {
overflow: hidden;
height: 92rpx;
/*设置小图标*/
background: url(icon.png) no-repeat 28rpx center/31rpx 25rpx;
}
.item-box {
overflow: hidden;
position: relative;
height: 40rpx;
/*根据需要设置合适的边距,左边距88rpx是因为左侧有个小图标*/
margin: 26rpx 28rpx 0 88rpx;
}
.scroll {
/*实现定位*/
position: absolute;
left: 0;
top: 0;
right: 0;
/*动画由css3完成*/
transition: all 0.24s;
}
.item {
height: 40rpx;
line-height: 40rpx;
font-size: 24rpx;
}
下面来用js实现滚动。原理是:
首先,获取到列表总高度和项目的高度。虽然在wxss中给.item-box
和.item
设置了高度,但那是以rpx
为单位的,实际在wxml
中,还是以px
为单位的。
然后,用计时器定时改变.item-box
的top
,配合设置的transition
,就实现了向上滚动。
最后,在滚动到最后一项后,返回第一项。
这样的问题是,没法实现无缝滚动。回想在用jQuery的时代,实现无缝滚动的方法是:复制列表最后一项,追加到列表的最前面。当列表滚动到最后一项时,立刻把列表定位到第一项。因为第一项和最后一项是相同的,所以用户看不到这个切换的过程。然后再继续重复这个过程就行了。
在小程序中我们同样这样做。这里,列表项就用静态数据了,注意.item
的顺序:
<view class='scroll-box'>
<view class='item-box'>
<view class='scroll'>
<view class='item'>4每周精选上新:本周精选欢迎采购!</view>
<view class='item'>1每周精选上新:本周精选欢迎采购!</view>
<view class='item'>2每周精选上新:本周精选欢迎采购!</view>
<view class='item'>3每周精选上新:本周精选欢迎采购!</view>
<view class='item'>4每周精选上新:本周精选欢迎采购!</view>
</view>
</view>
</view>
考虑到1、从最后一项定位到第一项,不需要动画效果;2、这个切换要立刻完成,不能占用一个定时器周期,所以需要定义一个变量,来给.scroll
增加/去除动画,还要定义一个变量,用来改变定时器的等待时间。
改变的css如下:
.scroll {
position: absolute;
left: 0;
top: 0;
right: 0;
}
/*去掉.scroll的transition,放到单独的类中*/
.scroll.trans {
transition: all 0.24s;
}
js代码如下:
Component({
properties: {
},
data: {
// 给.scroll增删动画效果
trans: 'trans',
// 定位.scroll,初始在第2条.item的位置(因为第一条和最后一条相同)
//这里省略了计算过程,给了经过计算的scrollBoxItemHeight值
top: 20,
// 记录.scroll的高度
scrollBoxItemHeight: null,
// 记录.item的高度
scrollBoxHeight: null,
// .item的条数
itemCount: 5,
timer: null,
// 计时器的等待时间
duration: 4000
},
methods: {
// 无缝滚动,要复制列表最后一项,追加为列表第一项,使第一项和最后一项相同
// 当滚动到最后一项时,去掉css3动画,定位到第一项,设置计时器的等待时间为0
_scroll() {
let _this = this; // 本网站箭头函数的语法高亮有问题...
this.data.timer = setTimeout(function(){
// 滚动到最后一条后,去掉动画,改变duration为0,立刻定位到第一条
if (_this.data.top >= _this.data.scrollBoxHeight) {
_this.setData({
trans: '',
top: 0,
duration: 0
}, function(){
// 继续
_this._scroll();
});
} else {
_this.data.top += 20;
_this.setData({
trans: 'trans',
top: this.data.top,
duration: 4000
}, function(){
_this._scroll();
});
}
}, _this.data.duration);
}
},
ready() {
let _this = this;
// 获取高度,用法请查询文档
this.createSelectorQuery().select('.scroll .item').boundingClientRect(function(r) {
_this.data.scrollBoxItemHeight = r.height;
_this.data.scrollBoxHeight = (_this.data.itemCount - 1) * r.height;
_this._scroll();
}).exec();
}
})
最后,给wxml绑定相关数据:
......
<view class='scroll {{trans}}' style='top:{{-top}}px'>
......
以上就实现了文字无缝垂直滚动。