【Vue】锚点定位 及 回到顶部

一. 跨页面锚点跳转(Vue2)

  1. 首页 跳转到 搜索页

1.1 首页

  1. 通过路由跳转
query:{
   id:id
}

handleClick(){
    this.$router.push({
    	path:'/search',
    	query:this.query
    })
}

1.2 搜索页

  1. 处理
<template>
    <div>
        <div class="industry_area">
            <div class="industry_single" v-for="(menu, index) in industryList" :key="index"
                @click="handleClick(menu.id)">
                <div :id="`id${menu.id}`" class="industry_title "
                    :class="activeIndustryId == menu.id ? 'industry_active' : ''">
                    {{
                        menu.dictValue
                    }}
                </div>
                <div :id="`id${menuChild.id}`" class="industry_child"
                    :class="activeIndustryId == menuChild.id ? 'industry_active' : ''" v-if="menu.children.length > 0"
                    v-for="(menuChild, index) in menu.children" :key="index" @click.stop="handleClick(menuChild.id, 2)">
                    <span>{{ menuChild.dictValue }}</span>
                </div>
            </div>
        </div>
    </div>
</template>

<script>

import { getTree} from '@/api/xxx'
export default {
    data() {
        return {
            industryList: [],
            activeIndustryId :'' // 当前选中项
        }
    },
    mounted() {
        this.fetchData()
    },
    methods: {

        /**
         * 获取行业列表数据
         */
        fetchData() {
            getTree('industry').then(res => {
                this.industryList = res.data
                if (res.code == 200 && this.activeIndustryId != "") {
                    this.$nextTick(() => {
                        this.anchorScrollIntoTarget()
                    })
                }
            })
        },
        
        /**
         * 根据行业id获取文档列表
         * @param {Int} id 
         */
        handleClick(id, level) {
            this.activeIndustryId = id
            this.$emit('handleClickSearch', id, level)
        },

        /**
         * 锚点跳转
         */
        anchorScrollIntoTarget() {  
            document.querySelector(`#id${this.activeIndustryId}`).scrollIntoView({
                behavior: 'smooth',
                block: 'center'
            })
        }
    }
}
</script>

二. 本页面跳转(Vue3)

  1. 需求:选中哪个,哪个居中显示
  2. 效果:
    在这里插入图片描述
  3. 实现
<template>
	<div class="tab" >
		<div v-for="(item, index) in tabList" :id="'tab-' + index" :key="index" :ref="setRef" class="tab-item"
          :class="{ active: activeIndex === index }" @click="changeTab(index)">
          {{ item }}
        </div>
	</div>
</template>
<script>
const tabList = ref([
  '标签1',
  '标签2',
  '标签3',
  '标签4',
  '标签5',
  '标签6',
  '标签7',
  '标签8'
])
const activeIndex = ref(0)
// 设置锚点
const eleRefs = ref([])
const setRef = (el) => {
  if (el) {
    eleRefs.value.push(el)
  }
}
// 切换tab
const changeTab = index => {
  anchor(index)
}
// 锚点定位
const anchor = index => {
  activeIndex.value = index
  if (eleRefs.value[index]) {
    eleRefs.value[index].scrollIntoView({
      block: 'center',
      inline: 'center',
      behavior: 'smooth' // 平滑滚动到目标位置
    })
  }
}
</script>
<style lang="scss" scoped>
.tab {
  width: 750px;
  height: 114px;
  display: flex;
  align-items: center;
  overflow-x: auto;
  /* 水平方向超出时显示滚动条 */
  white-space: nowrap;
  /* 保持内容在一行显示 */
  scrollbar-width: none;
  /* 隐藏滚动条 */
  -ms-overflow-style: none;
  background-image: url('@/assets/images/BI/indexbg2.png');
  background-repeat: no-repeat;
  background-size: 100% 100%;

  /* IE 10+ */
  .tab-item {
    height: 74px;
    font-size: 32px;
    background: rgba(4, 106, 249, 1);
    display: flex;
    align-items: center;
    justify-content: center;
    margin-left: 20px;
    border: 1px solid rgba(255, 255, 255, 0.4);
    border-radius: 40px;
    color: #fff;
    padding: 16px 15px;
  }

  .active {
    background: #fff;
    color: black;
    font-weight: 500;
  }
}
</style>

三. 回到顶部(Vue3)

3.1 document.body.scrollTo(无滚动条)

  1. 需求:“顶部”图标固定在页面右下方,点击“顶部”即可返回顶部

  2. 效果
    在这里插入图片描述

  3. 实现

<template>
  <div class="container">
    <div>内容区域</div>
    // 回到顶部
    <div class="back-to-top">
      <a class="top" @click="scrollToTop">
        <span>
          <van-icon name="arrow-up" class="i" />
          顶部
        </span>
      </a>
    </div>
  </div>
</template>
<script setup>
// 回到顶部
const scrollToTop = () => {
  // 因为没有滚动条所以此种方法不生效
  // window.scrollTo({
  //   top: 0,
  //   behavior: 'smooth'
  // });
  document.body.scrollTo({
    top: 0,
    behavior: 'smooth'
  })
}
</script>
<style>
.container{
  height: 100%; // height不能固定,例如 height:100vh,否则会失效
}
 // 返回顶部
.back-to-top {
  position: fixed;
  right: 0.2rem;
  bottom: 1.5rem;
  width: 1.2rem;
  z-index: 990;
}

.back-to-top .top {
  margin: 0 auto;
  width: 1rem;
  height: 1rem;
  background: #fff;
  box-shadow: 0 0.04rem 0.16rem 0 rgba(0, 0, 0, 0.18);
  display: flex;
  justify-content: center;
  align-items: center;
  border-radius: 2rem;
  text-align: center;
  font-size: 0.22rem;
}

.back-to-top .top .i {
  display: block;
  font-size: 0.34rem;
}

.back-to-top:hover {
  transform: scale(1.1);
}
</style>

3.2 id + a标签 + ref(无滚动条)

  1. 实现
<template>
  <div id="content" ref="outerDom" class="container">
    <div ref="containerTitle" class="top-title mt26">不跟随区域</div>
   // 固定跟随区域
    <div class="tab-nav">
      <div class="tab ">
        <div v-for="(item, index) in tabList" :id="'tab-' + index" :key="index" :ref="setRef" class="tab-item"
          :class="{ active: activeIndex === index }" @click="changeTab(index)">
          {{ item }}
        </div>
      </div>
      <div class="statTime mt1-1">
        时间
      </div>
    </div>
    <component :is="tabComponents[activeIndex]" @changeActivityPreview="changeActivityPreview">
    </component>
    <div class="back-to-top">
      <a ref="#content" class="top" @click="scrollToTop">
        <span>
          <van-icon name="arrow-up" class="i" />
          顶部
        </span>
      </a>
    </div>
  </div>
</template>

<script setup>
import { reactive, ref } from 'vue'
import tab1 from './components/tab1.vue'
import tab2 from './components/tab2.vue'
import tab3 from './components/tab3.vue'
import tab4 from './components/tab4.vue'
import tab5 from './components/tab5.vue'
import tab6 from './components/tab6.vue'
import tab7 from './components/tab7.vue'
import tab8 from './components/tab8.vue'

const tabList = ref([
  'tab1',
  'tab2',
  'tab3',
  'tab4',
  'tab5',
  'tab6',
  'tab7',
  'tab8'
])

const tabComponents = markRaw({
  0: tab1, 
  1: tab2, 
  2: tab3, 
  3: tab4, 
  4: tab5, 
  5: tab6, 
  6: tab7,
  7: tab8 
})
const activeIndex = ref(0)
// 设置锚点
const eleRefs = ref([])
const outerDom = ref(null)
const setRef = (el) => {
  if (el) {
    eleRefs.value.push(el)
  }
}
// 回到顶部
const scrollToTop = () => {
 const scrollStep = () => {
    if (outerDom.value.scrollTop > 0) {
      outerDom.value.scrollTop -= Math.min(outerDom.value.scrollTop / 20, 20);
      requestAnimationFrame(scrollStep);
    }
  };
  scrollStep();
}

// 切换tab
const changeTab = index => {
  anchor(index)
}

const changeActivityPreview = name => {
  const type = tabList.value.findIndex(e => {
    return e.includes(name)
  })
  anchor(type)
}

// 锚点定位
const anchor = index => {
  activeIndex.value = index
  if (eleRefs.value[index]) {
    eleRefs.value[index].scrollIntoView({
      block: 'center',
      inline: 'center',
      behavior: 'smooth' // 平滑滚动到目标位置
    })
  }
}
</script>

<style lang="scss" scoped>
.container {
  max-height: 100vh;
  position: relative;
  overflow: auto;
}
 // 返回顶部
.back-to-top {
  position: fixed;
  right: 0.2rem;
  bottom: 1.5rem;
  width: 1.2rem;
  z-index: 990;
}

.back-to-top .top {
  margin: 0 auto;
  width: 1rem;
  height: 1rem;
  background: #fff;
  box-shadow: 0 0.04rem 0.16rem 0 rgba(0, 0, 0, 0.18);
  display: flex;
  justify-content: center;
  align-items: center;
  border-radius: 2rem;
  text-align: center;
  font-size: 0.22rem;
}

.back-to-top .top .i {
  display: block;
  font-size: 0.34rem;
}

.back-to-top:hover {
  transform: scale(1.1);
}
.homebg {
  width: 100%;
  height: 310px;
  background-image: url('@/assets/images/BI/indexbg.png');
  background-repeat: no-repeat;
  background-size: 100% 100%;
  padding-top: 88px;
}

.top-title {
  height: 176px;
  background-image: url('@/assets/images/BI/indexbg1.png');
  background-repeat: no-repeat;
  background-size: 100% 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 36px;
  font-weight: 500;
  color: rgba(255, 255, 255, 1);
}

.tab {
  width: 750px;
  height: 114px;
  display: flex;
  align-items: center;
  overflow-x: auto;
  /* 水平方向超出时显示滚动条 */
  white-space: nowrap;
  /* 保持内容在一行显示 */
  scrollbar-width: none;
  /* 隐藏滚动条 */
  -ms-overflow-style: none;
  background-image: url('@/assets/images/BI/indexbg2.png');
  background-repeat: no-repeat;
  background-size: 100% 100%;

  /* IE 10+ */
  .tab-item {
    height: 74px;
    font-size: 32px;
    background: rgba(4, 106, 249, 1);
    display: flex;
    align-items: center;
    justify-content: center;
    margin-left: 20px;
    border: 1px solid rgba(255, 255, 255, 0.4);
    border-radius: 40px;
    color: #fff;
    padding: 16px 15px;
  }

  .active {
    background: #fff;
    color: black;
    font-weight: 500;
  }
}
</style>

  1. 代码解释

Template:

(1)包含一个 div 元素,其 id 为 content,并且使用 ref 绑定到 outerDom。 包含一个回到顶部的按钮,点击时触发
(2)scrollToTop 方法。

Script:

(1)导入 ref 从 vue 和 VantIcon 从 vant。 定义 outerDom 为一个响应式引用,用于引用外部 DOM 元素。 定义
(2)scrollToTop 方法,使用 requestAnimationFrame 和递归调用来逐步减少 scrollTop 的值,直到
(3)scrollTop 为 0,实现平滑滚动效果。

Style:

(1)定义 .back-to-top 的样式,包括位置、背景颜色、边框等。

  1. 工作原理

scrollToTop 方法中,scrollStep 函数递归调用自身,每次减少 scrollTop 的值,直到 scrollTop 为
0。 Math.min(outerDom.value.scrollTop / 5, 5) 确保每次滚动的距离不会太大,从而实现平滑滚动效果。
使用 requestAnimationFrame 来优化性能,避免在每次滚动时都执行大量计算。

<think>好的,我需要帮助用户了解如何在Vue3中实现跳转功能。首先,我要回忆一下Vue3的相关知识,尤其是关于DOM操作和滚动行为的处理。记得Vue3推荐使用Composition API,所以应该用ref来获取DOM元素的引用。 用户提到了跳转,可能需要用到scrollIntoView方法,这个方法可以让页面滚动到指定元素的位置。同时,用户还希望有平滑过渡的动画效果,所以应该设置behavior为'smooth'。 接下来,我需要考虑如何组织代码结构。可能需要先在模板中定义链接和目标元素,然后通过击事件触发滚动。使用ref绑定每个目标元素,这样在方法中可以通过refs数组访问到对应的DOM节。 另外,用户提供的参考内容里有提到Vue3和Nuxt3的项目开发,可能需要说明在单页面应用中的注意事项,比如路由不会重新加载页面,所以直接使用scrollIntoView是可行的。 然后,我需要检查是否有常见的坑,比如元素是否已经正确渲染,ref是否被正确绑定。还要考虑到动态生成内容的情况,可能需要使用nextTick来确保DOM已经更新后再执行滚动操作。 最后,需要提供一个完整的示例代码,包括模板部分和脚本部分,展示如何实现击菜单跳转到对应区块,并添加平滑滚动效果。同时,要解释代码的关键部分,帮助用户理解实现的步骤和原理。 可能还需要回答一些相关问题,比如如何处理动态、如何调整滚动偏移量以避免被固定导航栏遮挡,或者如何结合路由实现跨页面的跳转。这些问题可以作为扩展内容,帮助用户更全面地应用该功能。</think>### Vue3 实现跳转功能教程 在 Vue3 中实现跳转功能,可通过以下步骤完成: --- #### 一、基础实现原理 1. **DOM元素标记**:使用 `id` 或 `ref` 标记目标元素 2. **滚动控制**:通过 `scrollIntoView()` 方法实现滚动定位 3. **动画效果**:设置 `behavior: 'smooth'` 添加平滑过渡 --- #### 二、完整代码示例 ```html <template> <!-- 导航菜单 --> <div class="nav"> <button @click="scrollTo('section1')">章节1</button> <button @click="scrollTo('section2')">章节2</button> </div> <!-- 内容区块 --> <div class="content"> <section :ref="(el) => setRef(el, 'section1')"> <h2>第一章内容</h2> </section> <section :ref="(el) => setRef(el, 'section2')"> <h2>第二章内容</h2> </section> </div> </template> <script setup> import { ref } from 'vue' const sections = ref({}) // 绑定ref到对象 const setRef = (el, id) => { if (el) sections.value[id] = el } // 滚动控制方法 const scrollTo = (id) => { sections.value[id]?.scrollIntoView({ behavior: 'smooth', block: 'start' }) } </script> ``` --- #### 三、关键实现说明 1. **动态ref绑定**:使用函数式 `ref` 绑定多个元素到对象 2. **滚动参数配置**: - `behavior: 'smooth'` 实现平滑滚动 - `block: 'start'` 使元素顶部对齐视口 3. **响应式处理**:使用 `sections.value[id]?.` 避免空值错误 --- #### 四、优化建议 1. **滚动偏移**:当有固定导航栏时,添加 `scroll-margin-top` ```css section { scroll-margin-top: 80px; /* 导航栏高度 */ } ``` 2. **路由集成**:结合 `vue-router` 实现 URL 同步 3. **交互反馈**:添加滚动过程的状态指示 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值