uniapp: 仿红板报翻页

效果图
<template>
  <view
    class="wrapper"
    @touchstart="onTouchStart"
    @touchmove.stop="onTouchMove"
    @touchend="onTouchEnd"
  >
    <view class="card front-top" :style="[frontTopStyle]">
      <span v-for="n in 300" :key="n">{{ dataList[indexInfo.ft] }}</span>
    </view>
    <view class="card front-bottom" :style="[frontBotStyle]">
      <span v-for="n in 300" :key="n">{{ dataList[indexInfo.fb] }}</span>
    </view>
    <view class="card back-top" :style="[backTopStyle]">
      <span v-for="n in 300" :key="n">{{ dataList[indexInfo.bt] }}</span>
    </view>
    <view class="card back-bottom" :style="[backBotStyle]">
      <span v-for="n in 300" :key="n">{{ dataList[indexInfo.bb] }}</span>
    </view>
    <view class="card next-top" :style="[nextTopStyle]">
      <span v-for="n in 300" :key="n">{{ dataList[indexInfo.nt] }}</span>
    </view>
    <view class="card next-bottom" :style="[nextBotStyle]">
      <span v-for="n in 300" :key="n">{{ dataList[indexInfo.nb] }}</span>
    </view>
  </view>
</template>

<script>
const Direction = {
  up: 1,
  right: 2,
  down: 3,
  left: 4
}

const DELTA_VALUE = 3
const transition = 'all 120ms'
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) * DELTA_VALUE
      const deltaY = (pos.clientY - this.startY) * DELTA_VALUE

      // 垂直滑动
      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)
          
          console.log('上滑动', this.pageIndex);

          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
        
        console.log('下滑', this.pageIndex);

        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) {
            Object.assign(this.backTopStyle, {
              transform: 'rotateX(0deg)',
              transition
            })
            
            delay(() => {
                this.frontTopStyle = {
                  zIndex: zIndex97
                }
            })
          }
          // >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: red;
  clip-path: inset(50% 0 0 0);
}

.back-top {
  background: green;
  clip-path: inset(0 0 50% 0);
}

.back-bottom {
  background: green;
  clip-path: inset(50% 0 0 0);
}

.next-top {
  background: blue;
  clip-path: inset(0 0 50% 0);
}

.next-bottom {
  background: blue;
  transform: rotateX(0deg);
  clip-path: inset(50% 0 0 0);
}
</style>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,753评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,668评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,090评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,010评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,054评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,806评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,484评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,380评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,873评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,021评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,158评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,838评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,499评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,044评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,159评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,449评论 3 374
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,136评论 2 356

推荐阅读更多精彩内容