封装悬浮球组件,文件名s-icons.vue
<template>
<div ref="icons" class="icons-container" :style="{ left: left + 'px', top: top + 'px' }">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'FloatIcons',
props: {
// 滚动id
scroller: {
type: String,
default: ''
},
// 距离上有下左的安全距离
padding: {
type: String,
default: '10 10 10 10'
},
// 初始位置距离底部的距离
bottom: {
type: Number,
default: 0
}
},
data () {
return {
timer: null,
currentTop: 0,
clientWidth: 0,
clientHeight: 0,
itemWidth: 0,
itemHeight: 0,
left: null,
top: null
}
},
computed: {
// 滚动对象,默认空返回window
scrollContainer () {
if (this.scroller === '') {
return window
} else {
return document.getElementById(this.scroller)
}
},
// 安全距离
safeArea () {
return this.padding.split(' ')
}
},
created () {
// 屏幕宽度
this.clientWidth = document.documentElement.clientWidth
// 屏幕高度
this.clientHeight = document.documentElement.clientHeight
},
mounted () {
this.$nextTick(() => {
// 获取滚动元素
this.scrollContainer.addEventListener('scroll', this.handleScrollStart)
const div = this.$refs.icons
// 获取宽度
this.itemWidth = this.$refs.icons.offsetWidth
this.itemHeight = this.$refs.icons.offsetHeight
// 设置位置
this.left = this.clientWidth - this.itemWidth - this.safeArea[1]
this.top = this.clientHeight - this.itemWidth - this.bottom
div.addEventListener('touchstart', () => {
div.style.transition = 'none'
})
div.addEventListener('touchmove', e => {
// 阻止默认动作
e.preventDefault()
if (e.targetTouches.length === 1) {
const touch = event.targetTouches[0]
this.left = touch.clientX - this.itemWidth / 2
this.top = touch.clientY - this.itemHeight / 2
}
})
div.addEventListener('touchend', () => {
div.style.transition = 'all 0.3s'
// 手指放开left位置
if (this.left > this.clientWidth / 2) {
this.left = this.clientWidth - this.itemWidth - this.safeArea[1]
} else {
this.left = this.safeArea[3]
}
// 手指放开top位置
if (this.top < this.safeArea[0]) {
this.top = this.safeArea[0]
} else if (this.top > this.clientHeight - this.itemHeight - this.safeArea[2]) {
// 不低于最低
// this.top = this.clientHeight - this.itemHeight - this.safeArea[2]
this.top = this.clientHeight - this.itemHeight
}
})
})
},
beforeDestroy () {
this.scrollContainer.removeEventListener('scroll', this.handleScrollStart)
},
methods: {
// 滚动时候执行
handleScrollStart () {
this.timer && clearTimeout(this.timer)
this.timer = setTimeout(() => {
this.handleScrollEnd()
}, 300)
this.currentTop = document.documentElement.scrollTop || document.body.scrollTop
if (this.left > this.clientWidth / 2) {
this.left = this.clientWidth - this.itemWidth / 2
} else {
this.left = -this.itemWidth / 2
}
},
handleScrollEnd () {
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop
if (scrollTop === this.currentTop) {
if (this.left > this.clientWidth / 2) {
this.left = this.clientWidth - this.itemWidth - this.safeArea[1]
} else {
this.left = this.safeArea[3]
}
clearTimeout(this.timer)
}
}
}
}
</script>
<style lang="less" scoped>
.icons-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: fixed;
background: #ffffff;
box-shadow: 0px 2px 4px 0px rgba(0, 97, 209, 0.1);
border: 1px solid rgba(21, 95, 186, 0.1);
// border-radius: 50%;
z-index: 1000;
transition: all 0.3s;
}
</style>
在封装了一层组件引入s-icons.vue 文件,文件名goToHome.vue
<template>
<float-icons class="icons-warp" :bottom="bottom" :scroller="scroller">
<div class="float-icon-item" @click="goHomeClick">
<div class="home-title">返回顶部</div>
</div>
</float-icons>
</template>
<script>
import FloatIcons from './s-icons'
export default {
components: {
'float-icons': FloatIcons
},
props: {
// 滚动id
scroller: {
type: String,
default: ''
},
// 初始位置距离底部的距离
bottom: {
type: Number,
default: 60
}
},
created () {
},
methods: {
goHomeClick () {
// 跳转其他页面
// window.history.go(-(window.history.length - 2))
// 回到顶部
window.scrollTo({
top: 0,
left: 0,
behavior: 'smooth' // 平滑滚动效果
})
}
}
}
</script>
<style lang="less" scoped>
.icons-warp {
border-radius: 50%;
.float-icon-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
width: 52px;
height: 52px;
.home-title {
font-size: 14px;
font-weight: 400;
color: #1763bf;
}
}
}
</style>
最后一步,引入到页面中
<template>
<div class="main">
<h1>顶部</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>11111</h1>
<h1>底部</h1>
<GoToHome :bottom="bottom" />
</div>
</template>
<script>
import GoToHome from './goToHome.vue'
export default {
data () {
return {
bottom: 105
}
},
methods: {
},
components: {
GoToHome
}
}
</script>
<style lang="less" scoped>
</style>