vue实现遍历渲染可拖拽dom

2021-08-24  本文已影响0人  柚子硕
function drag (el, binding, vnode) {
  el.onmousedown = function (event) {
    const x = event.pageX - el.offsetLeft
    const y = event.pageY - el.offsetTop

    const dragDomWidth = el.offsetWidth
    const dragDomHeight = el.offsetHeight

    const screenWidth = document.body.scrollWidth
    const screenHeight = binding.value || document.body.clientHeight

    const minDragDomLeft = 0
    const maxDragDomLeft = screenWidth - dragDomWidth

    const minDragDomTop = 0
    const maxDragDomTop = screenHeight - dragDomHeight

    document.onmousemove = function (event) {
      let top = event.pageY - y
      let left = event.pageX - x

      if (top < minDragDomTop) {
        top = minDragDomTop
      } else if (top > maxDragDomTop) {
        top = maxDragDomTop
      }

      if (left < minDragDomLeft) {
        left = minDragDomLeft
      } else if (left > maxDragDomLeft) {
        left = maxDragDomLeft
      }

      el.style.left = left + 'px'
      el.style.top = top + 'px'

      vnode.context.$emit('move', {
        top,
        left
      })
    }

    document.onmouseup = function () {
      vnode.context.$emit('drag-end', {
        top: el.style.top,
        left: el.style.left
      })
      document.onmousemove = null
      document.onmouseup = null
    }
  }
}

export default {
  bind: drag
}

组件
<template>
  <div
    v-drag="1080"
    class="points-area-wrap"
    :class="{ animation }"
    :style="pointStyle">
    <span
      v-if="$slots.default"
      class="point-title"
      @click="handleClick"><slot /></span>
    <span
      class="pin"
      @click="handleClick">
      <i class="iconfont icon-ios-pin" />
    </span>
  </div>
</template>

<script>
import drag from '@/directive/drag'

export default {
  name: 'PointsQuYu',
  directives: {
    drag
  },
  props: {
    animation: {
      type: Boolean,
      default: false
    },
    top: {
      type: String,
      default: null
    },
    left: {
      type: String,
      default: null
    },
    right: {
      type: String,
      default: null
    },
    bottom: {
      type: String,
      default: null
    }
  },
  data () {
    return {
      dragged: false
    }
  },
  computed: {
    pointStyle () {
      return {
        top: this.top || 0,
        left: this.left || 0
      }
    }
  },
  created () {
    this.$on('move', () => {
      this.dragged = true
    })
  },
  methods: {
    handleClick () {
      alert('quyu')
      if (!this.dragged) {
        this.$emit('click')
      } else {
        this.dragged = false
      }
    }
  }
}
</script>

<style scoped lang="stylus">
@keyframes bounce {
  25% {
    transform: translateY(10px);
  }

  50%, 100% {
    transform: translateY(0);
  }

  75% {
    transform: translateY(-10px);
  }
}

.points-area-wrap {
  position: absolute;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  z-index: 1;

  .point-title {
    width: auto;
    height: 25px;
    line-height: 25px;
    font-size: 15px;
    background-color: rgba(22, 65, 255, 0.8);
    border-radius: 24px;
    color: #fff;
    padding: 0 10px;
    cursor: pointer;
  }

  .pin {
    margin-top: 12px;
    width: 30px;
    height: 30px;
    cursor: pointer;

    .iconfont {
      color: #1890ff;
      font-size: 30px;
    }
  }

  &.animation {
    animation: bounce 1s linear infinite;
  }
}
</style>
上一篇 下一篇

猜你喜欢

热点阅读