这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战
如下图,要实现仿照keep的一个可以切换日周月总时间维度的滑动轮播图,默认展示最后一条数据选中效果,分页展示,向左滑动展示历史日期的数据。看起来很炫酷,实现过程中也遇到了很多困难,记录一下,温故而知新!
第一次看到这个UI图 的时候,觉得不复杂,但是后面实现起来还是遇到了很多的困难。 首先要实现时间维度左右滑动,其次是柱子的滑动,最难点是和以往的分页顺序不同,这个是倒序排列,最新日期在第一页展示,如下图所示
1.时间选择维度
1.1 父组件wxml
- swiper实现时间维度的切换和点击
- van-tabs 的active实现选中效果
- 引入滑动组件,父子传值
<!-- 时间维度切换 -->
<view class="record-bg">
<view class="exe-name" bindtap="showPick">
<text class="txt-overflow">{{exeName}}</text>
<text class="iconfont iconzhankai"></text>
</view>
<van-tabs active="{{active}}" bind:change="onChange" data-flag="0">
<van-tab title="日"></van-tab>
<van-tab title="周"></van-tab>
<van-tab title="月"></van-tab>
<van-tab title="总"></van-tab>
</van-tabs>
</view>
<!-- 引入滑动组件-->
<slide-bar-chart chartData="{{chartYData}}" xName="name" yName="data" bindcurrentBarChange="currentBarChange" chartAllData="{{chartData}}" pageSize="{{pageSize}}" isFirst="{{isLineFirst}}" active="{{active}}">
/slide-bar-chart>
1.2 滑动组件wxml
- 利用scroll-view的横向滚动 scroll-left
- 柱子的padding-left实现选中效果
<view class='slide-content'>
<scroll-view bindscroll="chartScroll" scroll-x scroll-left="{{moveScroll}}" bindtouchstart='clickStart'
refresher-background="#4C4948" refresher-default-style="none" refresher-enabled="{{false}}">
<view class='slide-charts' style='padding-left:{{chartLeft}}px;width:{{chartWidth}}px'>
<view class='slide-item' wx:for="{{chartData}}" wx:key="index" bindtap="clickItem" data-index="{{index}}">
<view class="slide-item-content {{index == nowIndex - 1 ?'slide-bar-active' : '' }}">
<view class="slide-bar-content " style='height:{{item[yName]*10}}rpx'></view>
<view class='slide-number' hidden='{{!nowIndex}}'>
<block>{{item[xName]}}</block>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
1.3 滑动组件的样式
::-webkit-scrollbar {
display: none;
}
.slide-content {
transform: rotate(180deg);
background: #4C4948;
height: 562rpx;
width: 100%;
overflow-x: scroll;
overflow-y: hidden;
position: relative;
}
.slide-content scroll-view {
width: 100%;
height: 100%;
white-space: nowrap;
}
.slide-charts {
height: 100%;
transition: all 1s;
overflow-x: scroll;
overflow-y: hidden;
display: flex;
}
.slide-item {
display: inline-block;
height: 100%;
text-align: center;
width: 150rpx;
position: relative;
display: flex;
align-items: flex-end;
}
.slide-item::after {
content: "";
position: absolute;
bottom: 0;
height: 88%;
width: 2rpx;
left: 50%;
z-index: -1;
background: rgba(255, 255, 255, 0.1);
}
.slide-item-content {
transform: rotate(180deg);
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: center;
width: 150rpx;
height: 530rpx;
color: rgba(255, 255, 255, 0.5);
}
/* 默认柱体样式 */
.slide-bar-content {
background: #FB8337;
width: 40rpx;
z-index: 5;
border-radius: 8rpx;
margin-top: 24rpx;
}
.slide-number {
font-size: 20rpx;
color: rgba(255, 255, 255, 0.4);
text-align: center;
line-height: 24rpx;
margin-top: 14rpx;
}
.slide-bar-active .slide-bar-content {
background: #FFB687;
}
.slide-bar-active .slide-number {
font-size: 23.5rpx;
color: #ffffff;
}
.chart-bottom {
width: 100%;
position: relative;
}
.chart-bottom:after {
content: "";
position: absolute;
left: 50%;
top: -28rpx;
margin-left: -8rpx;
border-color: transparent transparent #fff transparent;
border-style: solid;
border-width: 16rpx;
}
2.滑动组件scroll-view
- 计算屏幕的宽度,根据柱子宽度,计算柱子间隔
- 选择最后一个在屏幕正中间
- 滑动事件,开始,过程,结束
- 滑动结束,要触发回调判断是否需要加载下一页,并传值给父组件
- 点击单个柱子,获取柱子的index,实现选中效果
// 滑动组件初始化
initialization() {
let self = this
setTimeout(() => {
wx.createSelectorQuery().in(this).selectAll('.slide-item-content').boundingClientRect(function (rects) {
// 获取bar的实际宽度
wx.getSystemInfo({
success: function (res) {
if (!rects.length) {
return;
}
let chartLeft = (res.windowWidth - rects[0].width) / 2; // padding-left
self.setData({
chartLeft: chartLeft,
chartWidth: (self.data.chartData.length - 0.5) * rects[0].width + res.windowWidth / 2, // chart宽度
pageNumAll: Math.ceil(self.data.chartAllData.length / self.data.pageSize), // 总页数 math函数返回整数
})
// 最后一个在正中间
setTimeout(() => {
if (self.data.isFirst) {
self.setData({
// 滚动到最后一个时间轴
moveScroll: 0, // 300
nowIndex: 1,
barWidth: rects[0].width,
windowWidth: res.windowWidth,
isFirst: false,
pageNum: 1,
})
}
}, 500)
}
})
}).exec()
}, 0)
},
// 滑动开始
clickStart(e) {
// 防止伪触摸
this.setData({
isTouch: true
})
},
// 滑动过程
chartScroll(e) {
clearTimeout(this.data.scrollTimeout)
if (this.data.isTouch) {
this.setData({
scrollX: e.detail.scrollLeft
})
// 节流函数,当滑动停止的100毫秒后执行结束事件
// 因为ios下有惯性滑动,这里不能直接touchend事件
this.setData({
scrollTimeout: setTimeout(() => {
this.clickEnd()
}, 100)
})
}
},
// 滑动结束
clickEnd() {
// console.log('滑动结束')
let nowIndex = Math.round(this.data.scrollX / this.data.barWidth + 1);
this.setData({
nowIndex: nowIndex,
moveScroll: this.data.barWidth * (nowIndex - 1),
isTouch: false
})
if (this.data.pageNumAll >= this.data.pageNum) {
let eIndex = (this.data.pageNum - 1) * this.data.pageSize + this.data.pageSize - 1;
if (this.data.chartData.length < eIndex) {
eIndex = this.data.chartData.length - 1;
}
if ((nowIndex - 1 == eIndex) && (this.data.pageNum < this.data.pageNumAll)) {
let num = this.data.pageNum + 1;
this.setData({
pageNum: num,
})
}
this.triggerEvent('currentBarChange', {
nowItem: this.data.chartData[nowIndex - 1],
pageNum: this.data.pageNum,
allPage: this.data.pageNumAll,
})
}
},
// 点击单个柱子
clickItem(e) {
let nowIndex = e.currentTarget.dataset.index + 1;
if (nowIndex == this.data.nowIndex) {
return;
}
if (nowIndex) {
this.setData({
nowIndex: nowIndex,
moveScroll: this.data.barWidth * (nowIndex - 1),
isTouch: false
})
if (this.data.pageNumAll >= this.data.pageNum) {
let eIndex = (this.data.pageNum - 1) * this.data.pageSize + this.data.pageSize - 1;
if (this.data.chartData.length < eIndex) {
eIndex = this.data.chartData.length - 1;
}
if ((nowIndex - 1 == eIndex) && (this.data.pageNum < this.data.pageNumAll)) {
let num = this.data.pageNum + 1;
this.setData({
pageNum: num,
})
}
this.triggerEvent('currentBarChange', {
nowItem: this.data.chartData[nowIndex - 1],
pageNum: this.data.pageNum,
allPage: this.data.pageNumAll,
})
}
}
},
至此就是滑动组件的全部代码了,其中遇到一个困难,在ios上,滚动之后会回到原位置,这个问题困扰了很久,最后发现去掉这个属性就好了scroll-with-animation,原意思是 在设置滚动条位置时使用动画过渡,总之,记录一下。谢谢观看。