side-index.vue 5.88 KB
<template>
    <div class="slide-bg">
        <v-touch tag="ul"
            class="side-idx"
            @tap="tap"
            @panup="showZoom"
            @pandown="showZoom"
            @panend="hideZoom"
            @press="showZoom"
            @pressup="hideZoom">
            <div class="zoom-in"
                :class="{show: zoomSwitch}">
                {{zoomName}}
            </div>
            <li v-for="(item, index) in items"
                :key="item.name"
                :data-idx="item.name">
                <a class="idx-item no-intercept"
                :class="{zoom: index === zoomIdx}"
                href="javascript:;">
                    {{item.name}}
                </a>
            </li>
        </v-touch>
    </div>
</template>
<script>
    export default {
        name: 'SideIndex',
        props: {
            indexList: Array
        },
        data() {
            return {
                items: [],
                timer: '',
                zoomName: '',
                zoomIdx: '',
                $zoomIn: '',
                $sideIdx: '',
                sideRect: {},
                firstIdxRect: {},
                zoomSwitch: false,
                slideLeft: 0
            };
        },
        watch: {
            indexList() {
                if (this.indexList) {
                    this.items = this.indexList;
                    this.$nextTick(function() {
                        this.recordIdxRect();
                    });
                }
            }
        },
        methods: {
            tap(e) {
                this.showZoom(e);
                clearTimeout(this.timer);
                this.timer = setTimeout(() => {
                    this.hideZoom();
                }, 500);
            },
            hideZoom() {

                // this.zoomIdx = '';
                this.zoomSwitch = false;
            },
            showZoom(e) {
                const eX = e.center.x;
                const eY = e.center.y;
                const outRect = eX < this.slideLeft || eY < this.sideRect.top || eY > this.sideRect.bottom;

                if (outRect) {
                    return this.hideZoom();
                }

                let len = this.items.length;
                let idx = Math.floor((eY - this.firstIdxRect.top) / this.firstIdxRect.height);

                if (idx < 0) {
                    idx = 0;
                }

                if (idx === len || idx > len) {
                    idx = len - 1;
                }

                this.zoomIdx = idx;
                this.zoomName = this.items[idx].name;
                this.$zoomIn.style.top = this.calcZoomInTop(idx);
                this.zoomSwitch = true;
                this.$emit('loc-index', this.zoomName);
            },
            calcZoomInTop(idx) {
                return (idx + 0.5) * this.firstIdxRect.height - this.$zoomIn.getBoundingClientRect().height / 2 +
                this.firstIdxRect.top - this.sideRect.top + 'px';
            },
            recordIdxRect() {
                this.$sideIdx = document.querySelector('.side-idx');
                this.$zoomIn = this.$sideIdx.querySelector('.zoom-in');
                this.sideRect = this.$sideIdx.getBoundingClientRect();
                this.firstIdxRect = this.$sideIdx.querySelector('li').getBoundingClientRect();
            }
        },
        created() {
            if (!this.indexList) {
                // 默认字母表索引数据
                for (let i = 65; i < 91; i++) {
                    let itemTemp = String.fromCharCode(i);

                    this.items.push({
                        index: itemTemp,
                        name: itemTemp
                    });
                }
                this.items.push({
                    index: '0~9',
                    name: '#'
                });
            } else {
                this.items = this.indexList;
            }
        },
        mounted() {
            if (this.indexList) {
                this.items = this.indexList;
                this.$nextTick(function() {
                    setTimeout(() => {
                        this.recordIdxRect();
                        this.slideLeft = this.sideRect.left;
                    }, 500);
                });
            }
        }
    };
</script>
<style>
.slide-bg {
    position: fixed;
    top: 88px;
    bottom: 0;
    right: 0;
    width: 40px;
    background-color: rgba(255, 255, 255, 0.7);
}

.side-idx {
    position: fixed;
    width: 40px;
    margin: 0;
    padding: 6px 0;
    right: 0;
    z-index: 10;
    top: 50% !important;
    transform: translateY(-50%);

    .bg {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        opacity: 0.7;
        background-color: #fff;
    }

    .zoom-in {
        position: absolute;
        top: 0;
        left: -160px;
        width: 120px;
        height: 100px;
        color: #fff;
        text-align: center;
        padding-right: 20px;
        line-height: 100px;
        background: resolve("product/zoom-in-bg.png");
        background-size: cover;
        transition: opacity 0.4s ease;
        opacity: 0;
        font-size: 68px;
        font-family: "SanFranciscoText-Medium";

        &.show {
            opacity: 1;
        }
    }

    li {
        position: relative;
        list-style: none;
        text-align: center;
        width: 100%;
        align-items: center;

        a {
            color: #000;
            font-size: 24px;
            display: inline-block;
            width: 24px;
            height: 24px;
            margin: 2px 0;
            font-family: "SanFranciscoText-Medium";

            &.zoom {
                background-color: #000;
                border-radius: 50%;
                color: #fff;
                width: 28px;
                height: 28px;
                line-height: 28px;
                text-align: center;
            }
        }
    }
}

</style>