uview-ui_components_u-index-list_u-index-list.vue

<template>

    <!-- 支付宝小程序使用$u.getRect()获取组件的根元素尺寸,所以在外面套一个"壳" -->

    <view>

        <view class="u-index-bar">

            <slot />

            <view v-if="showSidebar" class="u-index-bar__sidebar" @touchstart.stop.prevent="onTouchMove" @touchmove.stop.prevent="onTouchMove"

             @touchend.stop.prevent="onTouchStop" @touchcancel.stop.prevent="onTouchStop">

                <view v-for="(item, index) in indexList" :key="index" class="u-index-bar__index" :style="{zIndex: zIndex + 1, color: activeAnchorIndex === index ? activeColor : ''}"

                 :data-index="index">

                    {{ item }}

                </view>

            </view>

            <view class="u-indexed-list-alert" v-if="touchmove && indexList[touchmoveIndex]" :style="{

                zIndex: alertZIndex

            }">

                <text>{{indexList[touchmoveIndex]}}</text>

            </view>

        </view>

    </view>

</template>

<script>

    var indexList = function() {

        var indexList = [];

        var charCodeOfA = 'A'.charCodeAt(0);

        for (var i = 0; i < 26; i++) {

            indexList.push(String.fromCharCode(charCodeOfA + i));

        }

        return indexList;

    };

    /**

     * indexList 索引列表

     * @description 通过折叠面板收纳内容区域,搭配<u-index-anchor>使用

     * @tutorial https://www.uviewui.com/components/indexList.html#indexanchor-props

     * @property {Number String} scroll-top 当前滚动高度,自定义组件无法获得滚动条事件,所以依赖接入方传入

     * @property {Array} index-list 索引字符列表,数组(默认A-Z)

     * @property {Number String} z-index 锚点吸顶时的层级(默认965)

     * @property {Boolean} sticky 是否开启锚点自动吸顶(默认true)

     * @property {Number String} offset-top 锚点自动吸顶时与顶部的距离(默认0)

     * @property {String} highlight-color 锚点和右边索引字符高亮颜色(默认#2979ff)

     * @event {Function} select 选中右边索引字符时触发

     * @example <u-index-list :scrollTop="scrollTop"></u-index-list>

     */

    export default {

        name: "u-index-list",

        props: {

            sticky: {

                type: Boolean,

                default: true

            },

            zIndex: {

                type: [Number, String],

                default: ''

            },

            scrollTop: {

                type: [Number, String],

                default: 0,

            },

            offsetTop: {

                type: [Number, String],

                default: 0

            },

            indexList: {

                type: Array,

                default () {

                    return indexList()

                }

            },

            activeColor: {

                type: String,

                default: '#2979ff'

            }

        },

        created() {

            // #ifdef H5

            this.stickyOffsetTop = this.offsetTop ? uni.upx2px(this.offsetTop) : 44;

            // #endif

            // #ifndef H5

            this.stickyOffsetTop = this.offsetTop ? uni.upx2px(this.offsetTop) : 0;

            // #endif

            // 只能在created生命周期定义children,如果在data定义,会因为循环引用而报错

            this.children = [];

        },

        data() {

            return {

                activeAnchorIndex: 0,

                showSidebar: true,

                // children: [],

                touchmove: false,

                touchmoveIndex: 0,

            }

        },

        watch: {

            scrollTop() {

                this.updateData()

            }

        },

        computed: {

            // 弹出toast的z-index值

            alertZIndex() {

                return this.$u.zIndex.toast;

            }

        },

        methods: {

            updateData() {

                this.timer && clearTimeout(this.timer);

                this.timer = setTimeout(() => {

                    this.showSidebar = !!this.children.length;

                    this.setRect().then(() => {

                        this.onScroll();

                    });

                }, 0);

            },

            setRect() {

                return Promise.all([

                    this.setAnchorsRect(),

                    this.setListRect(),

                    this.setSiderbarRect()

                ]);

            },

            setAnchorsRect() {

                return Promise.all(this.children.map((anchor, index) => anchor

                    .$uGetRect('.u-index-anchor-wrapper')

                    .then((rect) => {

                        Object.assign(anchor, {

                            height: rect.height,

                            top: rect.top

                        });

                    })));

            },

            setListRect() {

                return this.$uGetRect('.u-index-bar').then((rect) => {

                    Object.assign(this, {

                        height: rect.height,

                        top: rect.top + this.scrollTop

                    });

                });

            },

            setSiderbarRect() {

                return this.$uGetRect('.u-index-bar__sidebar').then(rect => {

                    this.sidebar = {

                        height: rect.height,

                        top: rect.top

                    };

                });

            },

            getActiveAnchorIndex() {

                const {

                    children

                } = this;

                const {

                    sticky

                } = this;

                for (let i = this.children.length - 1; i >= 0; i--) {

                    const preAnchorHeight = i > 0 ? children[i - 1].height : 0;

                    const reachTop = sticky ? preAnchorHeight : 0;

                    if (reachTop >= children[i].top) {

                        return i;

                    }

                }

                return -1;

            },

            onScroll() {

                const {

                    children = []

                } = this;

                if (!children.length) {

                    return;

                }

                const {

                    sticky,

                    stickyOffsetTop,

                    zIndex,

                    scrollTop,

                    activeColor

                } = this;

                const active = this.getActiveAnchorIndex();

                this.activeAnchorIndex = active;

                if (sticky) {

                    let isActiveAnchorSticky = false;

                    if (active !== -1) {

                        isActiveAnchorSticky =

                            children[active].top <= 0;

                    }

                    children.forEach((item, index) => {

                        if (index === active) {

                            let wrapperStyle = '';

                            let anchorStyle = {

                                color: `${activeColor}`

                            };

                            if (isActiveAnchorSticky) {

                                wrapperStyle = {

                                    height: `${children[index].height}px`

                                };

                                anchorStyle = {

                                    position: 'fixed',

                                    top: `${stickyOffsetTop}px`,

                                    zIndex: `${zIndex ? zIndex : this.$u.zIndex.indexListSticky}`,

                                    color: `${activeColor}`

                                };

                            }

                            item.active = active;

                            item.wrapperStyle = wrapperStyle;

                            item.anchorStyle = anchorStyle;

                        } else if (index === active - 1) {

                            const currentAnchor = children[index];

                            const currentOffsetTop = currentAnchor.top;

                            const targetOffsetTop = index === children.length - 1 ?

                                this.top :

                                children[index + 1].top;

                            const parentOffsetHeight = targetOffsetTop - currentOffsetTop;

                            const translateY = parentOffsetHeight - currentAnchor.height;

                            const anchorStyle = {

                                position: 'relative',

                                transform: `translate3d(0, ${translateY}px, 0)`,

                                zIndex: `${zIndex ? zIndex : this.$u.zIndex.indexListSticky}`,

                                color: `${activeColor}`

                            };

                            item.active = active;

                            item.anchorStyle = anchorStyle;

                        } else {

                            item.active = false;

                            item.anchorStyle = '';

                            item.wrapperStyle = '';

                        }

                    });

                }

            },

            onTouchMove(event) {

                this.touchmove = true;

                const sidebarLength = this.children.length;

                const touch = event.touches[0];

                const itemHeight = this.sidebar.height / sidebarLength;

                let clientY = 0;

                clientY = touch.clientY;

                let index = Math.floor((clientY - this.sidebar.top) / itemHeight);

                if (index < 0) {

                    index = 0;

                } else if (index > sidebarLength - 1) {

                    index = sidebarLength - 1;

                }

                this.touchmoveIndex = index;

                this.scrollToAnchor(index);

            },

            onTouchStop() {

                this.touchmove = false;

                this.scrollToAnchorIndex = null;

            },

            scrollToAnchor(index) {

                if (this.scrollToAnchorIndex === index) {

                    return;

                }

                this.scrollToAnchorIndex = index;

                const anchor = this.children.find((item) => item.index === this.indexList[index]);

                if (anchor) {

                    this.$emit('select', anchor.index);

                    uni.pageScrollTo({

                        duration: 0,

                        scrollTop: anchor.top + this.scrollTop

                    });

                }

            }

        }

    };

</script>

<style lang="scss" scoped>

    @import "../../libs/css/style.components.scss";

   

    .u-index-bar {

        position: relative

    }

    .u-index-bar__sidebar {

        position: fixed;

        top: 50%;

        right: 0;

        @include vue-flex;

        flex-direction: column;

        text-align: center;

        transform: translateY(-50%);

        user-select: none;

        z-index: 99;

    }

    .u-index-bar__index {

        font-weight: 500;

        padding: 8rpx 18rpx;

        font-size: 22rpx;

        line-height: 1

    }

    .u-indexed-list-alert {

        position: fixed;

        width: 120rpx;

        height: 120rpx;

        right: 90rpx;

        top: 50%;

        margin-top: -60rpx;

        border-radius: 24rpx;

        font-size: 50rpx;

        color: #fff;

        background-color: rgba(0, 0, 0, 0.65);

        @include vue-flex;

        justify-content: center;

        align-items: center;

        padding: 0;

        z-index: 9999999;

    }

    .u-indexed-list-alert text {

        line-height: 50rpx;

    }

</style>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值