Maison >interface Web >js tutoriel >Créer un composant de pile coulissante en utilisant vue (tutoriel détaillé)

Créer un composant de pile coulissante en utilisant vue (tutoriel détaillé)

亚连
亚连original
2018-06-01 09:13:562961parcourir

Le composant coulissant empilé de Tantan joue un rôle clé. Voyons comment utiliser Vue pour écrire un composant empilé Tantan. Les amis intéressés peuvent y jeter un œil ensemble

Le rendu est le suivant :

Avant-propos

Bonjour, dis que Tantan doit être familier à tous les programmeurs (après tout, il y a beaucoup de filles). Les composants coulissants empilés de Tantan jouent un rôle clé dans la capacité de transformer les marques en douceur. Voyons comment écrire un composant Stacking Tantan

.

1. Analyse fonctionnelle

Après une utilisation simple, vous constaterez que la fonction coulissante d'empilement est très simple, utilisez simplement une image Le résumé est :

Un bref résumé des points fonctionnels de base inclus :

  • Empilage d'images

  • Glisser la première image

  • Glisser une fois la condition réussie, rebondir après l'échec de la condition

  • Glisser L'image suivante est empilée vers le haut

  • Optimisation de l'expérience

  • Selon les différents points de contact, la première image sera décalée sous différents angles lors du glissement

  • Déterminer si la zone de décalage a réussi à glisser

Mise en œuvre spécifique

Avec les points fonctionnels résumés, notre idée de mise en œuvre des composants sera plus claire

1 Effet d'empilage

L'effet d'image d'empilement est disponible en ligne. Dans un grand nombre d'exemples, les méthodes de mise en œuvre sont similaires, principalement en définissant la perspective et l'origine de la perspective sur le calque parent pour obtenir la perspective de la sous-couche. Après avoir défini la valeur de l'axe Z Translate3d sur la sous-couche, l'empilement. L'effet peut être simulé. Le code spécifique est le suivant

// 图片堆叠dom
 <!--opacity: 0 隐藏我们不想看到的stack-item层级-->
 <!--z-index: -1 调整stack-item层级"-->
<ul class="stack">
 <li class="stack-item" style="transform: translate3d(0px, 0px, 0px);opacity: 1;z-index: 10;"><img src="1.png" alt="01"></li>
 <li class="stack-item" style="transform: translate3d(0px, 0px, -60px);opacity: 1;z-index: 1"><img src="2.png" alt="02"></li>
 <li class="stack-item" style="transform: translate3d(0px, 0px, -120px);opacity: 1;z-index: 1"><img src="3.png" alt="03"></li>
 <li class="stack-item" style="transform: translate3d(0px, 0px, -180px);opacity: 0;z-index: -1"><img src="4.png" alt="04"></li>
 <li class="stack-item" style="transform: translate3d(0px, 0px, -180px);opacity: 0;z-index: -1"><img src="5.png" alt="05"></li>
</ul>
<style>
.stack {
 width: 100%;
 height: 100%;
 position: relative;
 perspective: 1000px; //子元素视距
 perspective-origin: 50% 150%; //子元素透视位置
 -webkit-perspective: 1000px;
 -webkit-perspective-origin: 50% 150%;
 margin: 0;
 padding: 0;
 }
 .stack-item{
 background: #fff;
 height: 100%;
 width: 100%;
 border-radius: 4px;
 text-align: center;
 overflow: hidden;
 }
 .stack-item img {
 width: 100%;
 display: block;
 pointer-events: none;
 }
</style>

Ce qui précède n'est qu'un ensemble de code statique. Ce que nous espérons obtenir est une vue. composant, nous devons donc d'abord créer un modèle de composant stack.vue. Dans le modèle, nous pouvons utiliser v-for pour parcourir le nœud de pile et utiliser :style pour modifier le style de chaque élément. Le code est le suivant

<.>

<template>
 <ul class="stack">
  <li class="stack-item" v-for="(item, index) in pages" :style="[transform(index)]">
  <img :src="item.src">
  </li>
 </ul>
</template>
<script>
export default {
 props: {
 // pages数据包含基础的图片数据
 pages: {
  type: Array,
  default: []
 }
 },
 data () {
 return {
  // basicdata数据包含组件基本数据
  basicdata: {
  currentPage: 0 // 默认首图的序列
  },
  // temporaryData数据包含组件临时数据
  temporaryData: {
  opacity: 1, // 记录opacity
  zIndex: 10, // 记录zIndex
  visible: 3 // 记录默认显示堆叠数visible
  }
 }
 },
 methods: {
 // 遍历样式
 transform (index) {
  if (index >= this.basicdata.currentPage) {
  let style = {}
  let visible = this.temporaryData.visible
  let perIndex = index - this.basicdata.currentPage
  // visible可见数量前滑块的样式
  if (index <= this.basicdata.currentPage + visible - 1) {
   style[&#39;opacity&#39;] = &#39;1&#39;
   style[&#39;transform&#39;] = &#39;translate3D(0,0,&#39; + -1 * perIndex * 60 + &#39;px&#39; + &#39;)&#39;
   style[&#39;zIndex&#39;] = visible - index + this.basicdata.currentPage
   style[&#39;transitionTimingFunction&#39;] = &#39;ease&#39;
   style[&#39;transitionDuration&#39;] = 300 + &#39;ms&#39;
  } else {
   style[&#39;zIndex&#39;] = &#39;-1&#39;
   style[&#39;transform&#39;] = &#39;translate3D(0,0,&#39; + -1 * visible * 60 + &#39;px&#39; + &#39;)&#39;
  }
  return style
  }
 }
 }
}
</script>

Clic clé

 : style pour lier des objets ainsi que des tableaux et des fonctions, qui est très utile lors de la traversée

La structure dom la plus basique a été construite, ensuite La première étape consiste à faire "bouger" la première image

2. Glissement de l'image

L'effet de glissement d'image apparaît dans de nombreuses scènes. Son principe n'est rien de plus que de surveiller l'événement tactile, d'obtenir le déplacement, puis de modifier le déplacement cible via Translate3D, les étapes que nous souhaitons mettre en œuvre sont donc les suivantes

    Lier l'événement Touchs à la pile
  • Surveiller et stocker les valeurs des changements de position des gestes
  • Changer les valeurs x, y de translate3D dans l'attribut css de la première image
  • #### Implémentation spécifique

Dans le framework vue, ce n'est pas recommandé pour faire fonctionner les nœuds directement, mais pour lier les éléments via l'instruction v-on, nous écrivons donc toutes les liaisons dans v-for traversal et les jugeons via index. Qu'il s'agisse de la première image, utilisez :style pour modifier le style de la page d'accueil. le code spécifique est le suivant :

<template>
 <ul class="stack">
  <li class="stack-item" v-for="(item, index) in pages"
  :style="[transformIndex(index),transform(index)]"
  @touchstart.stop.capture="touchstart"
  @touchmove.stop.capture="touchmove"
  @touchend.stop.capture="touchend"
  @mousedown.stop.capture="touchstart"
  @mouseup.stop.capture="touchend"
  @mousemove.stop.capture="touchmove">
  <img :src="item.src">
  </li>
 </ul>
</template>
<script>
export default {
 props: {
 // pages数据包含基础的图片数据
 pages: {
  type: Array,
  default: []
 }
 },
 data () {
 return {
  // basicdata数据包含组件基本数据
  basicdata: {
  start: {}, // 记录起始位置
  end: {}, // 记录终点位置
  currentPage: 0 // 默认首图的序列
  },
  // temporaryData数据包含组件临时数据
  temporaryData: {
  poswidth: &#39;&#39;, // 记录位移
  posheight: &#39;&#39;, // 记录位移
  tracking: false // 是否在滑动,防止多次操作,影响体验
  }
 }
 },
 methods: {
 touchstart (e) {
  if (this.temporaryData.tracking) {
  return
  }
  // 是否为touch
  if (e.type === &#39;touchstart&#39;) {
  if (e.touches.length > 1) {
   this.temporaryData.tracking = false
   return
  } else {
   // 记录起始位置
   this.basicdata.start.t = new Date().getTime()
   this.basicdata.start.x = e.targetTouches[0].clientX
   this.basicdata.start.y = e.targetTouches[0].clientY
   this.basicdata.end.x = e.targetTouches[0].clientX
   this.basicdata.end.y = e.targetTouches[0].clientY
  }
  // pc操作
  } else {
  this.basicdata.start.t = new Date().getTime()
  this.basicdata.start.x = e.clientX
  this.basicdata.start.y = e.clientY
  this.basicdata.end.x = e.clientX
  this.basicdata.end.y = e.clientY
  }
  this.temporaryData.tracking = true
 },
 touchmove (e) {
  // 记录滑动位置
  if (this.temporaryData.tracking && !this.temporaryData.animation) {
  if (e.type === &#39;touchmove&#39;) {
   this.basicdata.end.x = e.targetTouches[0].clientX
   this.basicdata.end.y = e.targetTouches[0].clientY
  } else {
   this.basicdata.end.x = e.clientX
   this.basicdata.end.y = e.clientY
  }
  // 计算滑动值
  this.temporaryData.poswidth = this.basicdata.end.x - this.basicdata.start.x
  this.temporaryData.posheight = this.basicdata.end.y - this.basicdata.start.y
  }
 },
 touchend (e) {
  this.temporaryData.tracking = false
  // 滑动结束,触发判断
 },
 // 非首页样式切换
 transform (index) {
  if (index > this.basicdata.currentPage) {
  let style = {}
  let visible = 3
  let perIndex = index - this.basicdata.currentPage
  // visible可见数量前滑块的样式
  if (index <= this.basicdata.currentPage + visible - 1) {
   style[&#39;opacity&#39;] = &#39;1&#39;
   style[&#39;transform&#39;] = &#39;translate3D(0,0,&#39; + -1 * perIndex * 60 + &#39;px&#39; + &#39;)&#39;
   style[&#39;zIndex&#39;] = visible - index + this.basicdata.currentPage
   style[&#39;transitionTimingFunction&#39;] = &#39;ease&#39;
   style[&#39;transitionDuration&#39;] = 300 + &#39;ms&#39;
  } else {
   style[&#39;zIndex&#39;] = &#39;-1&#39;
   style[&#39;transform&#39;] = &#39;translate3D(0,0,&#39; + -1 * visible * 60 + &#39;px&#39; + &#39;)&#39;
  }
  return style
  }
 },
 // 首页样式切换
 transformIndex (index) {
  // 处理3D效果
  if (index === this.basicdata.currentPage) {
  let style = {}
  style[&#39;transform&#39;] = &#39;translate3D(&#39; + this.temporaryData.poswidth + &#39;px&#39; + &#39;,&#39; + this.temporaryData.posheight + &#39;px&#39; + &#39;,0px)&#39;
  style[&#39;opacity&#39;] = 1
  style[&#39;zIndex&#39;] = 10
  return style
  }
 }
 }
}
</script>

3. Glisser une fois la condition réussie, le rebond après l'échec de la condition

Le jugement déclencheur de la condition est effectué après le toucher/mouseup. Ici, nous utilisons d'abord des conditions simples pour juger, et en même temps donnons les premiers effets de pop-up et de rebond d'image. Le code est le suivant

<template>
 <ul class="stack">
  <li class="stack-item" v-for="(item, index) in pages"
  :style="[transformIndex(index),transform(index)]"
  @touchmove.stop.capture="touchmove"
  @touchstart.stop.capture="touchstart"
  @touchend.stop.capture="touchend"
  @mousedown.stop.capture="touchstart"
  @mouseup.stop.capture="touchend"
  @mousemove.stop.capture="touchmove">
  <img :src="item.src">
  </li>
 </ul>
</template>
<script>
export default {
 props: {
  // pages数据包含基础的图片数据
 pages: {
  type: Array,
  default: []
 }
 },
 data () {
 return {
  // basicdata数据包含组件基本数据
  basicdata: {
  start: {}, // 记录起始位置
  end: {}, // 记录终点位置
  currentPage: 0 // 默认首图的序列
  },
  // temporaryData数据包含组件临时数据
  temporaryData: {
  poswidth: &#39;&#39;, // 记录位移
  posheight: &#39;&#39;, // 记录位移
  tracking: false, // 是否在滑动,防止多次操作,影响体验
  animation: false, // 首图是否启用动画效果,默认为否
  opacity: 1 // 记录首图透明度
  }
 }
 },
 methods: {
 touchstart (e) {
  if (this.temporaryData.tracking) {
  return
  }
  // 是否为touch
  if (e.type === &#39;touchstart&#39;) {
  if (e.touches.length > 1) {
   this.temporaryData.tracking = false
   return
  } else {
   // 记录起始位置
   this.basicdata.start.t = new Date().getTime()
   this.basicdata.start.x = e.targetTouches[0].clientX
   this.basicdata.start.y = e.targetTouches[0].clientY
   this.basicdata.end.x = e.targetTouches[0].clientX
   this.basicdata.end.y = e.targetTouches[0].clientY
  }
  // pc操作
  } else {
  this.basicdata.start.t = new Date().getTime()
  this.basicdata.start.x = e.clientX
  this.basicdata.start.y = e.clientY
  this.basicdata.end.x = e.clientX
  this.basicdata.end.y = e.clientY
  }
  this.temporaryData.tracking = true
  this.temporaryData.animation = false
 },
 touchmove (e) {
  // 记录滑动位置
  if (this.temporaryData.tracking && !this.temporaryData.animation) {
  if (e.type === &#39;touchmove&#39;) {
   this.basicdata.end.x = e.targetTouches[0].clientX
   this.basicdata.end.y = e.targetTouches[0].clientY
  } else {
   this.basicdata.end.x = e.clientX
   this.basicdata.end.y = e.clientY
  }
  // 计算滑动值
  this.temporaryData.poswidth = this.basicdata.end.x - this.basicdata.start.x
  this.temporaryData.posheight = this.basicdata.end.y - this.basicdata.start.y
  }
 },
 touchend (e) {
  this.temporaryData.tracking = false
  this.temporaryData.animation = true
  // 滑动结束,触发判断
  // 简单判断滑动宽度超出100像素时触发滑出
  if (Math.abs(this.temporaryData.poswidth) >= 100) {
  // 最终位移简单设定为x轴200像素的偏移
  let ratio = Math.abs(this.temporaryData.posheight / this.temporaryData.poswidth)
  this.temporaryData.poswidth = this.temporaryData.poswidth >= 0 ? this.temporaryData.poswidth + 200 : this.temporaryData.poswidth - 200
  this.temporaryData.posheight = this.temporaryData.posheight >= 0 ? Math.abs(this.temporaryData.poswidth * ratio) : -Math.abs(this.temporaryData.poswidth * ratio)
  this.temporaryData.opacity = 0
  // 不满足条件则滑入
  } else {
  this.temporaryData.poswidth = 0
  this.temporaryData.posheight = 0
  }
 },
 // 非首页样式切换
 transform (index) {
  if (index > this.basicdata.currentPage) {
  let style = {}
  let visible = 3
  let perIndex = index - this.basicdata.currentPage
  // visible可见数量前滑块的样式
  if (index <= this.basicdata.currentPage + visible - 1) {
   style[&#39;opacity&#39;] = &#39;1&#39;
   style[&#39;transform&#39;] = &#39;translate3D(0,0,&#39; + -1 * perIndex * 60 + &#39;px&#39; + &#39;)&#39;
   style[&#39;zIndex&#39;] = visible - index + this.basicdata.currentPage
   style[&#39;transitionTimingFunction&#39;] = &#39;ease&#39;
   style[&#39;transitionDuration&#39;] = 300 + &#39;ms&#39;
  } else {
   style[&#39;zIndex&#39;] = &#39;-1&#39;
   style[&#39;transform&#39;] = &#39;translate3D(0,0,&#39; + -1 * visible * 60 + &#39;px&#39; + &#39;)&#39;
  }
  return style
  }
 },
 // 首页样式切换
 transformIndex (index) {
  // 处理3D效果
  if (index === this.basicdata.currentPage) {
  let style = {}
  style[&#39;transform&#39;] = &#39;translate3D(&#39; + this.temporaryData.poswidth + &#39;px&#39; + &#39;,&#39; + this.temporaryData.posheight + &#39;px&#39; + &#39;,0px)&#39;
  style[&#39;opacity&#39;] = this.temporaryData.opacity
  style[&#39;zIndex&#39;] = 10
  if (this.temporaryData.animation) {
   style[&#39;transitionTimingFunction&#39;] = &#39;ease&#39;
   style[&#39;transitionDuration&#39;] = 300 + &#39;ms&#39;
  }
  return style
  }
 }
 }
}
</script>

4. Après avoir glissé, l'image suivante est empilée sur le dessus

La réempilage est la dernière. la fonction du composant est également la fonction la plus importante et la plus complexe. Dans notre code, le tri des éléments de pile dépend de la fonction transformIndex et transform du style de liaison. La condition déterminée dans la fonction est currentPage. Est-il nécessaire de modifier la page actuelle et de la laisser +1 pour terminer le réempilement. ?

La réponse n'est pas si simple, car notre slide out est un effet d'animation, qui durera 300 ms, et le réarrangement provoqué par le changement de currentPage changera immédiatement, interrompant la progression de l'animation. Par conséquent, nous devons d'abord modifier les conditions de tri de la fonction de transformation, puis modifier la page actuelle.

#### Implémentation spécifique

Modifier la condition de tri de la fonction de transformation
  • Laisser currentPage+1
  • Ajoutez l'événement onTransitionEnd, et une fois le curseur terminé, déplacez-le dans la liste des piles

  • Le code est le suivant :

<template>
 <ul class="stack">
  <li class="stack-item" v-for="(item, index) in pages"
  :style="[transformIndex(index),transform(index)]"
  @touchmove.stop.capture="touchmove"
  @touchstart.stop.capture="touchstart"
  @touchend.stop.capture="touchend"
  @mousedown.stop.capture="touchstart"
  @mouseup.stop.capture="touchend"
  @mousemove.stop.capture="touchmove"
  @webkit-transition-end="onTransitionEnd"
  @transitionend="onTransitionEnd"
  >
  <img :src="item.src">
  </li>
 </ul>
</template>
<script>
export default {
 props: {
 // pages数据包含基础的图片数据
 pages: {
  type: Array,
  default: []
 }
 },
 data () {
 return {
  // basicdata数据包含组件基本数据
  basicdata: {
  start: {}, // 记录起始位置
  end: {}, // 记录终点位置
  currentPage: 0 // 默认首图的序列
  },
  // temporaryData数据包含组件临时数据
  temporaryData: {
  poswidth: &#39;&#39;, // 记录位移
  posheight: &#39;&#39;, // 记录位移
  lastPosWidth: &#39;&#39;, // 记录上次最终位移
  lastPosHeight: &#39;&#39;, // 记录上次最终位移
  tracking: false, // 是否在滑动,防止多次操作,影响体验
  animation: false, // 首图是否启用动画效果,默认为否
  opacity: 1, // 记录首图透明度
  swipe: false // onTransition判定条件
  }
 }
 },
 methods: {
 touchstart (e) {
  if (this.temporaryData.tracking) {
  return
  }
  // 是否为touch
  if (e.type === &#39;touchstart&#39;) {
  if (e.touches.length > 1) {
   this.temporaryData.tracking = false
   return
  } else {
   // 记录起始位置
   this.basicdata.start.t = new Date().getTime()
   this.basicdata.start.x = e.targetTouches[0].clientX
   this.basicdata.start.y = e.targetTouches[0].clientY
   this.basicdata.end.x = e.targetTouches[0].clientX
   this.basicdata.end.y = e.targetTouches[0].clientY
  }
  // pc操作
  } else {
  this.basicdata.start.t = new Date().getTime()
  this.basicdata.start.x = e.clientX
  this.basicdata.start.y = e.clientY
  this.basicdata.end.x = e.clientX
  this.basicdata.end.y = e.clientY
  }
  this.temporaryData.tracking = true
  this.temporaryData.animation = false
 },
 touchmove (e) {
  // 记录滑动位置
  if (this.temporaryData.tracking && !this.temporaryData.animation) {
  if (e.type === &#39;touchmove&#39;) {
   this.basicdata.end.x = e.targetTouches[0].clientX
   this.basicdata.end.y = e.targetTouches[0].clientY
  } else {
   this.basicdata.end.x = e.clientX
   this.basicdata.end.y = e.clientY
  }
  // 计算滑动值
  this.temporaryData.poswidth = this.basicdata.end.x - this.basicdata.start.x
  this.temporaryData.posheight = this.basicdata.end.y - this.basicdata.start.y
  }
 },
 touchend (e) {
  this.temporaryData.tracking = false
  this.temporaryData.animation = true
  // 滑动结束,触发判断
  // 简单判断滑动宽度超出100像素时触发滑出
  if (Math.abs(this.temporaryData.poswidth) >= 100) {
  // 最终位移简单设定为x轴200像素的偏移
  let ratio = Math.abs(this.temporaryData.posheight / this.temporaryData.poswidth)
  this.temporaryData.poswidth = this.temporaryData.poswidth >= 0 ? this.temporaryData.poswidth + 200 : this.temporaryData.poswidth - 200
  this.temporaryData.posheight = this.temporaryData.posheight >= 0 ? Math.abs(this.temporaryData.poswidth * ratio) : -Math.abs(this.temporaryData.poswidth * ratio)
  this.temporaryData.opacity = 0
  this.temporaryData.swipe = true
  // 记录最终滑动距离
  this.temporaryData.lastPosWidth = this.temporaryData.poswidth
  this.temporaryData.lastPosHeight = this.temporaryData.posheight
  // currentPage+1 引发排序变化
  this.basicdata.currentPage += 1
  // currentPage切换,整体dom进行变化,把第一层滑动置零
  this.$nextTick(() => {
   this.temporaryData.poswidth = 0
   this.temporaryData.posheight = 0
   this.temporaryData.opacity = 1
  })
  // 不满足条件则滑入
  } else {
  this.temporaryData.poswidth = 0
  this.temporaryData.posheight = 0
  this.temporaryData.swipe = false
  }
 },
 onTransitionEnd (index) {
  // dom发生变化后,正在执行的动画滑动序列已经变为上一层
  if (this.temporaryData.swipe && index === this.basicdata.currentPage - 1) {
  this.temporaryData.animation = true
  this.temporaryData.lastPosWidth = 0
  this.temporaryData.lastPosHeight = 0
  this.temporaryData.swipe = false
  }
 },
 // 非首页样式切换
 transform (index) {
  if (index > this.basicdata.currentPage) {
  let style = {}
  let visible = 3
  let perIndex = index - this.basicdata.currentPage
  // visible可见数量前滑块的样式
  if (index <= this.basicdata.currentPage + visible - 1) {
   style[&#39;opacity&#39;] = &#39;1&#39;
   style[&#39;transform&#39;] = &#39;translate3D(0,0,&#39; + -1 * perIndex * 60 + &#39;px&#39; + &#39;)&#39;
   style[&#39;zIndex&#39;] = visible - index + this.basicdata.currentPage
   style[&#39;transitionTimingFunction&#39;] = &#39;ease&#39;
   style[&#39;transitionDuration&#39;] = 300 + &#39;ms&#39;
  } else {
   style[&#39;zIndex&#39;] = &#39;-1&#39;
   style[&#39;transform&#39;] = &#39;translate3D(0,0,&#39; + -1 * visible * 60 + &#39;px&#39; + &#39;)&#39;
  }
  return style
  // 已滑动模块释放后
  } else if (index === this.basicdata.currentPage - 1) {
  let style = {}
  // 继续执行动画
  style[&#39;transform&#39;] = &#39;translate3D(&#39; + this.temporaryData.lastPosWidth + &#39;px&#39; + &#39;,&#39; + this.temporaryData.lastPosHeight + &#39;px&#39; + &#39;,0px)&#39;
  style[&#39;opacity&#39;] = &#39;0&#39;
  style[&#39;zIndex&#39;] = &#39;-1&#39;
  style[&#39;transitionTimingFunction&#39;] = &#39;ease&#39;
  style[&#39;transitionDuration&#39;] = 300 + &#39;ms&#39;
  return style
  }
 },
 // 首页样式切换
 transformIndex (index) {
  // 处理3D效果
  if (index === this.basicdata.currentPage) {
  let style = {}
  style[&#39;transform&#39;] = &#39;translate3D(&#39; + this.temporaryData.poswidth + &#39;px&#39; + &#39;,&#39; + this.temporaryData.posheight + &#39;px&#39; + &#39;,0px)&#39;
  style[&#39;opacity&#39;] = this.temporaryData.opacity
  style[&#39;zIndex&#39;] = 10
  if (this.temporaryData.animation) {
   style[&#39;transitionTimingFunction&#39;] = &#39;ease&#39;
   style[&#39;transitionDuration&#39;] = 300 + &#39;ms&#39;
  }
  return style
  }
 }
 }
}
</script>
ok~ Après avoir terminé les quatre étapes ci-dessus, les fonctions de base de l'empilage des composants ont été mises en œuvre. Venez voir l'effet

<.>

L'effet de glissement empilé a déjà été publié, mais Tantan a également ajouté le décalage de l'angle de contact et la détermination du rapport de la zone de glissement en termes d'expérience

Le principe du décalage d'angle est d'enregistrer la position tactile de l'utilisateur à chaque fois que l'utilisateur la touche, de calculer l'angle de décalage maximum et, lorsque le déplacement coulissant se produit, d'augmenter linéairement l'angle jusqu'à l'angle de décalage maximum.

Ce que vous devez faire dans la pile est :

  • Calculer l'angle et la direction requis dans touchmove

  • touchend Et dans onTransitionEnd, réglez l'angle sur zéro

pour déterminer le rapport de zone coulissante. La zone de décalage est principalement calculée à travers la quantité de décalage pour obtenir le rapport de zone et compléter le jugement.

Le code complet et la démo peuvent être consultés sur github. Le code source ne sera pas publié ici

Merci à tous d'avoir lu cet article. :star:️ sur github. Enfin, je vous souhaite tout le meilleur. Vous pouvez trouver votre ex-petite amie sur Tantan : green_heart :

Ce qui précède est ce que j'ai compilé pour tout le monde. tout le monde dans le futur.

Articles connexes :

Explication détaillée de l'utilisation de l'échafaudage vue-cli pour initialiser la structure du projet sous le projet Vue

Modifier la méthode de demande de vue pour une certaine valeur d'élément dans les données

Méthode d'implémentation de la barre de navigation en étoile JavaScript

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn