首頁  >  文章  >  web前端  >  利用vue行動裝置UI框架如何實現QQ側邊選單(詳細教學)

利用vue行動裝置UI框架如何實現QQ側邊選單(詳細教學)

亚连
亚连原創
2018-05-31 16:05:422354瀏覽

這篇文章主要介紹了vue行動端UI框架實現仿qq側邊選單元件,非常不錯,具有參考借鑒價值,需要的朋友可以參考下

最近面試發現很多前端程式設計師都從來沒有寫過插件的經驗,基本上都是網路百度。所以打算寫一系列文章,手把手的教一些沒有寫過組件的兄弟們如何去寫插件。本系列文章都基於VUE,核心內容都一樣,會了之後大家可以快速的改寫成react、angular或是小程式等元件。這篇文章是第一篇,寫的是一個類似QQ的側邊選單元件。

效果展示

先讓大家看個效果展示,知道咱們要做的東西是個怎麼樣的樣子,圖片有點模糊,大家先將就點:

開始製作

##DOM結構

整體結構中應該存在兩個容器:1. 選單容器2. 主頁面容器;因此目前DOM結構如下:

<template>
 <p class="r-slide-menu">
 <p class="r-slide-menu-wrap"></p>
 <p class="r-slide-menu-content"></p>
 </p>
</template>

為了使得選單內容和主題內容能夠定制,我們再給兩個容器中加入兩個slot插槽:預設插槽中放置主體內容、選單放置到menu插槽內:

<template>
 <p class="r-slide-menu">
 <p class="r-slide-menu-wrap">
  <slot name="menu"></slot>
 </p>
 <p class="r-slide-menu-content">
  <slot></slot>
 </p>
 </p>
</template>

css樣式

我專案中使用了scss,程式碼如下:

<style lang="scss">
@mixin one-screen {
 position: absolute;
 left:0;
 top:0;
 width:100%;
 height:100%;
 overflow: hidden;
}

.r-slide-menu{
 @include one-screen;
 &-wrap, &-content{
 @include one-screen;
 }
 &-transition{
 -webkit-transition: transform .3s;
 transition: transform .3s;
 }
}
</style>

此時我們就得到了兩個絕對定位的容器

javascript

現在開始正式的程式碼編寫了,首先我們理清下交互邏輯:

  • 手指左右滑動的時候主體容器和選單容器都跟著手指運動運動

  • #當手指移動的距離超過選單容器寬度的時候頁面不能繼續向右滑動

  • 當手指向左移動使得選單和頁面的移動距離歸零的時候頁面不能繼續向左移動

  • 當手指釋放離開螢幕的時候,頁面滑動如果超過一定的距離(整個選單寬度的比例)則打開整個選單,如果小於一定距離則關閉選單

所以現在咱們需要在使用組件的時候能夠入參定制菜單寬度以及觸發菜單收起關閉的臨界值和菜單寬度的比例,同時需要給主體容器添加touch事件,最後我們給菜單容器和主體容器添加各自添加一個控制他們運動的style,透過控制這個style來控制容器的移動

<template>
 <p class="r-slide-menu">
 <p class="r-slide-menu-wrap" :style="wrapStyle">
  <slot name="menu"></slot>
 </p>
 <p class="r-slide-menu-content" :style="contentStyle"
 @touchstart="touchstart"
 @touchmove="touchmove"
 @touchend="touchend">
  <slot></slot>
 </p>
 </p>
</template>
<script>
export default {
 props: {
 width: {
  type: String,
  default: &#39;250&#39;
 },
 ratio: {
  type: Number,
  default: 2
 }
 },
 data () {
 return {
  isMoving: false,
  transitionClass: &#39;&#39;,
  startPoint: {
  X: 0,
  y: 0
  },
  oldPoint: {
  x: 0,
  y: 0
  },
  move: {
  x: 0,
  y: 0
  }
 }
 },
 computed: {
 wrapStyle () {
  let style = {
  width: `${this.width}px`,
  left: `-${this.width / this.ratio}px`,
  transform: `translate3d(${this.move.x / this.ratio}px, 0px, 0px)`
  }
  return style
 },
 contentStyle () {
  let style = {
  transform: `translate3d(${this.move.x}px, 0px, 0px)`
  }
  return style
 }
 },
 methods: {
 touchstart (e) {},
 touchmove (e) {},
 touchend (e) {}
 }
}

接下來,我們來實現我們最核心的touch事件處理函數,事件的邏輯如下:

  1. 手指按下瞬間,記錄下目前手指所觸摸的點,以及目前主容器的位置

  2. 手指移動的時候,取得到移動的點的位置

  3. 計算目前手指所在點移動的X、Y軸距離,如果X移動的距離大於Y移動的距離則判定為橫向運動,否則為垂直移動

  4. 如果橫向移動則判斷當前移動的距離是在合理的移動區間(0到選單寬度)移動,如果是則改變兩個容器的位置(移動過程中阻止頁面中其他的事件觸發)

  5. 手指離開畫面:如果累積移動距離超過臨界值則運用動畫開啟選單,否則關閉選單

touchstart (e) {
 this.oldPoint.x = e.touches[0].pageX
 this.oldPoint.y = e.touches[0].pageY
 this.startPoint.x = this.move.x
 this.startPoint.y = this.move.y
 this.setTransition()
},
touchmove (e) {
 let newPoint = {
 x: e.touches[0].pageX,
 y: e.touches[0].pageY
 }
 let moveX = newPoint.x - this.oldPoint.x
 let moveY = newPoint.y - this.oldPoint.y
 if (Math.abs(moveX) < Math.abs(moveY)) return false
 e.preventDefault()
 this.isMoving = true
 moveX = this.startPoint.x * 1 + moveX * 1
 moveY = this.startPoint.y * 1 + moveY * 1
 if (moveX >= this.width) {
 this.move.x = this.width
 } else if (moveX <= 0) {
 this.move.x = 0
 } else {
 this.move.x = moveX
 }
},
touchend (e) {
 this.setTransition(true)
 this.isMoving = false
 this.move.x = (this.move.x > this.width / this.ratio) ? this.width : 0
},
setTransition (isTransition = false) {
 this.transitionClass = isTransition ? &#39;r-slide-menu-transition&#39; : &#39;&#39;
}

上面,這段核心程式碼中有一個setTransition 函數,這個函數的作用是在手指離開的時候給容器元素加上transition屬性,讓容器有一個過渡動畫,完成關閉或打開動畫;所以在手指按下去的瞬間需要把容器上的這個transition屬性去除,避免滑動過程中出現容器和手指滑動延遲的不良體驗。最後提醒下,程式碼中使用translate3d而非translate的原因是為了啟動行動手機的動畫3D加速,提升動畫流暢度。最終程式碼如下:

<template>
 <p class="r-slide-menu">
 <p class="r-slide-menu-wrap" :class="transitionClass" :style="wrapStyle">
  <slot name="menu"></slot>
 </p>
 <p class="r-slide-menu-content" :class="transitionClass" :style="contentStyle"
  @touchstart="touchstart"
  @touchmove="touchmove"
  @touchend="touchend">
  <slot></slot>
 </p>
 </p>
</template>
<script>
export default {
 props: {
 width: {
  type: String,
  default: &#39;250&#39;
 },
 ratio: {
  type: Number,
  default: 2
 }
 },
 data () {
 return {
  isMoving: false,
  transitionClass: &#39;&#39;,
  startPoint: {
  X: 0,
  y: 0
  },
  oldPoint: {
  x: 0,
  y: 0
  },
  move: {
  x: 0,
  y: 0
  }
 }
 },
 computed: {
 wrapStyle () {
  let style = {
  width: `${this.width}px`,
  left: `-${this.width / this.ratio}px`,
  transform: `translate3d(${this.move.x / this.ratio}px, 0px, 0px)`
  }
  return style
 },
 contentStyle () {
  let style = {
  transform: `translate3d(${this.move.x}px, 0px, 0px)`
  }
  return style
 }
 },
 methods: {
 touchstart (e) {
  this.oldPoint.x = e.touches[0].pageX
  this.oldPoint.y = e.touches[0].pageY
  this.startPoint.x = this.move.x
  this.startPoint.y = this.move.y
  this.setTransition()
 },
 touchmove (e) {
  let newPoint = {
  x: e.touches[0].pageX,
  y: e.touches[0].pageY
  }
  let moveX = newPoint.x - this.oldPoint.x
  let moveY = newPoint.y - this.oldPoint.y
  if (Math.abs(moveX) < Math.abs(moveY)) return false
  e.preventDefault()
  this.isMoving = true
  moveX = this.startPoint.x * 1 + moveX * 1
  moveY = this.startPoint.y * 1 + moveY * 1
  if (moveX >= this.width) {
  this.move.x = this.width
  } else if (moveX <= 0) {
  this.move.x = 0
  } else {
  this.move.x = moveX
  }
 },
 touchend (e) {
  this.setTransition(true)
  this.isMoving = false
  this.move.x = (this.move.x > this.width / this.ratio) ? this.width : 0
 },
 // 点击切换
 switch () {
  this.setTransition(true)
  this.move.x = (this.move.x === 0) ? this.width : 0
 },
 setTransition (isTransition = false) {
  this.transitionClass = isTransition ? &#39;r-slide-menu-transition&#39; : &#39;&#39;
 }
 }
}
</script>
<style lang="scss">
@mixin one-screen {
 position: absolute;
 left:0;
 top:0;
 width:100%;
 height:100%;
 overflow: hidden;
}
.r-slide-menu{
 @include one-screen;
 &-wrap, &-content{
 @include one-screen;
 }
 &-transition{
 -webkit-transition: transform .3s;
 transition: transform .3s;
 }
}
</style>

上面是我整理給大家的,希望今後會對大家有幫助。

相關文章:

使用Angular CLI從藍本產生程式碼詳解

Vue文件中幾個易忽略部分的剖析

淺談Vue下使用百度地圖的簡易方法

##

以上是利用vue行動裝置UI框架如何實現QQ側邊選單(詳細教學)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn