一、大致效果图
默认:
说明:使用非脚手架开发,脚手架开发使用 ==> 可直接新建.vue文件,复制代码过去即可。
二、主内容
2.0 页面调用
<go-top :nav-data="navData" ref="backTop" @handle-click="getHandleClick"></go-top>
2.1 数据获取
const vm = new Vue({
el: '#view',
data() {
return {
testData: [],
navData: []
}
},
created() {
// 页面假数据模拟
for (let i = 0; i < 200; i++) {
this.testData.push(`testScroll-${i + 1}`)
}
// `axios` 获取导航数据
axios.get('json/demo-goTop.json').then(res => {
this.navData = res.data.navData
}).catch(err => {
console.log(err)
})
},
methods: {
// 获取子组件点击事件传的值
getHandleClick(data, index, event) {
switch (data.clickType) {
case 0:
return false
case 1:
window.open(data.urlPath)
break
case 2:
window.open(data.urlPath, '_blank',
'width=300,height=300,menubar=no,toolbar=no, status=no,scrollbars=yes')
break
case 3:
this.$refs.backTop.goTopClick()
break
}
}
}
})
2.2 组件
Vue.component('go-top', {
template: `
<div class="top-warp" v-if="navData.length">
<div class="top-ul">
<div class="top-li" v-for="(val,key) in navData" :key="key" v-if="val.isShowTop" :style="bgStyle(key, oneNavHover)"
@click="handleClickData(val, key, $event)" @mouseover="addHoverStyle(key)" @mouseleave="cleanHoverStyle">
<i class="iconfont" :class="val.icon"></i>
<div class="top-li-msg" :class="{'top-QR' : val.children.length}" :style="bgStyle(key, twoNavHover)">
<span class="top-triangle" :style="brColor(key, twoNavHover)"></span>
<span v-if="!val.children.length">{{val.title}}</span>
<div class="top-img-flex" v-else>
<div class="top-img" v-for="(v,k) in val.children" :key="k">
<img :src="v.imgPath">
<span>{{v.msg}}</span>
</div>
</div>
</div>
</div>
</div>
</div>
`,
props: {
navData: {
type: Array,
default: () => []
},
speed: {
type: Number,
default: 100
},
showBtnOffset: {
type: Number,
default: 200
},
oneNavHover: {
type: String,
default: '#000000'
},
twoNavHover: {
type: String,
default: '#f44444'
}
},
data() {
return {
hoverIndex: null
}
},
mounted() {
window.addEventListener('scroll', this.getScollTop, true)
},
methods: {
// 动态添加样式
bgStyle(key, hoverStyle) {
return {
background: key == this.hoverIndex ? hoverStyle : ''
}
},
brColor(key, hoverStyle) {
return {
borderLeftColor: key == this.hoverIndex ? hoverStyle : ''
}
},
addHoverStyle(index) {
this.hoverIndex = index
},
cleanHoverStyle() {
this.hoverIndex = null
},
// 当前点击
handleClickData(data, index, event) {
this.$emit('handle-click', data, index, event)
},
// 滚动条
getScollTop() {
let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
this.navData.filter(item => {
if (scrollTop > this.showBtnOffset) {
return item.clickType == 3 ? item.isShowTop = true : item.isShowTop = true
} else {
return item.clickType == 3 ? item.isShowTop = false : item.isShowTop = true
}
})
},
// 返回顶部点击
goTopClick() {
let cleanTime = setInterval(() => {
document.documentElement.scrollTop -= this.speed
if (document.documentElement.scrollTop === 0) {
this.cleanHoverStyle()
clearInterval(cleanTime)
}
}, 10)
}
}
})
2.31 Attributes
参数 | 类型 | 默认值 | 说明 |
---|---|---|---|
nav-data | Array | [] | 导航数据(即下面的json ) |
speed | Number | 100 | 滚动条滚动到顶部的速度 |
show-btn-offset | Number | 200 | 返回顶部按钮出现的初始位置 |
one-nav-hover | String | ‘#000000’ | 一级导航鼠标经过背景颜色 |
two-nav-hover | String | ‘#f44444’ | 二级导航背景颜色 |
backTop | String | ref="backTop" |
*想要返回顶部功能,此项必须加上 |
2.32 Events
事件名 | 参数 | 说明 |
---|---|---|
handle-click | data, index, event | 当前项导航点击时,获取子组件的数据,下标及 event。通过 data.clickType 判断点击之后,具体要做什么,返回顶部按钮点击时,需要手动触发方法:this.$refs.backTop.goTopClick()
|
2.4 json 数据格式
{
"navData": [{
"icon": "icon-fanhuidingbu",
"urlPath": "https://www.csdn.net/",
"title": "返回顶部",
"isShowTop": false,
"clickType": 3,
"children": []
},
{
"icon": "icon-erweima",
"urlPath": "https://www.csdn.net/",
"title": "",
"clickType": 0,
"isShowTop": true,
"children": [{
"msg": "关注公众号",
"imgPath": "https://g.csdnimg.cn/side-toolbar/1.2/images/qr_wechat.png"
},
{
"msg": "下载APP",
"imgPath": "https://g.csdnimg.cn/side-toolbar/1.2/images/qr_app.png"
}
]
},
{
"icon": "icon-2zaixiankefucheng",
"urlPath": "https://www.csdn.net/",
"title": "在线客服",
"clickType": 1,
"isShowTop": true,
"children": []
},
{
"icon": "icon-fankuiyijian",
"urlPath": "https://www.csdn.net/",
"title": "意见反馈",
"clickType": 2,
"isShowTop": true,
"children": []
}
]
}
三、全部html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>vue返回顶部组件</title>
<link rel="stylesheet" type="text/css" href="//at.alicdn.com/t/font_1078306_8ycnernwlnu.css" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<style type="text/css">
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body,
section {
width: 100%;
height: 100%;
background: #F5F5F5;
}
[v-clock] {
display: none;
}
/* start */
.top-warp {
width: 38px;
min-height: 38px;
position: fixed;
right: 20px;
bottom: 60px;
cursor: pointer;
background: white;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
}
.top-ul {
display: flex;
flex-direction: column;
}
.top-li {
width: 100%;
height: 38px;
display: flex;
position: relative;
}
.top-li i {
margin: auto;
}
.top-li-msg {
position: absolute;
height: 38px;
line-height: 38px;
left: -100px;
width: 74px;
color: white;
font-size: 14px;
text-align: center;
transition: left .5s ease;
transform-style: preserve-3d;
visibility: hidden;
opacity: 0;
}
.top-triangle {
position: absolute;
right: -5px;
top: 50%;
transform: translateY(-50%);
width: 0;
height: 0;
border-style: solid;
border-width: 5px 0 5px 5px;
border-color: transparent transparent transparent #f44444;
}
.top-QR {
height: 125px;
width: 200px;
top: 50%;
left: -226px;
transform: translateY(-50%);
z-index: 1;
display: flex;
}
.top-nav {
background: #f44444;
}
.top-img-flex {
display: flex;
width: 100%;
}
.top-img {
flex: 1;
padding: 10px;
font-size: 14px;
display: flex;
flex-direction: column;
margin: auto;
}
.top-img img {
width: 80px;
height: 80px;
margin: auto;
}
.top-li:hover {
color: white;
}
.top-li:hover .top-li-msg {
left: -74px;
transition: left .5s ease;
visibility: visible;
opacity: 1;
}
.top-li:hover .top-QR {
left: -200px;
}
/* end */
</style>
</head>
<body>
<section id="view" clock>
<div v-for="v in testData" :key="v">{{v}}</div>
<go-top :nav-data="navData" :speed="300" one-nav-hover="#09F" two-nav-hover="green" @handle-click="getHandleClick"
:show-btn-offset="1000" ref="backTop"></go-top>
</section>
</body>
<script type="text/javascript">
Vue.component('go-top', {
template: `
<div class="top-warp" v-if="navData.length">
<div class="top-ul">
<div class="top-li" v-for="(val,key) in navData" :key="key" v-if="val.isShowTop" :style="bgStyle(key, oneNavHover)"
@click="handleClickData(val, key, $event)" @mouseover="addHoverStyle(key)" @mouseleave="cleanHoverStyle">
<i class="iconfont" :class="val.icon"></i>
<div class="top-li-msg" :class="{'top-QR' : val.children.length}" :style="bgStyle(key, twoNavHover)">
<span class="top-triangle" :style="brColor(key, twoNavHover)"></span>
<span v-if="!val.children.length">{{val.title}}</span>
<div class="top-img-flex" v-else>
<div class="top-img" v-for="(v,k) in val.children" :key="k">
<img :src="v.imgPath">
<span>{{v.msg}}</span>
</div>
</div>
</div>
</div>
</div>
</div>
`,
props: {
navData: {
type: Array,
default: () => []
},
speed: {
type: Number,
default: 100
},
showBtnOffset: {
type: Number,
default: 200
},
oneNavHover: {
type: String,
default: '#000000'
},
twoNavHover: {
type: String,
default: '#f44444'
}
},
data() {
return {
hoverIndex: null
}
},
mounted() {
window.addEventListener('scroll', this.getScollTop, true)
},
methods: {
// 动态添加样式
bgStyle(key, hoverStyle) {
return {
background: key == this.hoverIndex ? hoverStyle : ''
}
},
brColor(key, hoverStyle) {
return {
borderLeftColor: key == this.hoverIndex ? hoverStyle : ''
}
},
addHoverStyle(index) {
this.hoverIndex = index
},
cleanHoverStyle() {
this.hoverIndex = null
},
// 当前点击
handleClickData(data, index, event) {
this.$emit('handle-click', data, index, event)
},
// 滚动条
getScollTop() {
let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
this.navData.filter(item => {
if (scrollTop > this.showBtnOffset) {
return item.clickType == 3 ? item.isShowTop = true : item.isShowTop = true
} else {
return item.clickType == 3 ? item.isShowTop = false : item.isShowTop = true
}
})
},
// 返回顶部点击
goTopClick() {
let cleanTime = setInterval(() => {
document.documentElement.scrollTop -= this.speed
if (document.documentElement.scrollTop === 0) {
this.cleanHoverStyle()
clearInterval(cleanTime)
}
}, 10)
}
}
})
</script>
<script type="text/javascript">
const vm = new Vue({
el: '#view',
data() {
return {
testData: [],
navData: []
}
},
created() {
// 页面假数据模拟
for (let i = 0; i < 200; i++) {
this.testData.push(`testScroll-${i + 1}`)
}
// `axios` 获取导航数据
axios.get('json/demo-goTop.json').then(res => {
this.navData = res.data.navData
}).catch(err => {
console.log(err)
})
},
methods: {
// 获取子组件点击事件传的值
getHandleClick(data, index, event) {
switch (data.clickType) {
case 0:
return false
case 1:
window.open(data.urlPath)
break
case 2:
window.open(data.urlPath, '_blank',
'width=300,height=300,menubar=no,toolbar=no, status=no,scrollbars=yes')
break
case 3:
this.$refs.backTop.goTopClick()
break
}
}
}
})
</script>
</html>