微信小程序-仿keep滑动组件初体验

598 阅读3分钟

这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战

如下图,要实现仿照keep的一个可以切换日周月总时间维度的滑动轮播图,默认展示最后一条数据选中效果,分页展示,向左滑动展示历史日期的数据。看起来很炫酷,实现过程中也遇到了很多困难,记录一下,温故而知新!

QQ图片20210823105836.png

第一次看到这个UI图 的时候,觉得不复杂,但是后面实现起来还是遇到了很多的困难。 首先要实现时间维度左右滑动,其次是柱子的滑动,最难点是和以往的分页顺序不同,这个是倒序排列,最新日期在第一页展示,如下图所示

QQ图片20210823105836.png

1.时间选择维度

1.1 父组件wxml

  1. swiper实现时间维度的切换和点击
  2. van-tabs 的active实现选中效果
  3. 引入滑动组件,父子传值
    <!-- 时间维度切换 -->
        <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

  1. 利用scroll-view的横向滚动 scroll-left
  2. 柱子的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

  1. 计算屏幕的宽度,根据柱子宽度,计算柱子间隔
  2. 选择最后一个在屏幕正中间
  3. 滑动事件,开始,过程,结束
  4. 滑动结束,要触发回调判断是否需要加载下一页,并传值给父组件
  5. 点击单个柱子,获取柱子的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,原意思是 在设置滚动条位置时使用动画过渡,总之,记录一下。谢谢观看。