You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
exam/BackToTop/index.vue

156 lines
4.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<!-- 使用 Vue 的过渡组件根据传入的 transitionName 决定过渡效果 -->
<transition :name="transitionName">
<!-- visible true 时显示返回顶部按钮应用自定义样式 -->
<div v-show="visible" :style="customStyle" class="back-to-ceiling" @click="backToTop">
<!-- 返回顶部按钮的图标 -->
<svg width="16" height="16" viewBox="0 0 17 17" xmlns="http://www.w3.org/2000/svg" class="Icon Icon--backToTopArrow" aria-hidden="true" style="height:16px;width:16px"><path d="M12.036 15.59a1 1 0 0 1-.997.995H5.032a.996.996 0 0 1-.997-.996V8.584H1.03c-1.1 0-1.36-.633-.578-1.416L7.33.29a1.003 1.003 0 0 1 1.412 0l6.878 6.88c.782.78.523 1.415-.58 1.415h-3.004v7.004z" /></svg>
</div>
</transition>
</template>
<script>
export default {
// 组件名称
name: 'BackToTop',
// 组件接收的属性
props: {
// 滚动高度达到该值时显示返回顶部按钮,默认 400
visibilityHeight: {
type: Number,
default: 400
},
// 返回的滚动位置,默认 0
backPosition: {
type: Number,
default: 0
},
// 自定义按钮样式
customStyle: {
type: Object,
default: function() {
return {
right: '50px',
bottom: '50px',
width: '40px',
height: '40px',
'border-radius': '4px',
'line-height': '45px',
background: '#e7eaf1'
}
}
},
// 过渡效果名称,默认 'fade'
transitionName: {
type: String,
default: 'fade'
}
},
// 组件的数据
data() {
return {
// 控制返回顶部按钮的显示与隐藏
visible: false,
// 定时器,用于滚动动画
interval: null,
// 标记是否正在滚动
isMoving: false
}
},
// 组件挂载后执行的钩子函数
mounted() {
// 监听窗口滚动事件
window.addEventListener('scroll', this.handleScroll)
},
// 组件销毁前执行的钩子函数
beforeDestroy() {
// 移除窗口滚动事件监听
window.removeEventListener('scroll', this.handleScroll)
// 清除定时器
if (this.interval) {
clearInterval(this.interval)
}
},
// 组件的方法
methods: {
// 处理滚动事件
handleScroll() {
// 当滚动高度超过 visibilityHeight 时显示按钮
this.visible = window.pageYOffset > this.visibilityHeight
},
// 返回顶部的方法
backToTop() {
// 如果正在滚动则不执行
if (this.isMoving) return
// 获取当前滚动位置
const start = window.pageYOffset
let i = 0
// 标记为正在滚动
this.isMoving = true
// 设置定时器,实现滚动动画
this.interval = setInterval(() => {
// 使用缓动函数计算下一个滚动位置
const next = Math.floor(this.easeInOutQuad(10 * i, start, -start, 500))
if (next <= this.backPosition) {
// 滚动到指定位置
window.scrollTo(0, this.backPosition)
// 清除定时器
clearInterval(this.interval)
// 标记为滚动结束
this.isMoving = false
} else {
// 滚动到计算出的位置
window.scrollTo(0, next)
}
i++
}, 16.7)
},
/**
* 缓动函数,实现二次方的缓入缓出效果
* @param {number} t - 当前时间(从动画开始到现在的时间)
* @param {number} b - 起始值
* @param {number} c - 变化量(最终值 - 起始值)
* @param {number} d - 持续时间
* @returns {number} - 当前时间对应的数值
*/
easeInOutQuad(t, b, c, d) {
if ((t /= d / 2) < 1) return c / 2 * t * t + b
return -c / 2 * (--t * (t - 2) - 1) + b
}
}
}
</script>
<style scoped>
/* 返回顶部按钮的基础样式 */
.back-to-ceiling {
position: fixed;
display: inline-block;
text-align: center;
cursor: pointer;
}
/* 返回顶部按钮的悬停样式 */
.back-to-ceiling:hover {
background: #d5dbe7;
}
/* 淡入动画的激活状态样式,过渡时间为 0.5 秒 */
.fade-enter-active,
.fade-leave-active {
transition: opacity .5s;
}
/* 淡入动画的起始状态和淡出动画的结束状态样式,透明度为 0 */
.fade-enter,
.fade-leave-to {
opacity: 0
}
/* 返回顶部按钮图标的样式 */
.back-to-ceiling .Icon {
fill: #9aaabf;
background: none;
}
</style>