side-index.vue 4.97 KB
<template>
    <v-touch tag="ul"
        class="side-idx"
        @tap="tap"
        @panup="showZoom"
        @pandown="showZoom"
        @panend="hideZoom"
        @press="showZoom"
        @pressup="hideZoom">
        <div class="bg"></div>
        <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>
</template>
<script>
    export default {
        name: 'SideIndex',
        props: {
            indexList: Array
        },
        data() {
            return {
                items: [],
                timer: '',
                zoomName: '',
                zoomIdx: '',
                $zoomIn: '',
                $sideIdx: '',
                sideRect: {},
                firstIdxRect: {},
                zoomSwitch: false
            };
        },
        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.sideRect.left || 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;
            }
        }
    };
</script>
<style>
.side-idx {
    position: fixed;
    width: 30px;
    margin: 0;
    padding: 6px 0;
    right: 0;
    top: 0;
    z-index: 10;

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

    .zoom-in {
        position: absolute;
        top: 0;
        left: -100px;
        width: 70px;
        height: 58px;
        padding-left: 20px;
        color: #fff;
        text-align: left;
        line-height: 58px;
        background: resolve("product/zoom-in-bg.png");
        background-size: cover;
        transition: opacity 0.4s ease;
        opacity: 0;

        &.show {
            opacity: 1;
        }
    }

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

        a {
            color: #fff;
            font-size: 18px;
            display: inline-block;
            width: 24px;
            height: 24px;
            margin: 2px 0;

            &.zoom {
                 background-color: #000;
                 border-radius: 50%;
            }
        }
    }
}

</style>