Vue Router 4.0+ 页面滚动行为&模拟锚点滚动

670 阅读3分钟

Vue Router 4.0+ 页面滚动行为&模拟锚点滚动

模拟锚点滚动:

router.js

import {
    createRouter,
    createWebHashHistory
} from 'vue-router'


//  锚点滚动行为
function scrollBehaviorOfAnchor (id, top) {
    /*
    'scrollBehaviorOfAnchor' 函数实现在目标元素小于屏幕高度时将元素滚动至屏幕中央,
    大于等于以及接近(75%屏幕高度)时将元素顶部与屏幕顶部对齐,
    如有悬浮栏时可将悬浮栏高度设置到参数 'top' 以实现避开悬浮栏
    */

    let position
    const _id = id.replace('#', '')
    const EL_HEIGHT = getOffset(document.getElementById(_id)).size.height
    const SCREEN_HEIGHT = window.screen.height

    // 当元素接近屏幕高度时,贴近视窗顶部,否则居中。
    if (EL_HEIGHT <= SCREEN_HEIGHT * 0.75) {
        position = (SCREEN_HEIGHT - EL_HEIGHT) / 2
    } else {
        position = top ? top : 0
    }
    
    return {
        el: id,
        behavior: 'smooth', // 浏览器支持滚动行为时使得动画更流畅
        top: position
    }
}


export default createRouter({
    history: createWebHashHistory(),
    routes, // TODO 你的 router
    // 设置滚动行为
    scrollBehavior: (to, from, savedPosition)=>{
        return new Promise((resolve)=>{
            if(to.hash){
                resolve(scrollBehaviorOfAnchor(to.hash))
            }
        })
    }
})

在页面中使用 <router-link> 即可跳转至相应位置。

Template

// 将 `to` 属性设置为目标块的 `id` 即可条状到对应位置。
<router-link to='#'>目标位置</router-link>

新导航回到页面顶部:

在单页应用中有滚动内容时可能会遇到跳转到新页面时不会自动回到页面顶部的问题。 在 scrollBehavior 选项中返回 top:0 即可在条状新导航时回到页面顶部。

router.js

export default createRouter({
    history: createWebHashHistory(),
    routes, // TODO 你的 router
    // 设置滚动行为
    scrollBehavior: (to, from, savedPosition)=>{
        return new Promise((resolve)=>{
            
            // 跳转到新导航时回到页面顶部
            resolve({ 
                left:0,
                top:0
            })
            
        })
    }
})

翻页时保持滚动位置

实现功能需要使用到 savedPosition 参数。在按下浏览器的翻页键&鼠标上的翻页键时它记录了页面中的滚动位置信息。它实际上就是一个对象就像这样。

Object savedPosition

console.log(savedPosition);

>>>{left:18000.799999237060547, top:0}

只需要返回这个对象在返回页面时将会处于离开时的位置。

router.js

export default createRouter({
    history: createWebHashHistory(),
    routes, // TODO 你的 router
    // 设置滚动行为
    scrollBehavior: (to, from, savedPosition)=>{
        return new Promise((resolve)=>{
         
             if(savedPosition){
                 resolve(savedPosition)
             }
            
        })
    }
})

完整模块

这个模块中的函数集合了翻页保持,自动回顶部,锚点行为模拟。

scrollBehavior.js 将以下代码保存为 scrollBehavior.js 文件。以便于引用。

export function scrollBehaviorOfAnchor (id, top) {
    /*
    现在目标元素小于屏幕高度时将元素滚动至屏幕中央,
    大于等于以及接近(75%屏幕高度)时将元素顶部与屏幕顶部对齐,
    如有悬浮栏时可将悬浮栏高度设置到参数 'top' 以实现避开悬浮栏
    */

    let position
    const _id = id.replace('#', '')

    const EL_HEIGHT = getOffset(document.getElementById(_id)).size.height
    const SCREEN_HEIGHT = window.screen.height

    // 当元素接近屏幕高度时,贴近视窗顶部,否则居中。
    if (EL_HEIGHT <= SCREEN_HEIGHT * 0.75) {
        position = (SCREEN_HEIGHT - EL_HEIGHT) / 2
    } else {
        position = top ? top : 0
    }
    return {
        el: id,
        behavior: 'smooth',
        top: position
    }
}

export function scrollBehaviorOfVueRouter (newRouterTimeOut, top) {
   /*
    newRouterTimeOut: 在跳转新连接时如设置了过渡动画则可以设置一个延时以避免动画闪烁,单位/ms
    top:  如有悬浮栏时可将悬浮栏高度设置到参数 'top' 以实现避开悬浮栏,单位/px
   */

    return function (to, from, savedPosition) {
        return new Promise((resolve) => {
            if (to.hash) {
                // 模拟锚点行为
                resolve(scrollBehaviorOfAnchor(to.hash, top))
            } else {
                // 等待页面过渡再滚动
                setTimeout(() => {
                    if (savedPosition) {
                        // 使用翻页键时
                        console.log(savedPosition)
                        resolve(savedPosition)
                    } else {
                        // 新链接
                        resolve({
                            top: 0,
                            left: 0
                        })
                    }
                }, newRouterTimeOut)

            }
        })
    }

}

如何使用模块

router.js

// 导入模块
import { scrollBehaviorOfVueRouter } from './scrollBehavior.js'

export default createRouter({
    history: createWebHashHistory(),
    routes, // TODO 你的 router

    // 使用滚动行为模块
    scrollBehavior: scrollBehaviorOfVueRouter(
        200// 例如在过渡到新页面时有 400ms 的透明度过渡动画则可在动画过渡到一半(200ms) 时进行滚动
        120// 例如页面有一个 100px 高度的悬浮导航栏,
             // 则设置滚动到屏幕顶部时距离屏幕顶部始终为 120px 以避开导航栏
    )
})