css无限循环滚动

318 阅读1分钟

infinite-scroll 无限循环滚动

index.wxml

<view class="infinite-scroll custom-class" style="{{style}}">
    <view class="infinite-scroll__item item-class" wx:for="{{column}}">
        <slot wx:if="{{useSlot}}" name="item{{index}}"></slot>
        <text wx:else>{{item.content}}</text>
    </view>
</view>

index.ts

import { type IOptions } from './type'

Component({
    externalClasses: ['custom-class', 'item-class'],
    options: {
        multipleSlots: true,
    },
    /**
     * 组件的属性列表
     */
    properties: {
        useSlot: Boolean,
        options: {
            type: Object,
            value: {
                list: [],
                direction: '',
                num: 0,
                itemHeight: 0,
                itemWidth: 0,
                animationDuration: 10,
            },
            observer: 'init',
        },
    },

    /**
     * 组件的初始数据
     */
    data: {
        column: [] as any[],
        style: '',
    },

    /**
     * 组件的方法列表
     */
    methods: {
        init() {
            let {
                list,
                direction,
                num = 0,
                itemHeight = 0,
                itemWidth = 0,
                animationDuration,
            } = this.data.options as IOptions
            direction = direction ?? 'column'
            const repeat = list.slice(0, num)
            const column = [...list, ...repeat]
            setTimeout(() => {
                this.triggerEvent('column', column)
            })
            const styleEnum = {
                column: `height: ${
                    num * itemHeight
                }rpx;--translate: translateY(-${
                    list.length
                }00%);--direction: ${direction};--animation-duration: ${animationDuration}s`,
                row: `width: ${num * itemWidth}rpx;--translate: translateX(-${
                    list.length
                }00%);--direction: ${direction};--itemWidth: ${itemWidth}rpx;--animation-duration: ${animationDuration}s`,
            }
            this.setData({
                style: styleEnum[direction],
                column,
            })
        },
    },
})

type.ts

export interface IOptions {
    list: Array<{ content: string }>
    direction: TDirection
    animationDuration: number
    num?: number
    itemHeight?: number
    itemWidth?: number
}
type TDirection = 'column' | 'row'

index.scss

.infinite-scroll {
    position: relative;
    display: flex;
    flex-direction: var(--direction);
    overflow: hidden;

    &__item {
        width: var(--itemWidth, 100%);
        animation: scroll 10s linear infinite;
        animation-duration: var(--animation-duration);
        display: flex;
        align-items: center;
        flex-shrink: 0;
        justify-content: center;
    }
}
@keyframes scroll {
    0% {
        transform: translate(0, 0, 0);
    }
    100% {
        transform: var(--translate);
    }
}

使用

<infinite-scroll
    options="{{options}}"
    item-class="infinite-scroll-item"
    bind:column="getScrollColumn"
    useSlot>
    <block wx:for="{{column}}">
        <view slot="item{{index}}">
            <image
                src="{{item.avatar}}"
                class="infinite-scroll-item__avator"></image>
            <text>{{item.content}}</text>
        </view>
    </block>
</infinite-scroll>
import {IOptions} from 'infinite-scroll/type'
const options:IOptions = {
    list: [{ content: '1', avatar: '1' }],
    direction: 'column',
    num: 2,
    itemHeight: 44,
    itemWidth: 750,
    animationDuration: 10,
}
getScrollColumn(e) {
    this.setData({
        column: e.detail,
    })
},
.infinite-scroll-item {
    justify-content: flex-start !important;
    &__avator {
        width: 36rpx;
        height: 36rpx;
    }
}

Props

参数说明类型默认值
useSlot是否使用自定义内容插槽string
options选项object

options

参数说明类型默认值
list原始数据array
direction滚动方向stringcolumn/row
num显示条数number
itemHeightdirection:column 时的单行高度number
itemWidthdirection:row 时的单行宽度number
animationDuration动画时长number

外部样式类

类名说明
custom-class根节点样式类
item-class项节点样式类