vue开发一个类似于手机淘宝中猜你喜欢的功能组件
组件代码:
<!--
* @Author: your name
* @Date: 2020-05-24 16:02:57
* @LastEditTime: 2020-05-27 16:23:09
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: /shopping-mall-template/src/components/MiddlePage/index.vue
-->
<template>
<div ref="container" class="vvi-middle-page-container">
<div ref="wrap" class="vvi-middle-page-wrap">
<div ref="tab" class="vvi-middle-page-tab">
<slot name="tab">
<ul class="vvi-middle-page-tab__items">
<li
v-for="n in 5"
:key="n"
class="vvi-middle-page-tab__item"
@touchstart="select"
>
<div class="title">全部</div>
<div class="subtitle">猜你喜欢</div>
</li>
<li v-show="activeShow" ref="activebar" class="active-bar" />
</ul>
</slot>
</div>
<div ref="content" class="vvi-middle-page-content">
<slot name="content">
<h1 v-for="n in numbers" :key="n">{{ `middle-page-content-${n}` }}</h1>
</slot>
<div v-if="isLoading" class="loading">
<span v-for="n in 8" :key="n" />
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'MiddlePage',
props: {
tabTop: {
type: Number,
default: 30
}
},
data() {
return {
activeShow: false,
isLoading: false,
count: 0,
numbers: 20
}
},
computed: {
parentHeight() {
let h = 0
this.$refs.container.parentElement.children.forEach(item => {
h += item.clientHeight
})
return h
},
flag: {
get() {
return this.count
},
set(v) {
this.count = v
}
}
},
mounted() {
this.$refs.container.parentElement.addEventListener(
'scroll',
this.MCU,
true
)
},
beforeDestroy() {
this.$refs.container.parentElement.removeEventListener('scroll', this.MCU)
},
methods: {
LoadMore() {},
removeStyle(nodes) {
nodes.forEach(item => {
item.removeAttribute('style')
})
},
/** 副控制器 */
SCU(self, parent, container, content, tab) {
if (container.scrollTop < this.flag) {
// console.log('向下')
if (container.scrollTop < 5) {
self.activeShow = false
self.removeStyle([container, tab, parent, content])
document.getElementsByClassName('subtitle').forEach(item => {
item.style.display = 'block'
})
}
} else {
// console.log('向上')
// console.log(`container.scrollTop:${container.scrollTop}`)
// console.log(`差值:${content.clientHeight - container.clientHeight}`)
if (
container.scrollTop ===
content.clientHeight - container.clientHeight
) {
self.isLoading = true
setTimeout(() => {
self.numbers += 20
self.isLoading = false
}, 3 * 1000)
}
}
setTimeout(() => {
self.flag = container.scrollTop
}, 0)
},
MCU(e) {
const container = this.$refs.container // 组件外层容器
const parent = container.parentElement
const tab = this.$refs.tab || null // 页中页 tab 导航
const content = this.$refs.content // 页中页内容部分容器
const _Top = container.getBoundingClientRect().top // 组件顶部距视口顶部高度
const self = this
if (_Top <= this.tabTop) {
parent.style.cssText = `overflow: hidden;` // 使父元素不可滚动
parent.scrollTop =
this.parentHeight - container.clientHeight - this.tabTop // 重新定位父元素位置
tab.style.cssText = `position: fixed; top: ${this.tabTop}px; background-color: #fff; height: 1rem`
document.getElementsByClassName('subtitle').forEach(item => {
item.style.display = 'none'
})
content.style.cssText = `padding-top: ${tab.clientHeight}px`
container.style.cssText = `overflow: scroll;`
this.activeShow = true
container.addEventListener('scroll', this.SCU(self, parent, container, content, tab), true)
} else {
// container.removeEventListener('scroll', SCU)
}
},
select(e) {
const activeBar = this.$refs.activebar
const target = e.currentTarget
activeBar.style.cssText = `transform: translateX(${target.offsetLeft}px); display: ${activeBar.style.display}`
}
}
}
</script>
<style lang="scss" scoped>
.vvi-middle-page-container {
position: relative;
height: 100vh;
width: 100%;
background-color: #f0f0f0;
overflow: hidden;
z-index: 99999;
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE 10+ */
&::-webkit-scrollbar {
display: none;
}
}
.vvi-middle-page-wrap {
position: relative;
height: 100%;
width: 100%;
}
.vvi-middle-page-tab {
position: relative;
background-color: transparent;
width: 100%;
height: 1.9738rem;
padding: 0.4237rem 0.17rem;
transition: height 0.5s linear;
.vvi-middle-page-tab__items {
display: flex;
justify-content: space-between;
align-items: center;
height: 100%;
.active-bar {
position: absolute;
bottom: 10%;
width: 16%;
height: 3px;
background-color: red;
transform: translateX(10%);
transition: all 0.5s linear;
}
.vvi-middle-page-tab__item {
position: relative;
display: block;
width: 20%;
text-align: center;
touch-action: none;
&::after {
position: absolute;
right: 0;
top: 5%;
content: "";
display: block;
width: 1px;
height: 90%;
background-color: #888;
}
&:nth-child(5)::after {
display: none;
}
.title {
width: 100%;
font-size: 1.5em;
font-weight: bold;
}
.subtitle {
font-size: 1.1em;
width: 100%;
color: #888;
margin-top: 0.5em;
transition: display 2s linear;
}
}
}
}
.vvi-middle-page-content {
overflow: hidden;
padding-left: 0.17rem;
padding-right: 0.17rem;
background-color: yellow;
padding-bottom: 2rem;
box-sizing: border-box;
}
.loading {
position: relative;
width: 1rem;
height: 1rem;
margin: 0 auto;
margin-top: .1rem;
span {
position: absolute;
display: inline-block;
width: 0.3rem;
height: 0.1rem;
border-top-left-radius: 0.05rem;
border-bottom-left-radius: 0.05rem;
background: lightgreen;
animation: load 1.04s ease infinite;
}
@keyframes load {
0% {
opacity: 1;
}
100% {
opacity: 0.2;
}
}
}
.loading span {
&:nth-child(1) {
left: 0;
top: 50%;
margin-top: -0.05rem;
animation-delay: 0.13s;
}
&:nth-child(2) {
left: 0.1rem;
top: 0.2rem;
transform: rotate(45deg);
animation-delay: 0.26s;
}
&:nth-child(3) {
left: 50%;
top: 0.1rem;
margin-left: -.15rem;
transform: rotate(90deg);
animation-delay: 0.39s;
}
&:nth-child(4) {
right: 0.1rem;
top: 0.2rem;
transform: rotate(135deg);
animation-delay: 0.52s;
}
&:nth-child(5) {
right: 0;
top: 50%;
transform: rotate(180deg);
animation-delay: 0.65s;
}
&:nth-child(6) {
right: 0.1rem;
bottom: 0.2rem;
transform: rotate(225deg);
animation-delay: 0.78s;
}
&:nth-child(7) {
left: 50%;
bottom: 0.1rem;
margin-left: -.15rem;
transform: rotate(270deg);
animation-delay: 0.91s;
}
&:nth-child(8) {
left: 0.1rem;
bottom: 0.2rem;
transform: rotate(315deg);
animation-delay: 1.04s;
}
}
</style>
调用方法:
<template>
<div class="test scrollbar-hidden">
<div class="box" />
<div class="box2" />
<middle-page>
<!-- <template v-slot:tab>
<ul>
<li>abc</li>
</ul>
</template> -->
<!-- <template v-slot:content>
<ul>
<li>gkl</li>
</ul>
</template> -->
</middle-page>
</div>
</template>
<script>
import MiddlePage from '@/components/MiddlePage'
export default {
name: 'ShoppingCar',
components: {
MiddlePage
},
data() {
return {
mark: 0
}
},
mounted() {
this.show()
this.getElementInfo()
},
methods: {
/**
* 1. 获取要移动的元素
*/
getElementInfo() {
},
show() {
// const styleSheets = document.styleSheets
// console.log(this.$store.getters.tabbar.index)
}
}
}
</script>
<style lang='scss' scoped>
.test {
width: 100%;
height: 100%;
overflow: scroll;
}
.box {
height: 3rem;
width: 100%;
background-color: green;
}
.box2 {
background-color: orange;
width: 100%;
height: 3rem;
}
</style>
组件自带部分样式,可自行插入slot,思路有点乱,如果有代码优化的想法,请给我留言,谢谢。