![](https://img.haomeiwen.com/i2028539/24d7c84dd8849cf1.gif)
效果图
<template>
<div
class="wrapper"
@touchstart="onTouchStart"
@touchmove.stop="onTouchMove"
@touchend="onTouchEnd"
>
<div class="card front-top" :style="[frontTopStyle]">
<span v-for="n in 300" :key="n">{{ dataList[indexInfo.ft] }}</span>
</div>
<div class="card front-bottom" :style="[frontBotStyle]">
<span v-for="n in 300" :key="n">{{ dataList[indexInfo.fb] }}</span>
</div>
<div class="card back-top" :style="[backTopStyle]">
<span v-for="n in 300" :key="n">{{ dataList[indexInfo.bt] }}</span>
</div>
<div class="card back-bottom" :style="[backBotStyle]">
<span v-for="n in 300" :key="n">{{ dataList[indexInfo.bb] }}</span>
</div>
<div class="card next-top" :style="[nextTopStyle]">
<span v-for="n in 300" :key="n">{{ dataList[indexInfo.nt] }}</span>
</div>
<div class="card next-bottom" :style="[nextBotStyle]">
<span v-for="n in 300" :key="n">{{ dataList[indexInfo.nb] }}</span>
</div>
</div>
</template>
<script>
const Direction = {
up: 1,
right: 2,
down: 3,
left: 4
}
const transition = 'all 0.25s'
const zIndex = 98
const zIndex97 = zIndex - 1
const zIndex99 = zIndex + 1
const deg90 = {
transform: 'rotateX(90deg)'
}
const degZeroTransition = {
transform: 'rotateX(0deg)',
transition
}
const degZeroTransitionZ = {
transform: 'rotateX(0deg)',
transition,
zIndex
}
const degZero = {
transform: 'rotateX(0deg)'
}
const min = Math.min
const max = Math.max
const delay = (fn) => {
const ti = setTimeout(() => {
fn()
clearTimeout(ti)
}, 200)
}
function debounce(func, wait) {
let timeout
return function () {
const context = this
const args = arguments
clearTimeout(timeout)
timeout = setTimeout(function () {
func.apply(context, args)
}, wait)
}
}
export default {
data() {
return {
// 下拉刷新的阈值
threshold: 90,
startX: 0,
startY: 0,
indexInfo: {
ft: 0,
fb: 0,
bt: 1,
bb: 1,
nt: 2,
nb: 2
},
scrollInfo: {},
pageIndex: 0,
frontTopStyle: {
zIndex
},
frontBotStyle: {
zIndex
},
backTopStyle: {},
backBotStyle: {
zIndex: zIndex97
},
nextTopStyle: {},
nextBotStyle: {},
dataList: [
'0你好啊',
'1你好啊',
'2你好啊',
'3你好啊',
'4你好啊',
'5你好啊',
'6你好啊',
'7你好啊',
'8你好啊',
'9你好啊'
]
}
},
computed: {
refreshDebounce() {
return debounce(() => {
console.log('要执行下拉刷新了')
this.$emit('onPrepareRefresh')
this.scrollInfo = {}
}, 100)
},
noMoreDebounce() {
return debounce(() => {
console.log('滑动到底了')
this.$emit('onNoMore')
}, 100)
},
isVertical() {
return (
this.scrollInfo.direction === Direction.up || this.scrollInfo.direction === Direction.down
)
},
isHorizontal() {
return (
this.scrollInfo.direction === Direction.left ||
this.scrollInfo.direction === Direction.right
)
}
},
methods: {
onTouchStart(e) {
const pos = e.changedTouches[0]
this.startX = pos.clientX
this.startY = pos.clientY
},
onTouchMove(e) {
const pos = e.changedTouches[0]
const deltaX = pos.clientX - this.startX
const deltaY = pos.clientY - this.startY
// 垂直滑动
if (Math.abs(deltaY) > Math.abs(deltaX)) {
if (this.isHorizontal) return
// 往上滑动
if (deltaY < 0) {
if (this.pageIndex >= this.dataList.length - 1) {
this.noMoreDebounce()
return
}
if (this.scrollInfo.direction === Direction.down) {
return
}
this.scrollInfo = {
direction: Direction.up,
deltaY
}
const absDeltaY = Math.abs(deltaY)
if (this.pageIndex % 3 === 0) {
this.nextBotStyle = {}
Object.assign(this.nextTopStyle, { transform: `rotateX(0deg)` })
if (absDeltaY <= 180) {
this.frontBotStyle = {
transform: `rotateX(${absDeltaY / 2}deg)`,
zIndex
}
this.backBotStyle = {
zIndex: zIndex97
}
this.backTopStyle = {
transform: `rotateX(90deg)`,
zIndex: zIndex99
}
} else {
this.frontBotStyle = {
transform: `rotateX(90deg)`,
zIndex
}
this.backTopStyle = {
transform: `rotateX(${min(-(360 - absDeltaY) / 2, 0)}deg)`,
zIndex: zIndex99
}
Object.assign(this.nextBotStyle, { transform: `rotateX(0deg)` })
}
} else if (this.pageIndex % 3 === 1) {
if (absDeltaY <= 180) {
this.backBotStyle = {
transform: `rotateX(${absDeltaY / 2}deg)`,
zIndex
}
this.nextTopStyle = {
transform: 'rotateX(90deg)',
zIndex
}
} else {
this.backBotStyle = {
transform: 'rotateX(90deg)',
zIndex
}
this.nextTopStyle = {
transform: `rotateX(${min(-(360 - absDeltaY) / 2, 0)}deg)`,
zIndex
}
}
} else if (this.pageIndex % 3 === 2) {
if (absDeltaY <= 180) {
this.frontTopStyle = {
transform: 'rotateX(90deg)',
zIndex: zIndex99
}
this.nextBotStyle = {
transform: `rotateX(${absDeltaY / 2}deg)`,
zIndex
}
} else {
this.nextBotStyle = {
transform: 'rotateX(90deg)',
zIndex
}
this.frontTopStyle = {
transform: `rotateX(${min(-(360 - absDeltaY) / 2, 0)}deg)`,
zIndex: zIndex99
}
}
}
}
// 往下滑动
else {
if (this.scrollInfo.direction === Direction.up) {
return
}
this.scrollInfo = {
direction: Direction.down,
deltaY
}
if (this.pageIndex === 0) {
if (deltaY >= this.threshold) {
this.refreshDebounce()
}
return
}
if (this.pageIndex % 3 === 0) {
if (deltaY <= 180) {
this.frontTopStyle = {
transform: `rotateX(${-deltaY / 2}deg)`,
zIndex
}
this.nextBotStyle = {
transform: `rotateX(90deg)`,
zIndex: zIndex
}
} else {
this.frontTopStyle = {
transform: `rotateX(90deg)`,
zIndex
}
this.nextBotStyle = {
transform: `rotateX(${max((360 - deltaY) / 2, 0)}deg)`,
zIndex: zIndex99
}
}
} else if (this.pageIndex % 3 === 1) {
if (deltaY <= 180) {
this.frontTopStyle = {
zIndex
}
this.backTopStyle = {
transform: `rotateX(${-deltaY / 2}deg)`,
zIndex
}
this.backBotStyle = {
zIndex
}
this.frontBotStyle = {
transform: `rotateX(90deg)`,
zIndex: zIndex99
}
} else {
this.backBotStyle = {
zIndex
}
this.backTopStyle = {
transform: `rotateX(90deg)`,
zIndex
}
this.frontBotStyle = {
transform: `rotateX(${max((360 - deltaY) / 2, 0)}deg)`,
zIndex: zIndex99
}
}
} else if (this.pageIndex % 3 === 2) {
this.indexInfo = {
ft: this.pageIndex + 1,
fb: this.pageIndex + 1,
bt: this.pageIndex - 1,
bb: this.pageIndex - 1,
nt: this.pageIndex,
nb: this.pageIndex
}
if (deltaY <= 180) {
this.backTopStyle = {
zIndex
}
this.nextTopStyle = {
transform: `rotateX(${-deltaY / 2}deg)`,
zIndex
}
this.backBotStyle = {
transform: `rotateX(90deg)`,
zIndex: zIndex99
}
} else {
this.backBotStyle = {
transform: `rotateX(${max((360 - deltaY) / 2, 0)}deg)`,
zIndex: zIndex99
}
this.nextTopStyle = {
transform: `rotateX(90deg)`,
zIndex
}
}
}
}
}
// 水平滑动
else {
if (this.isVertical) return
console.log('水平', deltaX)
// 向右滑动
if (deltaX > 0) {
this.scrollInfo = {
direction: Direction.right,
deltaY
}
}
// 向左滑动
else {
this.scrollInfo = {
direction: Direction.left,
deltaY
}
}
}
},
onTouchEnd() {
const { deltaY, direction } = this.scrollInfo
const y = Math.abs(deltaY)
// 往上滑动
if (direction === Direction.up) {
if (this.pageIndex % 3 === 0) {
if (y <= 180) {
Object.assign(this.frontBotStyle, {
transform: 'rotateX(0deg)',
transition
})
}
// >180
else {
this.indexInfo = {
ft: this.pageIndex,
fb: this.pageIndex,
bt: this.pageIndex + 1,
bb: this.pageIndex + 1
}
delay(() => {
this.indexInfo = {
ft: this.pageIndex - 1,
fb: this.pageIndex - 1,
bt: this.pageIndex,
bb: this.pageIndex,
nt: this.pageIndex + 1,
nb: this.pageIndex + 1
}
this.frontTopStyle = {
zIndex: zIndex97
}
this.frontBotStyle = {
zIndex: zIndex97
}
this.backTopStyle = {
zIndex
}
this.backBotStyle = {
zIndex
}
this.nextTopStyle = {
zIndex: zIndex97
}
this.nextBotStyle = {
zIndex: zIndex97
}
})
this.pageIndex++
this.frontBotStyle = deg90
this.backTopStyle = {
transform: 'rotateX(0deg)',
transition,
zIndex
}
}
} else if (this.pageIndex % 3 === 1) {
if (y <= 180) {
Object.assign(this.backBotStyle, {
transform: 'rotateX(0deg)',
transition
})
}
// >180
else {
this.indexInfo = {
bt: this.pageIndex,
bb: this.pageIndex,
nt: this.pageIndex + 1,
nb: this.pageIndex + 1
}
delay(() => {
this.indexInfo = {
ft: this.pageIndex + 1,
fb: this.pageIndex + 1,
bt: this.pageIndex - 1,
bb: this.pageIndex - 1,
nt: this.pageIndex,
nb: this.pageIndex
}
this.frontTopStyle = {
zIndex
}
this.frontBotStyle = {
zIndex
}
this.backTopStyle = {
zIndex: zIndex97
}
this.backBotStyle = {
zIndex: zIndex97
}
this.nextTopStyle = {
zIndex
}
this.nextBotStyle = {
zIndex
}
})
this.pageIndex++
this.backBotStyle = deg90
this.nextTopStyle = degZeroTransitionZ
}
} else if (this.pageIndex % 3 === 2) {
if (y <= 180) {
Object.assign(this.nextBotStyle, {
transform: 'rotateX(0deg)',
transition
})
}
// up && >180
else {
this.indexInfo = {
ft: this.pageIndex + 1,
fb: this.pageIndex + 1,
nt: this.pageIndex,
nb: this.pageIndex
}
delay(() => {
this.indexInfo = {
ft: this.pageIndex,
fb: this.pageIndex,
bt: this.pageIndex + 1,
bb: this.pageIndex + 1,
nt: this.pageIndex - 1,
nb: this.pageIndex - 1
}
this.frontTopStyle = {
zIndex
}
this.frontBotStyle = {
zIndex
}
this.backTopStyle = {}
this.backBotStyle = {
zIndex: zIndex97
}
this.nextTopStyle = {}
this.nextBotStyle = {}
})
this.pageIndex++
this.nextBotStyle = degZero
this.frontTopStyle = {
transform: 'rotateX(0deg)',
transition,
zIndex: zIndex99
}
}
} else {
throw Error('onTouchEnd Error')
}
}
// 往下滑动
else if (direction === Direction.down) {
if (this.pageIndex === 0) return
if (this.pageIndex % 3 === 0) {
if (y <= 180) {
this.frontTopStyle = degZeroTransitionZ
}
// >180
else {
this.indexInfo = {
ft: this.pageIndex,
fb: this.pageIndex,
bt: this.pageIndex + 1,
bb: this.pageIndex + 1,
nt: this.pageIndex - 1,
nb: this.pageIndex - 1
}
delay(() => {
this.frontTopStyle = {
zIndex
}
this.frontBotStyle = {
zIndex
}
this.backTopStyle = {
zIndex: zIndex97
}
this.backBotStyle = {
zIndex: zIndex97
}
this.nextTopStyle = {
zIndex
}
this.nextBotStyle = {
zIndex
}
})
this.pageIndex--
this.frontTopStyle = deg90
this.nextBotStyle = {
transform: 'rotateX(0deg)',
transition,
zIndex: zIndex99
}
}
} else if (this.pageIndex % 3 === 1) {
if (y <= 180) {
this.frontTopStyle = {
zIndex: zIndex97
}
Object.assign(this.backTopStyle, {
transform: 'rotateX(0deg)',
transition
})
}
// >180
else {
this.indexInfo = {
ft: this.pageIndex - 1,
fb: this.pageIndex - 1,
bt: this.pageIndex,
bb: this.pageIndex
}
delay(() => {
this.indexInfo = {
ft: this.pageIndex,
fb: this.pageIndex,
bt: this.pageIndex + 1,
bb: this.pageIndex + 1,
nt: this.pageIndex - 1,
nb: this.pageIndex - 1
}
})
this.pageIndex--
this.backTopStyle = deg90
this.frontBotStyle = {
transform: 'rotateX(0deg)',
transition,
zIndex: zIndex99
}
if (this.pageIndex === 0) {
this.nextTopStyle = {}
this.nextBotStyle = {}
}
}
} else if (this.pageIndex % 3 === 2) {
if (y <= 180) {
Object.assign(this.nextTopStyle, {
transform: 'rotateX(0deg)',
transition
})
}
// up && >180
else {
this.indexInfo = {
bt: this.pageIndex - 1,
bb: this.pageIndex - 1,
nt: this.pageIndex,
nb: this.pageIndex
}
delay(() => {
this.indexInfo = {
ft: this.pageIndex - 1,
fb: this.pageIndex - 1,
bt: this.pageIndex,
bb: this.pageIndex,
nt: this.pageIndex + 1,
nb: this.pageIndex + 1
}
this.frontTopStyle = {
zIndex: zIndex97
}
this.frontBotStyle = {
zIndex: zIndex97
}
this.backTopStyle = {
zIndex
}
this.backBotStyle = {
zIndex
}
this.nextTopStyle = {
zIndex: zIndex97
}
this.nextBotStyle = {
zIndex: zIndex97
}
})
this.pageIndex--
this.nextTopStyle = deg90
this.backBotStyle = {
transform: 'rotateX(0deg)',
transition,
zIndex: zIndex99
}
}
} else {
throw Error('onTouchEnd Error')
}
}
this.scrollInfo = {}
}
}
}
</script>
<style scoped>
.wrapper {
font-size: 17px;
background: purple;
color: white;
height: 100vh;
position: relative;
overflow: hidden;
perspective: 3000px;
}
.card {
width: 100vw;
height: 100vh;
position: absolute;
backface-visibility: hidden;
will-change: transform;
}
.front-top {
background: red;
clip-path: inset(0 0 50% 0);
}
.front-bottom {
background: orange;
clip-path: inset(50% 0 0 0);
}
.back-top {
background: grey;
clip-path: inset(0 0 50% 0);
}
.back-bottom {
background: green;
clip-path: inset(50% 0 0 0);
}
.next-top {
background: cyan;
clip-path: inset(0 0 50% 0);
}
.next-bottom {
background: blue;
transform: rotateX(0deg);
clip-path: inset(50% 0 0 0);
}
</style>
网友评论