自定义滚动条组件
场景:在微信小程序开发中,默认的滚动条已经非常完美了,不管是scroll-view还是默认的。但是还是会有些情况会使用到一些定制的滚动条,所以我就稍稍学习整理了一下,关于我对自定义滚动条的理解思路。
实例:
就像上面的那种,当我们滚动的时候,需要有一个标识块,用于表示当前滚动的位置。
废话不多说,上代码---
页面
思路 - 布局,样式什么的我就不说了,主要讲一下思路
通过获取容器宽度,总宽度,滚动的距离
利用计算 滚动距离 / (总宽度 - 容器宽度)可以获取滚动的比例 (0 ~ 1)
因为不管怎么说,总宽度 - 容器宽度 总是等于最大滚动距离的也就是说,当我不滚动的时候,滚动距离比例是0,当我滚动最大的时候,滚动距离比例是1
所以我们就可以根据这个比例进行操作了,只不过比例计算我是放到组件里去了。
demo
<!--demo.wxss - 这里我使用的是微信小程序的scroll-view组件 -->
<view class="scroll_wrap">
<scroll-view scroll-x style="overflow: auto;" bindscroll="onscroll" id="scroll">
<view style="width: calc({{bagWidth}} * {{assets.length}});white-space: nowrap;" id="scroll_wrap">
<view wx:for="{{assets}}" wx:key="id" class="item" style="width: {{bagWidth}}px;">
<view class="price flex flex-justify-center flex-items-center margin-auto">{{item.number}}</view>
<view class="title">
<view>{{item.title}}</view>
<view>{{item.remark}}</view>
</view>
</view>
</view>
</scroll-view>
</view>
// 上面用到一些flex,flex-justify-center。。。不用在意,相信也都明白,这是全局样式设置的,类似于bootstrap,这里就不多介绍了。
demo.js
Page({
data: {
bagWidth:0,
// 数据
assets:[
{id:1,number:"1",title:"优惠券",remark:""},
{id:2,number:"4.79万",title:"白条分分卡",remark:"额度待领取"},
{id:3,number:"0",title:"京豆",remark:""},
{id:4,number:"0",title:"红包",remark:""},
{id:5,number:"20.00万",title:"金条借款",remark:"白用30天"},
],
// 传入组件参数
scrollObj:{
scrollWidth:String,
totalWidth:String,
scrollLeft:String,
}
},
// 滚动距离
onscroll(e){
const that = this
wx.createSelectorQuery().select('#scroll').boundingClientRect(function(res){
// 容器宽度
let scrollWidth = res.width
// 总宽度 = 块宽度 * 数量
let cardWidth = that.data.bagWidth
let number = that.data.assets.length
let totalWidth = cardWidth * number
// 滚动距离
let scrollLeft = e.detail.scrollLeft
let scrollObj = {
scrollWidth,
totalWidth,
scrollLeft
}
that.setData({scrollObj})
}).exec()
},
/**
* 生命周期函数--监听页面加载
*/
/* 这一步等会介绍 */
onLoad(options) {
const that = this
// 获取bag宽度
wx.createSelectorQuery().select('#bag').boundingClientRect(function(res){
let bagWidth = res.width
that.setData({bagWidth})
}).exec()
},
})
// 思路:
// 1.利用wx.createSelectorQuery()方法获取容器宽度
// 2.通过计算出总宽度
// 3.利用scroll-view组件中自带的bindscroll事件获取滚动的左边距
demo.wxss
/* 模块3开始 */
.personal_module3{
background-color: #F6F6F6;
/* height: 200px; */
}
/* 全部资产容器 */
.personal_module3 .assets{
background-color: #fff;
display: flex;
justify-content: space-between;
}
.personal_module3 .scroll_wrap{
width: 80%;
}
.personal_module3 .item{
display: inline-block;
text-align: center;
padding-bottom: 30rpx;
}
.personal_module3 .item .price{
width: 26px;
height: 26px;
font-size: 16px;
font-weight: 700;
}
.personal_module3 .item .title{
position: relative;
}
.personal_module3 .item .title>view:nth-of-type(1){
font-size: 11px;
}
.personal_module3 .item .title>view:nth-of-type(2){
position: absolute;
left: 50%;
transform: translateX(-50%);
z-index: 1;
bottom: -26rpx;
font-size: 10px;
color: #999;
}
.personal_module3 .money_bag{
width: 20%;
display: flex;
flex-direction: column;
align-items: center;
}
.personal_module3 .money_bag>view:nth-of-type(1){
width: 26px;
height: 26px;
}
.scroll_bar{
background-color: #999;
height: 10px;
width: 20%;
margin: auto;
}
.scroll_position{
background-color: #f00;
width: 30%;
height: 10px;
}
/* 模块3结束 */
demo.json - 使用组件
{
"usingComponents": {
"progress-bar":"/components/progress-bar/progress-bar"
}
}
组件 - progress-bar
组件思路很简单
设定好两个块,一个块是背景,另一个块表示进度位置
根据传入的参数,计算出当前滚动的比例
然后根据比例移动内部块的位置
js
// components/progress-bar/progress-bar.js
Component({
properties: {
message:{
type:Object,
value:{}
}
},
// 监听参数变化
observers:{
'message':function(newVal,oldVal){
let {scrollLeft , scrollWidth , totalWidth} = newVal
// 滚动比例 = 滚动距离 / (总宽度 - 容器宽度)
let result = scrollLeft / (totalWidth - scrollWidth)
if(!isNaN(result)){
this.setData({result})
}
}
},
data: {
result:0
},
})
json
{
"component": true,
"usingComponents": {}
}
wxml
<!--components/progress-bar/progress-bar.wxml-->
<view class="wrap">
<view class="opsition" style="margin-left: calc(35rpx * {{result}});"></view>
</view>
wxss
/* components/progress-bar/progress-bar.wxss */
.wrap{
width: 60rpx;
height:10rpx;
background-color: #ccc;
border-radius: 20px;
overflow: hidden;
}
.opsition{
width: 25rpx;
height: 10rpx;
background-color: #f00;
border-radius: 20px;
}
成品:
讲解全部代码
页面
<!-- 模块3 全部资产 --> <view class="personal_module3 px pt pb"> // 外围容器 没有给固定宽高 但是设置了flex布局 <view class="assets border-r-m py flex flex-items-center"> <view class="scroll_wrap"> <scroll-view scroll-x style="overflow: auto;" bindscroll="onscroll" id="scroll"> // bagWidth就是“全部资产”的宽度 <view style="width: calc({{bagWidth}} * {{assets.length}});white-space: nowrap;" id="scroll_wrap"> <view wx:for="{{assets}}" wx:key="id" class="item" style="width: {{bagWidth}}px;"> <view class="price flex flex-justify-center flex-items-center margin-auto">{{item.number}}</view> <view class="title"> <view>{{item.title}}</view> <view>{{item.remark}}</view> </view> </view> </view> </scroll-view> </view> // 设置宽度为容器宽度的20%,确保显示部分保证为五个块 <view class="money_bag" id="bag"> <view><image style="height: 100%;width: 100%;" src="/static/personal/images/钱包.png" mode=""/></view> <view class="mt-1">全部资产</view> </view> </view> // 使用组件 <view> <progress-bar message="{{scrollObj}}"></progress-bar> </view> </view>wxss
/* 模块3开始 */ .personal_module3{ background-color: #F6F6F6; /* height: 200px; */ } /* 全部资产容器 */ .personal_module3 .assets{ background-color: #fff; display: flex; justify-content: space-between; } .personal_module3 .scroll_wrap{ width: 80%; } .personal_module3 .item{ display: inline-block; text-align: center; padding-bottom: 30rpx; } .personal_module3 .item .price{ width: 26px; height: 26px; font-size: 16px; font-weight: 700; } .personal_module3 .item .title{ position: relative; } .personal_module3 .item .title>view:nth-of-type(1){ font-size: 11px; } .personal_module3 .item .title>view:nth-of-type(2){ position: absolute; left: 50%; transform: translateX(-50%); z-index: 1; bottom: -26rpx; font-size: 10px; color: #999; } .personal_module3 .money_bag{ width: 20%; display: flex; flex-direction: column; align-items: center; } .personal_module3 .money_bag>view:nth-of-type(1){ width: 26px; height: 26px; } .scroll_bar{ background-color: #999; height: 10px; width: 20%; margin: auto; } .scroll_position{ background-color: #f00; width: 30%; height: 10px; } /* 模块3结束 */js
Page({ data: { bagWidth:0, // 数据 assets:[ {id:1,number:"1",title:"优惠券",remark:""}, {id:2,number:"4.79万",title:"白条分分卡",remark:"额度待领取"}, {id:3,number:"0",title:"京豆",remark:""}, {id:4,number:"0",title:"红包",remark:""}, {id:5,number:"20.00万",title:"金条借款",remark:"白用30天"}, ], // 传入组件参数 scrollObj:{ scrollWidth:String, totalWidth:String, scrollLeft:String, } }, // 滚动距离 onscroll(e){ const that = this wx.createSelectorQuery().select('#scroll').boundingClientRect(function(res){ // 容器宽度 let scrollWidth = res.width // 总宽度 = 块宽度 * 数量 let cardWidth = that.data.bagWidth // 块宽度 let number = that.data.assets.length // scroll里面的块数量 let totalWidth = cardWidth * number // scroll 整体宽度 // 滚动距离 let scrollLeft = e.detail.scrollLeft // 保存并当作参数传递给组件 let scrollObj = { scrollWidth, totalWidth, scrollLeft } that.setData({scrollObj}) }).exec() }, /** * 生命周期函数--监听页面加载 */ /* 页面加载,获取“全部资产”宽度,适用于所有设备 */ onLoad(options) { const that = this // 获取“全部资产”宽度,保存到bagWidth中 wx.createSelectorQuery().select('#bag').boundingClientRect(function(res){ let bagWidth = res.width that.setData({bagWidth}) }).exec() }, })
组件
组件代码其实没什么可说的,如果硬要说的话,其实就一点
很多人可能不明白
35rpx怎么回事<view class="wrap"> <view class="opsition" style="margin-left: calc(35rpx * {{result}});"></view> </view>这里我要说一下,35rpx不是随便来的
35rpx = 60rpx - 25rpx - 是个人都知道
但是:60rpx是wrap(外部容器)的宽度,而25rpx则是opsition(内部容器)宽度
这样一说,答案显而易见35rpx是空白部分的宽度
有人可能会问,我们要移动内部容器的位置,为什么要计算空白部分的宽度
这里就要说了,因为我们计算的滚动比例是从0-1的,而空白部分就恰恰符合我们的要求,当移动比例是0的时候,移动位置 = 35rpx * 0,位置不动,当滚动比例发生变化,最大到1的时候,我们的空白部分就是35rpx * 1 = 35rpx , 则我们的块刚好是25rpx,加一起刚好又是60rpx,多么巧合(当然不是啦)!是我们通过计算得来的啦。
PS:如果有什么疑问,欢迎到评论区提出!