Vue3回到顶部(BackTop)

news2024/12/30 2:03:57

效果如下图:在线预览

在这里插入图片描述

APIs

参数说明类型默认值必传
bottomBackTop 距离页面底部的高度number | string40false
rightBackTop 距离页面右侧的宽度number | string40false
visibilityHeight滚动时触发显示回到顶部的高度number180false
toBackTop 渲染的容器节点 可选 元素标签名 例如 body 或者 元素本身,下同string | HTMLElement‘body’false
listenTo监听滚动的元素,如果为 undefined 会监听距离最近的一个可滚动的祖先节点string | HTMLElementundefinedfalse

Events

事件名称说明参数
show是否展现的回调(show: boolean) => void

创建回到顶部组件BackTop.vue

<script setup lang="ts">
import { ref, computed, watch, watchEffect, nextTick } from 'vue'
interface Props {
  bottom?: number|string // BackTop 距离页面底部的高度
  right?: number|string // BackTop 距离页面右侧的宽度
  visibilityHeight?: number // 滚动时触发显示回到顶部的高度
  to?: string|HTMLElement // BackTop 渲染的容器节点 可选 元素标签名(例如 'body') 或者 目标元素本身,下同
  listenTo?: string|HTMLElement // 监听滚动的元素,如果为 undefined 会监听距离最近的一个可滚动的祖先节点
}
const props = withDefaults(defineProps<Props>(), {
  bottom: 40,
  right: 40,
  visibilityHeight: 180,
  to: 'body',
  listenTo: undefined
})
const bottomPosition = computed(() => {
  if (typeof props.bottom === 'number') {
    return props.bottom + 'px'
  }
  return props.bottom
})
const rightPosition = computed(() => {
  if (typeof props.right === 'number') {
    return props.right + 'px'
  }
  return props.right
})
const backtop = ref()
const scrollTop = ref(0)
const scrollTarget = ref<any>()
watchEffect(() => {
  // 监听滚动的元素
  nextTick(() => {
    if (props.listenTo === undefined) {
      scrollTarget.value = getScrollParentElement(backtop.value?.parentElement)
    } else if (typeof props.listenTo === 'string') {
      scrollTarget.value = typeof document !== 'undefined' ? document.getElementsByTagName(props.listenTo)[0] : null
    } else if (props.listenTo instanceof HTMLElement) {
      scrollTarget.value = props.listenTo
    }
    if (scrollTarget.value) {
      observeElement(scrollTarget.value)
      scrollTarget.value.addEventListener('scroll', (e: any) => {
        scrollTop.value = e.target.scrollTop
      })
    }
  })
})
function observeElement (el: HTMLElement) {
  // 当观察到变动时执行的回调函数
  const callback = function (mutationsList: any, observer: any) {
    scrollTop.value = scrollTarget.value.scrollTop
  }
  // 观察器的配置(需要观察什么变动)
  const config = { attributes: true, subtree: true }
  // 创建一个观察器实例并传入回调函数
  const observer = new MutationObserver(callback)
  // 以上述配置开始观察目标节点
  observer.observe(el, config)
}
watchEffect(() => {
  // 渲染容器节点
  nextTick(() => {
    var target = null
    if (typeof props.to === 'string') {
      target = typeof document !== 'undefined' ? document.getElementsByTagName(props.to)[0] : null
    } else if (props.to instanceof HTMLElement) {
      target = props.to
    }
    target && target.appendChild(backtop.value)
  })
})
const show = computed(() => {
  return scrollTop.value >= props.visibilityHeight
})
function getScrollParentElement (el: any) {
  if (el) {
    if (el.scrollHeight > el.clientHeight) {
      return el
    } else {
      return getScrollParentElement(el.parentElement)
    }
  }
  return null
}
const emits = defineEmits(['click', 'show'])
function onBackTop () {
  scrollTarget.value && scrollTarget.value.scrollTo({
    top: 0,
    behavior: 'smooth' // 平滑滚动并产生过渡效果
  })
  emits('click')
}
watch(show, (to) => {
  emits('show', to)
})
</script>
<template>
  <Transition>
    <div ref="backtop" v-show="show" @click="onBackTop" class="m-backtop" :style="`bottom: ${bottomPosition}; right: ${rightPosition};`">
      <slot>
        <span class="m-icon">
          <svg class="u-icon" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xlinkHref="http://www.w3.org/1999/xlink"><g stroke="none" stroke-width="1" fill-rule="evenodd"><g transform="translate(-139.000000, -4423.000000)" fill-rule="nonzero"><g transform="translate(120.000000, 4285.000000)"><g transform="translate(7.000000, 126.000000)"><g transform="translate(24.000000, 24.000000) scale(1, -1) translate(-24.000000, -24.000000) translate(12.000000, 12.000000)"><g transform="translate(4.000000, 2.000000)"><path d="M8,0 C8.51283584,0 8.93550716,0.38604019 8.99327227,0.883378875 L9,1 L9,10.584 L12.2928932,7.29289322 C12.6834175,6.90236893 13.3165825,6.90236893 13.7071068,7.29289322 C14.0675907,7.65337718 14.0953203,8.22060824 13.7902954,8.61289944 L13.7071068,8.70710678 L8.70710678,13.7071068 L8.62544899,13.7803112 L8.618,13.784 L8.59530661,13.8036654 L8.4840621,13.8753288 L8.37133602,13.9287745 L8.22929083,13.9735893 L8.14346259,13.9897165 L8.03324678,13.9994506 L7.9137692,13.9962979 L7.77070917,13.9735893 L7.6583843,13.9401293 L7.57677845,13.9063266 L7.47929125,13.8540045 L7.4048407,13.8036865 L7.38131006,13.7856883 C7.35030318,13.7612383 7.32077858,13.7349921 7.29289322,13.7071068 L2.29289322,8.70710678 L2.20970461,8.61289944 C1.90467972,8.22060824 1.93240926,7.65337718 2.29289322,7.29289322 C2.65337718,6.93240926 3.22060824,6.90467972 3.61289944,7.20970461 L3.70710678,7.29289322 L7,10.585 L7,1 L7.00672773,0.883378875 C7.06449284,0.38604019 7.48716416,0 8,0 Z"></path><path d="M14.9333333,15.9994506 C15.5224371,15.9994506 16,16.4471659 16,16.9994506 C16,17.5122865 15.5882238,17.9349578 15.0577292,17.9927229 L14.9333333,17.9994506 L1.06666667,17.9994506 C0.477562934,17.9994506 0,17.5517354 0,16.9994506 C0,16.4866148 0.411776203,16.0639435 0.9422708,16.0061783 L1.06666667,15.9994506 L14.9333333,15.9994506 Z"></path></g></g></g></g></g></g></svg>
        </span>
      </slot>
    </div>
  </Transition>
</template>
<style lang="less" scoped>
.v-enter-from,
.v-leave-to {
  opacity: 0;
  transform: scale(.75);
}
.m-backtop {
  position: fixed;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgba(0, 0, 0, .88);
  border-radius: 22px;
  height: 44px;
  min-width: 44px;
  box-shadow: 0 2px 8px 0px rgba(0, 0, 0, .12);
  background-color: #fff;
  transition: all .3s cubic-bezier(.4, 0, .2, 1);
  &:hover {
    color: @themeColor;
    box-shadow: 0 2px 8px 3px rgba(0, 0, 0, .12);
    .m-icon .u-icon {
      fill: @themeColor;
    }
  }
  .m-icon {
    font-size: 26px;
    transition: color .3s cubic-bezier(.4, 0, .2, 1);
    height: 1em;
    width: 1em;
    line-height: 1em;
    text-align: center;
    display: inline-block;
    position: relative;
    fill: currentColor;
    transform: translateZ(0);
    .u-icon {
      fill: rgba(0, 0, 0, .88);
      pointer-events: none;
      height: 1em;
      width: 1em;
    }
  }
}
</style>

在要使用的页面引入

<script setup lang="ts">
import BackTop from './BackTop.vue'
import { ref } from 'vue'
function onShow (show: boolean) {
  console.log('show', show)
}
const scrollContainer = ref()
</script>
<template>
  <div>
    <h1>BackTop 回到顶部</h1>
    <h2 class="mt30 mb10" style="margin-top: 150vh;">基本使用</h2>
    <h3 class="mb10">BackTop 会找到首个可滚动的祖先元素并且监听它的滚动事件</h3>
    <BackTop :right="100" @show="onShow" />
    <h2 class="mt30 mb10">自定义可视高度</h2>
    <h3 class="mb10">自定义滚动时触发显示回到顶部的高度</h3>
    <BackTop :bottom="100" :visibility-height="300">
      <div style="width: 200px; height: 40px; line-height: 40px; text-align: center; font-size: 14px;">
        可视高度:300px
      </div>
    </BackTop>
    <h2 class="mt30 mb10">自定义位置</h2>
    <BackTop :right="40" :bottom="160">
      <div style="width: 200px; height: 40px; line-height: 40px; text-align: center; font-size: 14px;">
        改变位置
      </div>
    </BackTop>
    <h2 class="mt30 mb10">自定义监听目标</h2>
    <h3 class="mb10">自定义设定监听哪个元素来触发 BackTop</h3>
    <BackTop :listen-to="scrollContainer" :bottom="220" :visibility-height="10">
      <div style="width: 200px; height: 40px; line-height: 40px; text-align: center; font-size: 14px;">
        指定目标
      </div>
    </BackTop>
    <div ref="scrollContainer" style="overflow: auto; height: 72px; line-height: 1.5">
      这块应该写一个有意思的笑话。<br>
      这块应该写一个有意思的笑话。<br>
      这块应该写一个有意思的笑话。<br>
      这块应该写一个有意思的笑话。<br>
      这块应该写一个有意思的笑话。<br>
      这块应该写一个有意思的笑话。<br>
      这块应该写一个有意思的笑话。<br>
    </div>
  </div>
</template>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/970401.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

05. 逻辑门和加法器等原理探究

1. 二极管 1.1 什么是二极管 二极管是一种电子元件&#xff0c;它的主要特点是只允许电流在一个方向通过&#xff0c;而另一个方向电流将被阻止。 下面是二极管的示意图: 电流往箭头指向的地方流 1.2 二极管的作用 下面第一个图&#xff1a;给灯泡加上正负电压&#xff0c;…

pcapng 文件转 pcap 文件

pcap 是早期计算机网络抓包格式,几乎所有抓包工具都支持pcap&#xff1b;pcapng 是下一代抓包格式&#xff0c;支持不同路线以寻求标准化&#xff0c;pcapng格式通过使用标准化块和字段来实现可扩展性需求。 在tcpreplay重放数据包的时候&#xff0c;手里只有pcapng文件&#…

【牛客网题目】合并k个已排序的链表

目录 描述 题目分析 描述 合并 k 个升序的链表并将结果作为一个升序的链表返回其头节点。 数据范围&#xff1a;节点总数 0≤n≤5000&#xff0c;每个节点的val满足∣val∣<1000 要求&#xff1a;时间复杂度 O(nlogn) 示例1 输入&#xff1a;[{1,2,3},{4,5,6,7}]返回值…

地质灾害监测方案(地质灾害监测原理与方法)

我国坡地较多,地质灾害时有发生,给人民生命财产安全和经济建设造成严重威胁。采用工业物联网技术进行地质灾害监测,可以实现对山体移动、边坡变形等地质灾害的预警和实时监测,保护人民生命财产安全。现提出如下地质灾害监测方案: 1. 监测场景:针对易发地质灾害的区域,如矿山边坡…

有效的价格管理手段

品牌产品的价格定位是非常严肃的事情&#xff0c;尤其像新品发售期间&#xff0c;价格的波动会对销量影响非常大&#xff0c;所以产品定价的稳定性关系到品牌发展&#xff0c;同时也会影响经销商的销售热情&#xff0c;所以品牌需要对价格进行管理&#xff0c;维持住价格的定价…

分支创建查看切换

1、初始化git目录&#xff0c;创建文件并将其推送到本地库 git init echo "123" > hello.txt git add hello.txt git commit -m "first commit" hello.txt$ git init Initialized empty Git repository in D:/Git/git-demo/.git/ AdministratorDESKT…

elementUI textarea可自适应文本高度的文本域

效果图; 通过设置 autosize 属性可以使得文本域的高度能够根据文本内容自动进行调整&#xff0c;并且 autosize 还可以设定为一个对象&#xff0c;指定最小行数和最大行数。 <el-inputtype"textarea"autosizeplaceholder"请输入内容"v-model"te…

书单文案素材哪里找?怎么做成视频?

在当今信息爆炸的时代&#xff0c;越来越多的人开始关注书单并希望分享自己的阅读经验。但是&#xff0c;很多人可能不知道如何寻找书单文案素材以及如何将其制作成视频。本文将为大家介绍一些寻找书单文案素材和制作书单视频的方法。 寻找书单文案素材 1.书单推荐网站 除了书…

前端如何将后台数组进行等分切割

前端如何切割数组 目标&#xff1a;前端需要做轮播&#xff0c;一屏展示12个&#xff0c;后端返回的数组需要进行切割&#xff0c;将数据以12为一组进行分割 环境&#xff1a;vue3tselement plus 代码如下&#xff1a; function divideArrayIntoEqualParts(array, chunkSiz…

输出归一化位置式PID(COTRUST完整梯形图代码)

SMART PLC单自由度和双自由度位置式PID的完整源代码,请参看下面文章链接: 位置式PID(S7-200SMART 单自由度、双自由度梯形图源代码)_RXXW_Dor的博客-CSDN博客有关位置型PID和增量型PID的更多详细介绍请参看PID专栏的相关文章,链接如下:SMART PLC增量型PID算法和梯形图代码…

pycharm 下jupyter noteobook显示黑白图片不正常

背景现象&#xff1a; 1、显示一张黑白图片&#xff0c;颜色反过来了。 from IPython.display import display source Image.open(examples/images/forest_pruned.bmp) display(source) 2、原因&#xff1a; 是pycharm会在深色皮肤下默认反转jupyter notebook输出图片的颜…

【python爬虫】13.吃什么不会胖(爬虫实操练习)

文章目录 前言项目实操明确目标分析过程代码实现 前言 吃什么不会胖——这是我前段时间在健身时比较关注的话题。 相信很多人&#xff0c;哪怕不健身&#xff0c;也会和我一样注重饮食的健康&#xff0c;在乎自己每天摄入的食物热量。 不过&#xff0c;生活中应该很少有人会…

【Docker】 08-Dockerfile

什么是Dockerfile Dockerfile可以认为是Docker镜像的描述文件&#xff0c;是由一系列命令和参数构成的教程&#xff0c;主要作用是用来构建docker镜像的构建文件。 Dockerfile解析过程 Dockerfile的保留命令 保留字作用FROM当前镜像是基于哪个镜像的 第一个指令必须是FROMMA…

做一个答题小程序需要多少钱

做一个答题小程序需要多少钱呢&#xff1f;相信这是很多想做答题小程序的小伙伴非常关心的一个问题&#xff0c;那么今天我们就来分析一下做一个影响答题小程序价格的主要功能因素。 答题小程序开发成本的高低很大一部分是由具体的功能需求决定的&#xff0c;比如个人答题模式…

rk3399 linux 5.10 usb 2.0设备上电概率性注册失败

多次开关机&#xff0c;发现usb hub和4G都通信失败了&#xff0c;这就有点奇怪了&#xff0c;按理说usb驱动是没啥问题的 先查看usb log rootlinaro-alip:/# dmesg | grep usb [ 1.723797] usbcore: registered new interface driver usbfs [ 1.723828] usbcore: regis…

苹果平板如何录屏?这个方法亲测有效!

“苹果的平板可以录屏吗&#xff0c;买了个平板拿来上网课&#xff0c;但是老师讲课速度太快了&#xff0c;就想说录下来&#xff0c;可是找不到哪里可以录制&#xff0c;有人知道苹果平板如何录屏吗&#xff1f;” 苹果平板作为一款功能强大的设备&#xff0c;不仅适用于日常…

全网最全MySQL锁全面解析

目录 1. 锁的分类 1.1 从操作类型划分&#xff0c;分为读锁和写锁&#xff1b; 1.2 从锁的粒度划分&#xff0c;分为全局锁&#xff0c;表锁&#xff0c;页锁&#xff0c;行锁&#xff1b; 1.3 从锁的态度划分&#xff0c;分为乐观锁和悲观锁&#xff1b; 2. 读锁和写锁 …

说说JavaScript与DOM之间的关系

dom&#xff08;文档对象模型&#xff09;是JavaScript的一个组成部分&#xff0c;它为JavaScript提供处理网页内容的方法和接口&#xff1b;JavaScript对网页进行的所有操作都是通过DOM进行的。 完整的 JavaScript 是由以下三个部分组成&#xff1a; 核心&#xff08;ECMAScri…

elementui el-table在有summary-method时,table数据行将合计行遮挡住了

前端使用框架&#xff1a;elementUI 使用组件&#xff1a;el-table 在表格内添加合计了合计行&#xff0c;根据业务多次调用数据渲染画面后&#xff0c;偶然导致画面变成如下图所示&#xff0c;table的数据行将合计行遮挡住了&#xff0c;且这个现象有时候好用&#xff0c;有…

界面控件DevExpress .NET应用安全 Web API v23.1亮点:支持Swagger模式

DevExpress拥有.NET开发需要的所有平台控件&#xff0c;包含600多个UI控件、报表平台、DevExpress Dashboard eXpressApp 框架、适用于 Visual Studio的CodeRush等一系列辅助工具。 DevExpress 今年第一个重要版本v23.1日前已正式发布了&#xff0c;该版本拥有众多新产品和数十…