美文网首页
H5: 仿小红书翻页

H5: 仿小红书翻页

作者: 春暖花已开 | 来源:发表于2023-11-07 17:40 被阅读0次
效果图
<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>

相关文章

网友评论

      本文标题:H5: 仿小红书翻页

      本文链接:https://www.haomeiwen.com/subject/teefwdtx.html