當(dāng)前位置:首頁 >  站長 >  編程技術(shù) >  正文

vue swipeCell滑動單元格(仿微信)的實(shí)現(xiàn)示例

 2020-10-28 14:07  來源: 腳本之家   我來投稿 撤稿糾錯

  域名預(yù)訂/競價(jià),好“米”不錯過

這篇文章主要介紹了vue swipeCell滑動單元格(仿微信)的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

抽離Vant weapp滑動單元格代碼改造而成

帶有拉動彈性回彈效果

demo展示:https://littaotao.github.io/me/index(切換為瀏覽器調(diào)試的手機(jī)模式并且再次刷新一次)

<template>
 <div
  class="cell_container"
  @touchstart
  v-click-outside="handleClickOutside"
  @click="getClickHandler('cell')">
  <div
   :style="{'transform':
   'translateX('+(offset+(isElastic?elasticX:0))+'px)','transition-duration':dragging?'0s':'0.6s'}">
   <!-- <div ref="cellLeft" class="cell_left" @click="getClickHandler('left', true)">
    <div>收藏</div>
    <div>添加</div>
   </div> -->
   <div
    @touchend="onClick()"
    :class="offset?'cell_content':'cell_content_active'">SwipeCell</div>
   <div ref="cellRight"
    class="cell_right"
    @click="getClickHandler('right', true)">
    <div
     :class="type?'divPostion':''"
     ref="remove"
     :style="{'background':'#ccc','padding-left':'10px','padding-right':10+(isElastic?Math.abs(elasticX/3):0)+'px','transition-duration':dragging?'0s':'0.6s'}">標(biāo)記</div>
    <div
     :class="type?'divPostion':''"
     ref="tag"
     :style="{'transform': type?'translateX('+(-offset*removeWidth/cellRightWidth-(isElastic?elasticX/3:0))+'px)':'','padding-left':'10px','padding-right':10+(isElastic?Math.abs(elasticX/3):0)+'px','transition-duration':dragging?'0s':'0.6s','background':'#000'}">不再關(guān)注</div>
    <div
     :class="type?'divPostion':''"
     :style="{'transform': type?'translateX('+(-offset*(removeWidth+tagWidth)/cellRightWidth-(isElastic?elasticX/3*2:0))+'px)':'','padding-left':'10px','padding-right':10+(isElastic?Math.abs(elasticX/3):0)+'px','transition-duration':dragging?'0s':'0.6s'}">刪除</div>
   </div>
  </div>
 </div>
</template>
<script>
import ClickOutside from 'vue-click-outside';
import { TouchMixin } from '@/components/mixins/touch';
export default{
 name:"SwipeCell",
 props: {
  // @deprecated
  // should be removed in next major version, use beforeClose instead
  onClose: Function,
  disabled: Boolean,
  leftWidth: [Number, String],
  rightWidth: [Number, String],
  beforeClose: Function,
  stopPropagation: Boolean,
  name: {
   type: [Number, String],
   default: '',
  },
  //
  type:{
   type:[Number,String],
   default:1 //0 常規(guī) 1 定位
  },
  isElastic:{ //彈性
   type:Boolean,
   default:true
  }
 },
 data(){
  return {
   offset: 0,
   dragging: true,
   //-位移
   elasticX:0,
   removeWidth:0,
   tagWidth:0,
   cellRightWidth:0,
   cellLeftWidth:0
  }
 },
 computed: {
  computedLeftWidth() {
   return +this.leftWidth || this.getWidthByRef('cellLeft');
  },

  computedRightWidth() {
   return +this.rightWidth || this.getWidthByRef('cellRight');
  },
 },
 mounted() {
  //防止彈性效果影響寬度
  this.cellRightWidth = this.getWidthByRef('cellRight');
  this.cellLeftWidth = this.getWidthByRef('cellLeft');
  this.removeWidth = this.getWidthByRef('remove');
  this.tagWidth = this.getWidthByRef('tag');
  this.bindTouchEvent(this.$el);
 },
 mixins: [
  TouchMixin
 ],
 directives: {
  ClickOutside
 },
 methods: {
  getWidthByRef(ref) {
   if (this.$refs[ref]) {
    const rect = this.$refs[ref].getBoundingClientRect();
    //type=1定位時(shí)獲取寬度為0,為此采用獲取子元素寬度之和
    if(!rect.width){
     let childWidth = 0;
     for(const item of this.$refs[ref].children){
      childWidth += item.getBoundingClientRect().width
     }
     return childWidth;
    }
    return rect.width;
   }
   return 0;
  },

  handleClickOutside(e){
   if(this.opened) this.close()
  },

  // @exposed-api
  open(position) {
   const offset =
   position === 'left' ? this.computedLeftWidth : -this.computedRightWidth;

   this.opened = true;
   this.offset = offset;

   this.$emit('open', {
    position,
    name: this.name,
    // @deprecated
    // should be removed in next major version
    detail: this.name,
   });
  },

  // @exposed-api
  close(position) {
   this.offset = 0;

   if (this.opened) {
    this.opened = false;
    this.$emit('close', {
     position,
     name: this.name,
    });
   }
  },

  onTouchStart(event) {
   if (this.disabled) {
    return;
   }
   this.startOffset = this.offset;
   this.touchStart(event);
  },

  range(num, min, max) {
   return Math.min(Math.max(num, min), max);
  },

  preventDefault(event, isStopPropagation) {
   /* istanbul ignore else */
   if (typeof event.cancelable !== 'boolean' || event.cancelable) {
    event.preventDefault();
   }

   if (this.isStopPropagations) {
    stopPropagation(event);
   }
  },

  stopPropagations(event) {
   event.stopPropagation();
  },

  onTouchMove(event) {
   if (this.disabled) {
    return;
   }
   this.touchMove(event);
   if (this.direction === 'horizontal') {
    this.dragging = true;
    this.lockClick = true;
    const isPrevent = !this.opened || this.deltaX * this.startOffset < 0;
    if (isPrevent) {
     this.preventDefault(event, this.stopPropagation);
    }
    
    this.offset = this.range(
     this.deltaX + this.startOffset,
     -this.computedRightWidth,
     this.computedLeftWidth
    );
    //增加彈性
    if(this.computedRightWidth && this.offset === -this.computedRightWidth || this.computedLeftWidth && this.offset === this.computedLeftWidth){
     //
     this.preventDefault(event, this.stopPropagation);
     //彈性系數(shù)
     this.elasticX = (this.deltaX + this.startOffset - this.offset)/4;
    }
   }else{
    //上下滑動后取消close
    this.dragging = true;
    this.lockClick = true;
   }
  },

  onTouchEnd() {
   if (this.disabled) {
    return;
   }
   //回彈
   this.elasticX = 0
   if (this.dragging) {
    this.toggle(this.offset > 0 ? 'left' : 'right');
    this.dragging = false;
    // compatible with desktop scenario
    setTimeout(() => {
     this.lockClick = false;
    }, 0);
   }
  },

  toggle(direction) {
   const offset = Math.abs(this.offset);
   const THRESHOLD = 0.15;
   const threshold = this.opened ? 1 - THRESHOLD : THRESHOLD;
   const { computedLeftWidth, computedRightWidth } = this;

   if (
   computedRightWidth &&
   direction === 'right' &&
   offset > computedRightWidth * threshold
   ) {
    this.open('right');
   } else if (
   computedLeftWidth &&
   direction === 'left' &&
   offset > computedLeftWidth * threshold
   ) {
    this.open('left');
   } else {
    this.close();
   }
  },

  onClick(position = 'outside') {
   this.$emit('click', position);

   if (this.opened && !this.lockClick) {
    if (this.beforeClose) {
     this.beforeClose({
      position,
      name: this.name,
      instance: this,
     });
    } else if (this.onClose) {
     this.onClose(position, this, { name: this.name });
    } else {
     this.close(position);
    }
   }
  },

  getClickHandler(position, stop) {
   return (event) => {
    if (stop) {
     event.stopPropagation();
    }
    this.onClick(position);
   };
  },
 }
}
</script>
<style lang="stylus" scoped>
.cell_container{
 position: relative;
 overflow: hidden;
 line-height: 68px;
 height:68px;
 div{
  height: 100%;
  .cell_content{
   height: 100%;
   width: 100%;
   text-align: center;
  }
  .cell_content_active{
   height: 100%;
   width: 100%;
   text-align: center;
   &:active{
    background: #e8e8e8;
   }
  }
  .cell_left,.cell_right{
   position: absolute;
   top: 0;
   height: 100%;
   display: flex;
   color: #fff;
   .divPostion{
    position: absolute;
   }
   div{
    white-space:nowrap;
    display: flex;
    align-items: center;
    background: #ccc;
   }
  }
  .cell_left{
   left: 0;
   transform:translateX(-100%);
  }
  .cell_right{
   right: 0;
   transform:translateX(100%);
  }
 }
}
</style>

touch.js

import Vue from 'vue';
export const isServer=false;
const MIN_DISTANCE = 10;
const TouchMixinData = {
 startX: Number,
 startY: Number,
 deltaX: Number,
 deltaY: Number,
 offsetX: Number,
 offsetY: Number,
 direction: String
};

function getDirection(x,y) {
 if (x > y && x > MIN_DISTANCE) {
 return 'horizontal';
 }

 if (y > x && y > MIN_DISTANCE) {
 return 'vertical';
 }

 return '';
}


export let supportsPassive = false;

export function on(
 target,
 event,
 handler,
 passive = false
) {
 if (!isServer) {
 target.addEventListener(
  event,
  handler,
  supportsPassive ? { capture: false, passive } : false
 );
 }
}

export const TouchMixin = Vue.extend({
 data() {TouchMixinData
 return { direction: '' } ;
 },

 methods: {
 touchStart() {
  this.resetTouchStatus();
  this.startX = event.touches[0].clientX;
  this.startY = event.touches[0].clientY;
 },

 touchMove() {
  const touch = event.touches[0];
  this.deltaX = touch.clientX - this.startX;
  this.deltaY = touch.clientY - this.startY;
  this.offsetX = Math.abs(this.deltaX);
  this.offsetY = Math.abs(this.deltaY);
  this.direction =
  this.direction || getDirection(this.offsetX, this.offsetY);
 },

 resetTouchStatus() {
  this.direction = '';
  this.deltaX = 0;
  this.deltaY = 0;
  this.offsetX = 0;
  this.offsetY = 0;
 },

 // avoid Vue 2.6 event bubble issues by manually binding events
 //https://github.com/youzan/vant/issues/3015

 bindTouchEvent( el ) {

  const { onTouchStart, onTouchMove, onTouchEnd } = this;

  on(el, 'touchstart', onTouchStart);
  on(el, 'touchmove', onTouchMove);

  if (onTouchEnd) {
  on(el, 'touchend', onTouchEnd);
  on(el, 'touchcancel', onTouchEnd);
  }
 },
 },
});

引入即可?。?!

到此這篇關(guān)于vue swipeCell滑動單元格(仿微信)的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)vue swipeCell滑動單元格內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

來源:腳本之家

鏈接:https://www.jb51.net/article/195575.htm

申請創(chuàng)業(yè)報(bào)道,分享創(chuàng)業(yè)好點(diǎn)子。點(diǎn)擊此處,共同探討創(chuàng)業(yè)新機(jī)遇!

相關(guān)文章

熱門排行

信息推薦