当前位置:首页 >  站长 >  编程技术 >  正文

vue swipeCell滑动单元格(仿微信)的实现示例

 2020-10-28 14:07  来源: 脚本之家   我来投稿 撤稿纠错

  【推荐】海外独服/站群服务器/高防

这篇文章主要介绍了vue swipeCell滑动单元格(仿微信)的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

抽离Vant weapp滑动单元格代码改造而成

带有拉动弹性回弹效果

demo展示:https://littaotao.github.io/me/index(切换为浏览器调试的手机模式并且再次刷新一次)

<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'}">标记</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'}">不再关注</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 常规 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定位时获取宽度为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);
     //弹性系数
     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);
  }
 },
 },
});

引入即可!!!

到此这篇关于vue swipeCell滑动单元格(仿微信)的实现示例的文章就介绍到这了,更多相关vue swipeCell滑动单元格内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

来源:脚本之家

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

申请创业报道,分享创业好点子。点击此处,共同探讨创业新机遇!

相关文章

热门排行

信息推荐