左滑删除组件

左滑删除组件

组件SlideDelete.vue

<template>
    <div :class="componentName" v-slide="">
      <div class="slide__top" ref="slideItem" :style="wrapperStyle">
        <slot name="item" v-if="$slots.item" />
        <slot v-else />
      </div>
      <div :class="`slide__del ${delCls}`" @click="$emit('del-click')">
        <slot name="del" v-if="$slots.del" />
        <span v-else>{{ delText }}</span>
      </div>
    </div>
</template>

<script>
  const COMPONENT_NAME = 'slide-delete'

  export default {
    name: COMPONENT_NAME,

    computed: {
      wrapperStyle() {
        return {
          transform: `translate3d(${this.offset}px, 0, 0)`,
          transition: this.draging ? 'none' : '.6s cubic-bezier(0.18, 0.89, 0.32, 1)'
        }
      }
    },

    data() {
      return {
        delAreaWidth: 70,
        open: false,
        offset: 0,
        draging: false,
        componentName: COMPONENT_NAME
      }
    },

    props: {
      threshold: {
        type: Number,
        default: 35
      },
      delCls: {
        type: String,
        default: 'slide__del-red'
      },
      delText: {
        type: String,
        default: '删除'
      }
    },

    methods: {
      getTouch(touch) {
        return touch.changedTouches[0] || touch.targetTouches[0]
      },

      isAngleLeft(diffX, diffY) {
        const x = Math.abs(diffX)
        const y = Math.abs(diffY)
        return !(x < 5 || (x >= 5 && y >= x * 1.73))
      },

      setOpen(type = false) {
        this.open = type
        this.offset = type ? -this.delAreaWidth : 0
      },

      closeOther() {
        this.$parent.$children
          .filter(vNode =>
            vNode.$el.classList.contains(this.componentName) &&
            vNode.open &&
            vNode !== this
          )
          .forEach(vNode => {
            vNode.setOpen(false)
          })
      }
    },

    directives: {
      slide: {
        bind(el, binding, vNode) {
          let startX, startY, diffX, diffY
          let vm = vNode.context

          el.addEventListener('touchstart', (e)=> {
            const { clientX, clientY } = vm.getTouch(e)
            startX = clientX
            startY = clientY
            vm.draging = true
          })

          el.addEventListener('touchmove', (e)=> {
            const { clientX, clientY } = vm.getTouch(e)
            diffX = clientX - startX
            diffY = clientY - startY

            if (
              vm.isAngleLeft(diffX, diffY) &&
              Math.abs(diffX) <= vm.delAreaWidth &&
              (
                diffX < 0 && !vm.open ||
                diffX > 0 && vm.open
              )
            ) {
              event.preventDefault()
              vm.offset = vm.open ? diffX - vm.delAreaWidth : diffX
            }
          })

          el.addEventListener('touchend', (e)=> {
            const { clientX, clientY } = vm.getTouch(e)
            diffX = clientX - startX
            diffY = clientY - startY
            vm.draging = false

            if (
              diffX > 0 && diffX > vm.threshold ||
              diffX < 0 && diffX > -vm.threshold
            ) {
              vm.open = false
              vm.offset = 0
            } else if (
              vm.isAngleLeft(diffX, diffY) &&
              (
                diffX > 0 && diffX <= vm.threshold ||
                diffX < 0 && diffX <= -vm.threshold
              )
            ) {
              vm.open = true
              vm.offset = -vm.delAreaWidth
              vm.closeOther.call(vm, el)
              vm.$emit('slip-open', vm)
            }
          })
        }
      }
    }
  }
</script>

<style lang="scss">
  .slide {
    position: relative;

    .slide__top {
      position: relative;
      width: 100%;
      z-index: 1;
      background-color: #fff;
    }

    .slide__del {
      position: absolute;
      right: 0;
      top: 0;
      bottom: 0;
      width: 70px;
      color: #fff;
      z-index: 0;

      span {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        text-align: center;
      }
    }

    .slide__del-red {
      background-color: #f23030;
    }
  }
</style>

使用:

<template>
    <div id="app">
        <div class="wrapper">
            <div class="wrapper-item">
                <slide-del
                  v-for="(item, i) in list"
                  :key="i"
                  ref="slipDel"
                  del-text="移除"
                  @del-click="del(item.id)"
                  @slip-open="slipOpen"
                >
                  <div class="demo-item">{{item.title}}</div>
                  <!-- <div slot="del">删除icon可编辑</div> -->
                </slide-del>
            </div>
        </div>
    </div>
</template>

<script>
  import SlideDel from '@/components/SlideDelete.vue'

  export default {
    name: 'app',
    data() {
      return {
        list: [{title: 1, id: 1},{title: 2, id: 2},{title: 3, id: 3}],
      }
    },
    methods: {
      slipOpen(vm) {
      },
      //删除操作
      del(id) {
        console.log(id)
      }
    },
    components: {
      SlideDel
    }
  }
</script>

<style>
    .demo-item {
        padding: 20px 0;
        border-bottom: 1px #ccc solid;
    }
    .wrapper {
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        bottom: 0;
        overflow: auto;
    }
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值