最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

vue移動(dòng)端UI框架實(shí)現(xiàn)QQ側(cè)邊菜單組件

 更新時(shí)間:2018年03月09日 09:20:39   作者:webKity  
這篇文章主要介紹了vue移動(dòng)端UI框架實(shí)現(xiàn)仿qq側(cè)邊菜單組件,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下

最近面試發(fā)現(xiàn)很多前端程序員都從來(lái)沒(méi)有寫過(guò)插件的經(jīng)驗(yàn),基本上都是網(wǎng)上百度。所以打算寫一系列文章,手把手的教一些沒(méi)有寫過(guò)組件的兄弟們?nèi)绾稳懖寮?。本系列文章都基于VUE,核心內(nèi)容都一樣,會(huì)了之后大家可以快速的改寫成react、angular或者是小程序等組件。這篇文章是第一篇,寫的是一個(gè)類似QQ的側(cè)邊菜單組件。

效果展示

先讓大家看個(gè)效果展示,知道咱們要做的東西是個(gè)怎么樣的樣子,圖片有點(diǎn)模糊,大家先將就點(diǎn):

開(kāi)始制作

DOM結(jié)構(gòu)

整體結(jié)構(gòu)中應(yīng)該存在兩個(gè)容器:1. 菜單容器 2. 主頁(yè)面容器;因此當(dāng)前DOM結(jié)構(gòu)如下:

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

為了使得菜單內(nèi)容和主題內(nèi)容能夠定制,我們?cè)俳o兩個(gè)容器中加入兩個(gè)slot插槽:默認(rèn)插槽中放置主體內(nèi)容、菜單放置到menu插槽內(nèi):

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

css樣式

我項(xiàng)目中使用了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>

此時(shí)我們就得到了兩個(gè)絕對(duì)定位的容器

javascript

現(xiàn)在開(kāi)始正式的代碼編寫了,首先我們理清下交互邏輯:

  • 手指左右滑動(dòng)的時(shí)候主體容器和菜單容器都跟著手指運(yùn)動(dòng)運(yùn)動(dòng)
  • 當(dāng)手指移動(dòng)的距離超過(guò)菜單容器寬度的時(shí)候頁(yè)面不能繼續(xù)向右滑動(dòng)
  • 當(dāng)手指向左移動(dòng)使得菜單和頁(yè)面的移動(dòng)距離歸零的時(shí)候頁(yè)面不能繼續(xù)向左移動(dòng)
  • 當(dāng)手指釋放離開(kāi)屏幕的時(shí)候,頁(yè)面滑動(dòng)如果超過(guò)一定的距離(整個(gè)菜單寬度的比例)則打開(kāi)整個(gè)菜單,如果小于一定距離則關(guān)閉菜單

所以現(xiàn)在咱們需要在使用組件的時(shí)候能夠入?yún)⒍ㄖ撇藛螌挾纫约坝|發(fā)菜單收起關(guān)閉的臨界值和菜單寬度的比例,同時(shí)需要給主體容器添加touch事件,最后我們給菜單容器和主體容器添加各自添加一個(gè)控制他們運(yùn)動(dòng)的style,通過(guò)控制這個(gè)style來(lái)控制容器的移動(dòng)

<template>
 <div class="r-slide-menu">
 <div class="r-slide-menu-wrap" :style="wrapStyle">
  <slot name="menu"></slot>
 </div>
 <div class="r-slide-menu-content" :style="contentStyle"
 @touchstart="touchstart"
 @touchmove="touchmove"
 @touchend="touchend">
  <slot></slot>
 </div>
 </div>
</template>
<script>
export default {
 props: {
 width: {
  type: String,
  default: '250'
 },
 ratio: {
  type: Number,
  default: 2
 }
 },
 data () {
 return {
  isMoving: false,
  transitionClass: '',
  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) {}
 }
}

接下來(lái),我們來(lái)實(shí)現(xiàn)我們最核心的touch事件處理函數(shù),事件的邏輯如下:

  1. 手指按下瞬間,記錄下當(dāng)前手指所觸摸的點(diǎn),以及當(dāng)前主容器的位置
  2. 手指移動(dòng)的時(shí)候,獲取到移動(dòng)的點(diǎn)的位置
  3. 計(jì)算當(dāng)前手指所在點(diǎn)移動(dòng)的X、Y軸距離,如果X移動(dòng)的距離大于Y移動(dòng)的距離則判定為橫向運(yùn)動(dòng),否則為豎向運(yùn)動(dòng)
  4. 如果橫向運(yùn)動(dòng)則判斷當(dāng)前移動(dòng)的距離是在合理的移動(dòng)區(qū)間(0到菜單寬度)移動(dòng),如果是則改變兩個(gè)容器的位置(移動(dòng)過(guò)程中阻止頁(yè)面中其他的事件觸發(fā))
  5. 手指離開(kāi)屏幕:如果累計(jì)移動(dòng)距離超過(guò)臨界值則運(yùn)用動(dòng)畫打開(kāi)菜單,否則關(guān)閉菜單
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 ? 'r-slide-menu-transition' : ''
}

上面,這段核心代碼中有一個(gè)setTransition 函數(shù),這個(gè)函數(shù)的作用是在手指離開(kāi)的時(shí)候給容器元素添加transition屬性,讓容器有一個(gè)過(guò)渡動(dòng)畫,完成關(guān)閉或者打開(kāi)動(dòng)畫;所以在手指按下去的瞬間需要把容器上的這個(gè)transition屬性去除,避免滑動(dòng)過(guò)程中出現(xiàn)容器和手指滑動(dòng)延遲的不良體驗(yàn)。 最后提醒下,代碼中使用translate3d而非translate的原因是為了啟動(dòng)移動(dòng)端手機(jī)的動(dòng)畫3D加速,提升動(dòng)畫流暢度。最終代碼如下:

<template>
 <div class="r-slide-menu">
 <div class="r-slide-menu-wrap" :class="transitionClass" :style="wrapStyle">
  <slot name="menu"></slot>
 </div>
 <div class="r-slide-menu-content" :class="transitionClass" :style="contentStyle"
  @touchstart="touchstart"
  @touchmove="touchmove"
  @touchend="touchend">
  <slot></slot>
 </div>
 </div>
</template>
<script>
export default {
 props: {
 width: {
  type: String,
  default: '250'
 },
 ratio: {
  type: Number,
  default: 2
 }
 },
 data () {
 return {
  isMoving: false,
  transitionClass: '',
  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
 },
 // 點(diǎn)擊切換
 switch () {
  this.setTransition(true)
  this.move.x = (this.move.x === 0) ? this.width : 0
 },
 setTransition (isTransition = false) {
  this.transitionClass = isTransition ? 'r-slide-menu-transition' : ''
 }
 }
}
</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>

總結(jié)

以上所述是小編給大家介紹的vue移動(dòng)端UI框架實(shí)現(xiàn)QQ側(cè)邊菜單組件,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!

相關(guān)文章

最新評(píng)論

五常市| 新泰市| 新田县| 维西| 盐城市| 静海县| 博客| 遂溪县| 亚东县| 海门市| 龙川县| 镇原县| 金山区| 三台县| 马山县| 吉木萨尔县| 龙胜| 泽州县| 江城| 望都县| 玉屏| 隆林| 海宁市| 延长县| 吉木萨尔县| 赞皇县| 乃东县| 墨玉县| 南江县| 永泰县| 铜梁县| 汉沽区| 莆田市| 苏尼特左旗| 鸡泽县| 石楼县| 延庆县| 吉木乃县| 海丰县| 汝州市| 翼城县|