头图


锚点滚动有几个基本要素:
1、父级盒子
2、子级元素
3、锚点

在一个实际场景里,通常是父级盒子内部套了一个子元素列表,父级盒子有一个指定高度,子元素高度超过父级盒子时,子元素在父元素内部滚动。
在布局方面,左侧和右侧没有任何关联,左侧仅仅只是分类列表,右侧仅仅只是分类详情列表,根据这个可以得出如下布局:
image.png
点击左侧分类的时候,通过事件,获取右侧分类锚点,然后给右侧的父级盒子设置滚动
下面是完整代码,非常简单:

<template>
    <view style="display: flex;">
        <view class="left_box">
            <view class="left_inner_box" :class="current == item.value ? 'current' : ''" v-for="item in list"
                :key="item.value" @click="onTab(item.value)">
                {{item.label}}
            </view>
        </view>
        <view class="right_box">
      <!-- 给子元素设置锚点id -->
            <view style="background: #3c3f47;" id="step1">
                <view class="right_inner_box" v-for="item in listInfo.type1">
                    {{ item.label }}
                </view>
            </view>
      <!-- 给子元素设置锚点id -->
            <view style="background: green;" id="step2">
                <view class="right_inner_box" v-for="item in listInfo.type2">
                    {{ item.label }}
                </view>
            </view>

            <view style="background: #3c3f47;" id="step3">
                <view class="right_inner_box" v-for="item in listInfo.type3">
                    {{ item.label }}
                </view>
            </view>

            <view style="background: green;" id="step4">
                <view class="right_inner_box" v-for="item in listInfo.type4">
                    {{ item.label }}
                </view>
            </view>

            <view style="background: #3c3f47;" id="step5">
                <view class="right_inner_box" v-for="item in listInfo.type5">
                    {{ item.label }}
                </view>
            </view>

            <view style="background: green;" id="step6">
                <view class="right_inner_box" v-for="item in listInfo.type6">
                    {{ item.label }}
                </view>
            </view>
        </view>
    </view>
</template>

<script>
    export default {
        data() {
            return {
                list: [{
                        label: '分类一',
                        value: 1
                    },
                    {
                        label: '分类二',
                        value: 2
                    },
                    {
                        label: '分类三',
                        value: 3
                    },
                    {
                        label: '分类四',
                        value: 4
                    },
                    {
                        label: '分类五',
                        value: 5
                    },
                    {
                        label: '分类六',
                        value: 6
                    }
                ],
                current: 1,
                listInfo: {
                    type1: [{
                            label: '分类一的内容-01',
                            value: 11
                        },
                        {
                            label: '分类一的内容-02',
                            value: 12
                        },
                        {
                            label: '分类一的内容-03',
                            value: 13
                        },
                        {
                            label: '分类一的内容-04',
                            value: 14
                        },
                        {
                            label: '分类一的内容-05',
                            value: 15
                        },
                        {
                            label: '分类一的内容-06',
                            value: 16
                        },
                        {
                            label: '分类一的内容-07',
                            value: 17
                        },
                    ],
                    type2: [{
                            label: '分类二的内容-01',
                            value: 21
                        },
                        {
                            label: '分类二的内容-02',
                            value: 22
                        },
                        {
                            label: '分类二的内容-03',
                            value: 23
                        },
                        {
                            label: '分类二的内容-04',
                            value: 24
                        },
                        {
                            label: '分类二的内容-05',
                            value: 25
                        },
                        {
                            label: '分类二的内容-06',
                            value: 26
                        },
                        {
                            label: '分类二的内容-07',
                            value: 27
                        },
                    ],
                    type3: [{
                            label: '分类三的内容-01',
                            value: 31
                        },
                        {
                            label: '分类三的内容-02',
                            value: 32
                        },
                        {
                            label: '分类三的内容-03',
                            value: 33
                        },
                        {
                            label: '分类三的内容-04',
                            value: 34
                        },
                        {
                            label: '分类三的内容-05',
                            value: 35
                        },
                        {
                            label: '分类三的内容-06',
                            value: 36
                        },
                        {
                            label: '分类三的内容-07',
                            value: 37
                        },
                    ],
                    type4: [{
                            label: '分类四的内容-01',
                            value: 41
                        },
                        {
                            label: '分类四的内容-02',
                            value: 42
                        },
                        {
                            label: '分类四的内容-03',
                            value: 43
                        },
                        {
                            label: '分类四的内容-04',
                            value: 44
                        },
                        {
                            label: '分类四的内容-05',
                            value: 45
                        },
                        {
                            label: '分类四的内容-06',
                            value: 46
                        },
                        {
                            label: '分类四的内容-07',
                            value: 47
                        },
                    ],
                    type5: [{
                            label: '分类五的内容-01',
                            value: 51
                        },
                        {
                            label: '分类五的内容-02',
                            value: 52
                        },
                        {
                            label: '分类五的内容-03',
                            value: 53
                        },
                        {
                            label: '分类五的内容-04',
                            value: 54
                        },
                        {
                            label: '分类五的内容-05',
                            value: 55
                        },
                        {
                            label: '分类五的内容-06',
                            value: 56
                        },
                        {
                            label: '分类五的内容-07',
                            value: 57
                        },
                    ],
                    type6: [{
                            label: '分类六的内容-01',
                            value: 61
                        },
                        {
                            label: '分类六的内容-02',
                            value: 62
                        },
                        {
                            label: '分类六的内容-03',
                            value: 63
                        },
                        {
                            label: '分类六的内容-04',
                            value: 64
                        },
                        {
                            label: '分类六的内容-05',
                            value: 65
                        },
                        {
                            label: '分类六的内容-06',
                            value: 66
                        },
                        {
                            label: '分类六的内容-07',
                            value: 67
                        },
                    ]
                }
            };
        },
        methods: {
            onTab(value) {
                this.current = value;
                const dom = document.getElementById(`step${value}`);
                if (dom) {
                    dom.scrollIntoView({
                        behavior: 'smooth',
                        block: 'center'
                    });
                }
            },
        }
    }
</script>

<style lang="scss" scoped>
    .left_box {
        width: 100px;

        .left_inner_box {
            width: 100px;
            height: 40px;
            line-height: 40px;
            text-align: center;
            background-color: #ccc;
            margin-bottom: 10px;
        }

        .current {
            color: red;
            background-color: cyan;
        }
    }

    .right_box {
        width: calc(100% - 110px);
        height: 600px;
        border: 1px solid red;
        overflow: scroll;

        .right_inner_box {
            height: 40px;
            line-height: 40px;
            text-align: center;
            color: #fff;
            margin-bottom: 4px;
        }
    }
</style>

上面的代码很简单,就是通过点击分类,获取右侧的锚点id,然后给该锚点的父级盒子设置滚动,滚动到指定位置。
核心代码就是这个:

onTab(value) {
    this.current = value;
    // 根据锚点id获取dom元素
    const dom = document.getElementById(`step${value}`);
    if (dom) {
        // 给该元素的父级盒子设置滚动
        dom.scrollIntoView({
            behavior: 'smooth',
            block: 'center'
        });
    }
},

scrollIntoView这个事件,可以越级滚动,那怕子元素不是滚动盒子的直属子元素,在子元素外层嵌套了多个父盒子,照样能让最外层的父级滚动盒子滚动到指定锚点位置,如下:

<template>
    <view style="display: flex;">
        <view class="left_box">
            <view class="left_inner_box" :class="current == item.value ? 'current' : ''" v-for="item in list"
                :key="item.value" @click="onTab(item.value)">
                {{item.label}}
            </view>
        </view>

        
        <!-- 父级滚动盒子 -->
        <view class="right_box">
            <!-- 嵌套层级一 -->
            <view>
                    <!-- 嵌套层级二 -->
                <view>
                        <!-- 嵌套层级三 -->
                    <view style="background: #3c3f47;" id="step1">
                        <view class="right_inner_box" v-for="item in listInfo.type1">
                            {{ item.label }}
                        </view>
                    </view>
                </view>
            </view>


            <view>
                <view>
                    <view style="background: green;" id="step2">
                        <view class="right_inner_box" v-for="item in listInfo.type2">
                            {{ item.label }}
                        </view>
                    </view>
                </view>
            </view>


            <view>
                <view>
                    <view style="background: #3c3f47;" id="step3">
                        <view class="right_inner_box" v-for="item in listInfo.type3">
                            {{ item.label }}
                        </view>
                    </view>
                </view>
            </view>


            <view>
                <view>
                    <view style="background: green;" id="step4">
                        <view class="right_inner_box" v-for="item in listInfo.type4">
                            {{ item.label }}
                        </view>
                    </view>
                </view>
            </view>


            <view>
                <view>
                    <view style="background: #3c3f47;" id="step5">
                        <view class="right_inner_box" v-for="item in listInfo.type5">
                            {{ item.label }}
                        </view>
                    </view>
                </view>
            </view>

            <view>
                <view>
                    <view style="background: green;" id="step6">
                        <view class="right_inner_box" v-for="item in listInfo.type6">
                            {{ item.label }}
                        </view>
                    </view>
                </view>
            </view>

        </view>
    </view>
</template>

需要注意的:
1、scrollIntoView的behavior: 'smooth'属性不兼容低版本浏览器,例如IE
2、js也可以设置平滑滚动效果: dom.style.scrollBehavior = "smooth"
3、子元素需要设置锚点id
4、scrollIntoView的滚动效果不受嵌套层级影响


参考文档:
Element.scrollIntoView()


兔子先森
466 声望556 粉丝

致力于新技术的推广与优秀技术的普及。