VUE带ID拖动组件实现

VUE源码

index.js

<template>
  <div
    :style="style"
    :class="[
      {
        [classNameActive]: enabled,
        [classNameDragging]: dragging,
        [classNameResizing]: resizing,
        [classNameDraggable]: draggable,
        [classNameResizable]: resizable
      },
      className,
      commonClassName
    ]"
    @mousedown="elementDown"
    @touchstart="elementTouchDown"
  >
    <div
      v-for="actualHandle in actualHandles"
      :key="actualHandle"
      :class="[classNameHandle, classNameHandle + '-' + actualHandle]"
      :style="{ display: enabled ? 'block' : 'none' }"
      @mousedown.stop.prevent="handleDown(actualHandle, $event)"
      @touchstart.stop.prevent="handleTouchDown(actualHandle, $event)"
    >
      <slot :name="actualHandle"></slot>
    </div>
    <slot></slot>
  </div>
</template>

<script>
import {
  matchesSelectorToParentElements,
  addEvent,
  removeEvent
} from './utils/dom'

const events = {
  mouse: {
    start: 'mousedown',
    move: 'mousemove',
    stop: 'mouseup'
  },
  touch: {
    start: 'touchstart',
    move: 'touchmove',
    stop: 'touchend'
  }
}
const userSelectNone = {
  userSelect: 'none',
  MozUserSelect: 'none',
  WebkitUserSelect: 'none',
  MsUserSelect: 'none'
}
const userSelectAuto = {
  userSelect: 'auto',
  MozUserSelect: 'auto',
  WebkitUserSelect: 'auto',
  MsUserSelect: 'auto'
}
let eventsFor = events.mouse
export default {
  replace: true,
  name: 'VueDraggableResizable',
  props: {
    className: {
      type: String,
      default: 'vdr'
    },
    commonClassName: {
      type: String,
      default: ''
    },
    classNameDraggable: {
      type: String,
      default: 'draggable'
    },
    classNameResizable: {
      type: String,
      default: 'resizable'
    },
    customId: {
      // 自定义id,用于区分不同的实例
      type: [String, Number],
      default: ''
    },
    classNameDragging: {
      type: String,
      default: 'dragging'
    },
    classNameResizing: {
      type: String,
      default: 'resizing'
    },
    classNameActive: {
      type: String,
      default: 'active'
    },
    classNameHandle: {
      type: String,
      default: 'handle'
    },
    disableUserSelect: {
      type: Boolean,
      default: true
    },
    enableNativeDrag: {
      type: Boolean,
      default: false
    },
    preventDeactivation: {
      type: Boolean,
      default: false
    },
    active: {
      type: Boolean,
      default: false
    },
    draggable: {
      type: Boolean,
      default: true
    },
    resizable: {
      type: Boolean,
      default: true
    },
    lockAspectRatio: {
      type: Boolean,
      default: false
    },
    w: {
      type: Number,
      default: 200,
      validator: val => val > 0
    },
    h: {
      type: Number,
      default: 200,
      validator: val => val > 0
    },
    minWidth: {
      type: Number,
      default: 0,
      validator: val => val >= 0
    },
    minHeight: {
      type: Number,
      default: 0,
      validator: val => val >= 0
    },
    maxWidth: {
      type: Number,
      default: null,
      validator: val => val >= 0
    },
    maxHeight: {
      type: Number,
      default: null,
      validator: val => val >= 0
    },
    x: {
      type: Number,
      default: 0,
      validator: val => typeof val === 'number'
    },
    y: {
      type: Number,
      default: 0,
      validator: val => typeof val === 'number'
    },
    z: {
      type: [String, Number],
      default: 'auto',
      validator: val =>
        typeof val === 'string' ? val === 'auto' : val >= 0
    },
    handles: {
      type: Array,
      default: () => ['tl', 'tm', 'tr', 'mr', 'br', 'bm', 'bl', 'ml'],
      validator: val => {
        const s = new Set([
          'tl',
          'tm',
          'tr',
          'mr',
          'br',
          'bm',
          'bl',
          'ml'
        ])
        return new Set(val.filter(h => s.has(h))).size === val.length
      }
    },
    dragHandle: {
      type: String,
      default: null
    },
    dragCancel: {
      type: String,
      default: null
    },
    axis: {
      type: String,
      default: 'both',
      validator: val => ['x', 'y', 'both'].includes(val)
    },
    grid: {
      type: Array,
      default: () => [1, 1]
    },
    parent: {
      type: Boolean,
      default: false
    },
    onDragStart: {
      type: Function,
      default: null
    },
    onResizeStart: {
      type: Function,
      default: null
    },
    parentW: {
      type: Number,
      default: 0,
      validator: function (val) {
        return val >= 0
      }
    },
    parentH: {
      type: Number,
      default: 0,
      validator: function (val) {
        return val >= 0
      }
    }
  },
  data: function () {
    return {
      rawWidth: this.w,
      rawHeight: this.h,
      rawLeft: this.x,
      rawTop: this.y,
      rawRight: null,
      rawBottom: null,
      left: this.x,
      top: this.y,
      right: null,
      bottom: null,
      aspectFactor: this.w / this.h,
      parentWidth: null,
      parentHeight: null,
      minW: this.minWidth,
      minH: this.minHeight,
      maxW: this.maxWidth,
      maxH: this.maxHeight,
      handle: null,
      enabled: this.active,
      resizing: false,
      dragging: false,
      zIndex: this.z
    }
  },
  computed: {
    style () {
      // console.log(this.left)
      return {
        position: 'absolute',
        top: this.top + 'px',
        left: this.left + 'px',
        width: this.width + 'px',
        height: this.height + 'px',
        zIndex: this.zIndex,
        ...(this.dragging && this.disableUserSelect
          ? userSelectNone
          : userSelectAuto)
      }
    },
    actualHandles () {
      if (!this.resizable) return []
      return this.handles
    },
    width () {
      return this.parentWidth - this.left - this.right
    },
    height () {
      return this.parentHeight - this.top - this.bottom
    },
    resizingOnX () {
      return (
        Boolean(this.handle) &&
        (this.handle.includes('l') || this.handle.includes('r'))
      )
    },
    resizingOnY () {
      return (
        Boolean(this.handle) &&
        (this.handle.includes('t') || this.handle.includes('b'))
      )
    },
    isCornerHandle () {
      return (
        Boolean(this.handle) &&
        ['tl', 'tr', 'br', 'bl'].includes(this.handle)
      )
    }
  },
  watch: {
    active (val) {
      this.enabled = val
      if (val) {
        this.$emit('activated', this.customId)
      } else {
        this.$emit('deactivated')
      }
    },
    z (val) {
      if (val >= 0 || val === 'auto') {
        this.zIndex = val
      }
    },
    rawLeft (newLeft) {
      // console.log('newLeft' + newLeft)
      const bounds = this.bounds
      const aspectFactor = this.aspectFactor
      const lockAspectRatio = this.lockAspectRatio
      const left = this.left
      const top = this.top
      if (bounds.minLeft !== null && newLeft < bounds.minLeft) {
        newLeft = bounds.minLeft
      } else if (bounds.maxLeft !== null && bounds.maxLeft < newLeft) {
        newLeft = bounds.maxLeft
      }
      if (lockAspectRatio && this.resizingOnX) {
        this.rawTop = top - (left - newLeft) / aspectFactor
      }
      this.left = newLeft
    },
    rawRight (newRight) {
      const bounds = this.bounds
      const aspectFactor = this.aspectFactor
      const lockAspectRatio = this.lockAspectRatio
      const right = this.right
      const bottom = this.bottom
      if (bounds.minRight !== null && newRight < bounds.minRight) {
        newRight = bounds.minRight
      } else if (bounds.maxRight !== null && bounds.maxRight < newRight) {
        newRight = bounds.maxRight
      }
      if (lockAspectRatio && this.resizingOnX) {
        this.rawBottom = bottom - (right - newRight) / aspectFactor
      }
      this.right = newRight
    },
    rawTop (newTop) {
      const bounds = this.bounds
      const aspectFactor = this.aspectFactor
      const lockAspectRatio = this.lockAspectRatio
      const left = this.left
      const top = this.top
      if (bounds.minTop !== null && newTop < bounds.minTop) {
        newTop = bounds.minTop
      } else if (bounds.maxTop !== null && bounds.maxTop < newTop) {
        newTop = bounds.maxTop
      }
      if (lockAspectRatio && this.resizingOnY) {
        this.rawLeft = left - (top - newTop) * aspectFactor
      }
      this.top = newTop
    },
    rawBottom (newBottom) {
      const bounds = this.bounds
      const aspectFactor = this.aspectFactor
      const lockAspectRatio = this.lockAspectRatio
      const right = this.right
      const bottom = this.bottom
      if (bounds.minBottom !== null && newBottom < bounds.minBottom) {
        newBottom = bounds.minBottom
      } else if (
        bounds.maxBottom !== null &&
        bounds.maxBottom < newBottom
      ) {
        newBottom = bounds.maxBottom
      }
      if (lockAspectRatio && this.resizingOnY) {
        this.rawRight = right - (bottom - newBottom) * aspectFactor
      }
      this.bottom = newBottom
    },
    x () {
      if (this.resizing || this.dragging) {
        return
      }
      if (this.parent) {
        this.bounds = this.calcDragLimits()
      }
      const delta = this.x - this.left
      if (delta % this.grid[0] === 0) {
        this.rawLeft = this.x
        this.rawRight = this.right - delta
      }
    },
    y () {
      if (this.resizing || this.dragging) {
        return
      }
      if (this.parent) {
        this.bounds = this.calcDragLimits()
      }
      const delta = this.y - this.top
      if (delta % this.grid[1] === 0) {
        this.rawTop = this.y
        this.rawBottom = this.bottom - delta
      }
    },
    lockAspectRatio (val) {
      if (val) {
        this.aspectFactor = this.width / this.height
      } else {
        this.aspectFactor = undefined
      }
    },
    minWidth (val) {
      if (val > 0 && val <= this.width) {
        this.minW = val
      }
    },
    minHeight (val) {
      if (val > 0 && val <= this.height) {
        this.minH = val
      }
    },
    maxWidth (val) {
      this.maxW = val
    },
    maxHeight (val) {
      this.maxH = val
    },
    w () {
      if (this.resizing || this.dragging) {
        return
      }
      if (this.parent) {
        this.bounds = this.calcResizeLimits()
      }
      const delta = this.width - this.w
      if (delta % this.grid[0] === 0) {
        this.rawRight = this.right + delta
      }
    },
    h () {
      if (this.resizing || this.dragging) {
        return
      }
      if (this.parent) {
        this.bounds = this.calcResizeLimits()
      }
      const delta = this.height - this.h
      if (delta % this.grid[1] === 0) {
        this.rawBottom = this.bottom + delta
      }
    },

    parentW (val) {
      this.right = val - this.width - this.left
      this.parentWidth = val
    },

    parentH (val) {
      this.bottom = val - this.height - this.top
      this.parentHeight = val
    }
  },
  created: function () {
    // eslint-disable-next-line
    if (this.maxWidth && this.minWidth > this.maxWidth)
      console.warn(
        '[Vdr warn]: Invalid prop: minWidth cannot be greater than maxWidth'
      )
    // eslint-disable-next-line
    if (this.maxWidth && this.minHeight > this.maxHeight)
      console.warn(
        '[Vdr warn]: Invalid prop: minHeight cannot be greater than maxHeight'
      )
    this.resetBoundsAndMouseState()
  },
  mounted: function () {
    this.parentElement = this.$el.parentNode
    this.parentWidth = this.parentW ? this.parentW : this.parentElement.clientWidth
    this.parentHeight = this.parentH ? this.parentH : this.parentElement.clientHeight
    if (!this.enableNativeDrag) {
      this.$el.ondragstart = () => false
    }
    [this.parentWidth, this.parentHeight] = this.getParentSize()
    this.rawRight = this.parentWidth - this.rawWidth - this.rawLeft
    this.rawBottom = this.parentHeight - this.rawHeight - this.rawTop
    addEvent(document.documentElement, 'mousedown', this.deselect)
    addEvent(
      document.documentElement,
      'touchend touchcancel',
      this.deselect
    )
    addEvent(window, 'resize', this.checkParentSize)
  },
  beforeDestroy: function () {
    removeEvent(document.documentElement, 'mousedown', this.deselect)
    removeEvent(document.documentElement, 'touchstart', this.handleUp)
    removeEvent(document.documentElement, 'mousemove', this.move)
    removeEvent(document.documentElement, 'touchmove', this.move)
    removeEvent(document.documentElement, 'mouseup', this.handleUp)
    removeEvent(
      document.documentElement,
      'touchend touchcancel',
      this.deselect
    )
    removeEvent(window, 'resize', this.checkParentSize)
  },
  methods: {
    /**
     * 手动设置位置
     * @param left 左边距
     * @param top 又边距
     */
    setPosByHands (left, top) {
      let _self = this
      setTimeout(() => {
        _self.left = left
        _self.rawLeft = left
      }, 2000)
      // console.log(this.style)
    },
    resetBoundsAndMouseState () {
      this.mouseClickPosition = {
        mouseX: 0,
        mouseY: 0,
        x: 0,
        y: 0,
        w: 0,
        h: 0
      }
      this.bounds = {
        minLeft: null,
        maxLeft: null,
        minRight: null,
        maxRight: null,
        minTop: null,
        maxTop: null,
        minBottom: null,
        maxBottom: null
      }
    },
    checkParentSize () {
      if (this.parent) {
        const [newParentWidth, newParentHeight] = this.getParentSize()
        const deltaX = this.parentWidth - newParentWidth
        const deltaY = this.parentHeight - newParentHeight
        this.rawRight -= deltaX
        this.rawBottom -= deltaY
        this.parentWidth = newParentWidth
        this.parentHeight = newParentHeight
      }
    },
    getParentSize () {
      if (this.parent) {
        const style = window.getComputedStyle(
          this.$el.parentNode,
          null
        )
        return [
          parseInt(style.getPropertyValue('width'), 10),
          parseInt(style.getPropertyValue('height'), 10)
        ]
      }
      return [null, null]
    },
    elementTouchDown (e) {
      eventsFor = events.touch
      this.elementDown(e)
    },
    elementDown (e) {
      // 没有e则是外部调用方式
      const target = e ? e.target || e.srcElement : ''
      const classNameArr = target.className
        ? target.className.split(' ')
        : []
      // 被点击元素是关闭按钮
      if (classNameArr.includes('close')) {
        return
      }
      if (e && this.$el.contains(target)) {
        if (this.onDragStart && this.onDragStart(e) === false) {
          return
        }
        if (
          (this.dragHandle &&
            !matchesSelectorToParentElements(
              target,
              this.dragHandle,
              this.$el
            )) ||
          (this.dragCancel &&
            matchesSelectorToParentElements(
              target,
              this.dragCancel,
              this.$el
            ))
        ) {
          return
        }
        if (!this.enabled) {
          this.enabled = true
          this.$emit('activated', this.customId)
          this.$emit('update:active', true)
        }
        if (this.draggable) {
          this.dragging = true
        }
        this.mouseClickPosition.mouseX = e.touches
          ? e.touches[0].pageX
          : e.pageX
        this.mouseClickPosition.mouseY = e.touches
          ? e.touches[0].pageY
          : e.pageY
        this.mouseClickPosition.left = this.left
        this.mouseClickPosition.right = this.right
        this.mouseClickPosition.top = this.top
        this.mouseClickPosition.bottom = this.bottom
        if (this.parent) {
          this.bounds = this.calcDragLimits()
        }
        addEvent(document.documentElement, eventsFor.move, this.move)
        addEvent(
          document.documentElement,
          eventsFor.stop,
          this.handleUp
        )
      } else {
        // 用于父组件直接调用
        if (!this.enabled) {
          // console.log(666)
          this.enabled = true
          this.$emit('activated', this.customId)
          this.$emit('update:active', true)
          if (this.draggable) {
            this.dragging = true
          }
        }
      }
    },
    calcDragLimits () {
      return {
        minLeft: (this.parentWidth + this.left) % this.grid[0],
        maxLeft:
        Math.floor(
          (this.parentWidth - this.width - this.left) /
          this.grid[0]
        ) *
        this.grid[0] +
        this.left,
        minRight: (this.parentWidth + this.right) % this.grid[0],
        maxRight:
        Math.floor(
          (this.parentWidth - this.width - this.right) /
          this.grid[0]
        ) *
        this.grid[0] +
        this.right,
        minTop: (this.parentHeight + this.top) % this.grid[1],
        maxTop:
        Math.floor(
          (this.parentHeight - this.height - this.top) /
          this.grid[1]
        ) *
        this.grid[1] +
        this.top,
        minBottom: (this.parentHeight + this.bottom) % this.grid[1],
        maxBottom:
        Math.floor(
          (this.parentHeight - this.height - this.bottom) /
          this.grid[1]
        ) *
        this.grid[1] +
        this.bottom
      }
    },
    deselect (e) {
      const target = e.target || e.srcElement
      const regex = new RegExp(this.className + '-([trmbl]{2})', '')
      if (!this.$el.contains(target) && !regex.test(target.className)) {
        if (this.enabled && !this.preventDeactivation) {
          this.enabled = false

          this.$emit('deactivated')
          this.$emit('update:active', false)
        }

        removeEvent(document.documentElement, eventsFor.move, this.handleResize)
      }
      this.resetBoundsAndMouseState()
    },
    handleTouchDown (handle, e) {
      eventsFor = events.touch
      this.handleDown(handle, e)
    },
    handleDown (handle, e) {
      if (this.onResizeStart && this.onResizeStart(handle, e) === false) {
        return
      }
      if (e.stopPropagation) e.stopPropagation()
      // Here we avoid a dangerous recursion by faking
      // corner handles as middle handles
      if (this.lockAspectRatio && !handle.includes('m')) {
        this.handle = 'm' + handle.substring(1)
      } else {
        this.handle = handle
      }
      this.resizing = true
      this.mouseClickPosition.mouseX = e.touches
        ? e.touches[0].pageX
        : e.pageX
      this.mouseClickPosition.mouseY = e.touches
        ? e.touches[0].pageY
        : e.pageY
      this.mouseClickPosition.left = this.left
      this.mouseClickPosition.right = this.right
      this.mouseClickPosition.top = this.top
      this.mouseClickPosition.bottom = this.bottom
      this.bounds = this.calcResizeLimits()
      addEvent(document.documentElement, eventsFor.move, this.handleMove)
      addEvent(document.documentElement, eventsFor.stop, this.handleUp)
    },
    calcResizeLimits () {
      let minW = this.minW
      let minH = this.minH
      let maxW = this.maxW
      let maxH = this.maxH
      const aspectFactor = this.aspectFactor
      const [gridX, gridY] = this.grid
      const width = this.width
      const height = this.height
      const left = this.left
      const top = this.top
      const right = this.right
      const bottom = this.bottom
      if (this.lockAspectRatio) {
        if (minW / minH > aspectFactor) {
          minH = minW / aspectFactor
        } else {
          minW = aspectFactor * minH
        }
        if (maxW && maxH) {
          maxW = Math.min(maxW, aspectFactor * maxH)
          maxH = Math.min(maxH, maxW / aspectFactor)
        } else if (maxW) {
          maxH = maxW / aspectFactor
        } else if (maxH) {
          maxW = aspectFactor * maxH
        }
      }
      maxW = maxW - (maxW % gridX)
      maxH = maxH - (maxH % gridY)
      const limits = {
        minLeft: null,
        maxLeft: null,
        minTop: null,
        maxTop: null,
        minRight: null,
        maxRight: null,
        minBottom: null,
        maxBottom: null
      }
      if (this.parent) {
        limits.minLeft = (this.parentWidth + left) % gridX
        limits.maxLeft =
          left + Math.floor((width - minW) / gridX) * gridX
        limits.minTop = (this.parentHeight + top) % gridY
        limits.maxTop =
          top + Math.floor((height - minH) / gridY) * gridY
        limits.minRight = (this.parentWidth + right) % gridX
        limits.maxRight =
          right + Math.floor((width - minW) / gridX) * gridX
        limits.minBottom = (this.parentHeight + bottom) % gridY
        limits.maxBottom =
          bottom + Math.floor((height - minH) / gridY) * gridY
        if (maxW) {
          limits.minLeft = Math.max(
            limits.minLeft,
            this.parentWidth - right - maxW
          )
          limits.minRight = Math.max(
            limits.minRight,
            this.parentWidth - left - maxW
          )
        }
        if (maxH) {
          limits.minTop = Math.max(
            limits.minTop,
            this.parentHeight - bottom - maxH
          )
          limits.minBottom = Math.max(
            limits.minBottom,
            this.parentHeight - top - maxH
          )
        }
        if (this.lockAspectRatio) {
          limits.minLeft = Math.max(
            limits.minLeft,
            left - top * aspectFactor
          )
          limits.minTop = Math.max(
            limits.minTop,
            top - left / aspectFactor
          )
          limits.minRight = Math.max(
            limits.minRight,
            right - bottom * aspectFactor
          )
          limits.minBottom = Math.max(
            limits.minBottom,
            bottom - right / aspectFactor
          )
        }
      } else {
        limits.minLeft = null
        limits.maxLeft =
          left + Math.floor((width - minW) / gridX) * gridX
        limits.minTop = null
        limits.maxTop =
          top + Math.floor((height - minH) / gridY) * gridY
        limits.minRight = null
        limits.maxRight =
          right + Math.floor((width - minW) / gridX) * gridX
        limits.minBottom = null
        limits.maxBottom =
          bottom + Math.floor((height - minH) / gridY) * gridY
        if (maxW) {
          limits.minLeft = -(right + maxW)
          limits.minRight = -(left + maxW)
        }
        if (maxH) {
          limits.minTop = -(bottom + maxH)
          limits.minBottom = -(top + maxH)
        }
        if (this.lockAspectRatio && (maxW && maxH)) {
          limits.minLeft = Math.min(limits.minLeft, -(right + maxW))
          limits.minTop = Math.min(limits.minTop, -(maxH + bottom))
          limits.minRight = Math.min(limits.minRight, -left - maxW)
          limits.minBottom = Math.min(limits.minBottom, -top - maxH)
        }
      }
      return limits
    },
    move (e) {
      if (this.resizing) {
        this.handleMove(e)
      } else if (this.dragging) {
        this.elementMove(e)
      }
    },
    elementMove (e) {
      const axis = this.axis
      // const grid = this.grid
      const mouseClickPosition = this.mouseClickPosition
      const tmpDeltaX =
        axis && axis !== 'y'
          ? mouseClickPosition.mouseX -
          (e.touches ? e.touches[0].pageX : e.pageX)
          : 0
      const tmpDeltaY =
        axis && axis !== 'x'
          ? mouseClickPosition.mouseY -
          (e.touches ? e.touches[0].pageY : e.pageY)
          : 0
      const [deltaX, deltaY] = this.snapToGrid(
        this.grid,
        tmpDeltaX,
        tmpDeltaY
      )
      this.rawTop = mouseClickPosition.top - deltaY
      this.rawBottom = mouseClickPosition.bottom + deltaY
      this.rawLeft = mouseClickPosition.left - deltaX
      this.rawRight = mouseClickPosition.right + deltaX
      // console.log(this.left, this.top)
      // console.log(this.rawLeft, this.rawTop)
      this.$emit('dragging', this.left, this.top)
    },
    handleMove (e) {
      const handle = this.handle
      const mouseClickPosition = this.mouseClickPosition
      const tmpDeltaX =
        mouseClickPosition.mouseX -
        (e.touches ? e.touches[0].pageX : e.pageX)
      const tmpDeltaY =
        mouseClickPosition.mouseY -
        (e.touches ? e.touches[0].pageY : e.pageY)
      const [deltaX, deltaY] = this.snapToGrid(
        this.grid,
        tmpDeltaX,
        tmpDeltaY
      )
      if (handle.includes('b')) {
        this.rawBottom = mouseClickPosition.bottom + deltaY
      } else if (handle.includes('t')) {
        this.rawTop = mouseClickPosition.top - deltaY
      }
      if (handle.includes('r')) {
        this.rawRight = mouseClickPosition.right + deltaX
      } else if (handle.includes('l')) {
        this.rawLeft = mouseClickPosition.left - deltaX
      }
      this.$emit(
        'resizing',
        this.left,
        this.top,
        this.width,
        this.height
      )
    },
    handleUp (e) {
      this.handle = null
      this.resetBoundsAndMouseState()
      this.rawTop = this.top
      this.rawBottom = this.bottom
      this.rawLeft = this.left
      this.rawRight = this.right
      if (this.resizing) {
        this.resizing = false
        this.$emit(
          'resizestop',
          this.left,
          this.top,
          this.width,
          this.height,
          this.customId
        )
      }
      if (this.dragging) {
        this.dragging = false
        this.$emit('dragstop', this.left, this.top, this.customId)
      }
      removeEvent(
        document.documentElement,
        eventsFor.move,
        this.handleMove
      )
    },
    snapToGrid (grid, pendingX, pendingY) {
      const x = Math.round(pendingX / grid[0]) * grid[0]
      const y = Math.round(pendingY / grid[1]) * grid[1]
      return [x, y]
    }
  }
}
</script>

JS源码

utils/dom.js

import { isFunction } from './fns'

export function matchesSelectorToParentElements (el, selector, baseNode) {
  let node = el

  const matchesSelectorFunc = [
    'matches',
    'webkitMatchesSelector',
    'mozMatchesSelector',
    'msMatchesSelector',
    'oMatchesSelector'
  ].find(func => isFunction(node[func]))

  if (!isFunction(node[matchesSelectorFunc])) return false

  do {
    if (node[matchesSelectorFunc](selector)) return true
    if (node === baseNode) return false
    node = node.parentNode
  } while (node)

  return false
}

export function addEvent (el, event, handler) {
  if (!el) {
    return
  }
  if (el.attachEvent) {
    el.attachEvent('on' + event, handler)
  } else if (el.addEventListener) {
    el.addEventListener(event, handler, true)
  } else {
    el['on' + event] = handler
  }
}

export function removeEvent (el, event, handler) {
  if (!el) {
    return
  }
  if (el.detachEvent) {
    el.detachEvent('on' + event, handler)
  } else if (el.removeEventListener) {
    el.removeEventListener(event, handler, true)
  } else {
    el['on' + event] = null
  }
}

utils/fns.js

export function isFunction (func) {
  return (typeof func === 'function' || Object.prototype.toString.call(func) === '[object Function]')
}

CSS源码

	.vdr {
	  touch-action: none;
	  position: absolute;
	  box-sizing: border-box;
	}
	.handle {
	  box-sizing: border-box;
	  position: absolute;
	  width: 10px;
	  height: 10px;
	  background: #EEE;
	  border: 1px solid #333;
	}
	.handle-tl {
	  top: -10px;
	  left: -10px;
	  cursor: nw-resize;
	}
	.handle-tm {
	  top: -10px;
	  left: 50%;
	  margin-left: -5px;
	  cursor: n-resize;
	}
	.handle-tr {
	  top: -10px;
	  right: -10px;
	  cursor: ne-resize;
	}
	.handle-ml {
	  top: 50%;
	  margin-top: -5px;
	  left: -10px;
	  cursor: w-resize;
	}
	.handle-mr {
	  top: 50%;
	  margin-top: -5px;
	  right: -10px;
	  cursor: e-resize;
	}
	.handle-bl {
	  bottom: -10px;
	  left: -10px;
	  cursor: sw-resize;
	}
	.handle-bm {
	  bottom: -10px;
	  left: 50%;
	  margin-left: -5px;
	  cursor: s-resize;
	}
	.handle-br {
	  bottom: -10px;
	  right: -10px;
	  cursor: se-resize;
	}
	@media only screen and (max-width: 768px) {
	  [class*="handle-"]:before {
	    content: '';
	    left: -10px;
	    right: -10px;
	    bottom: -10px;
	    top: -10px;
	    position: absolute;
	  }
	}

使用方式VUE

	<vue-draggable-resizable
	   v-for="(device, index) in deviceList"
	   :key="index"
	   :custom-id="device.id"
	   :w="device.width"
	   :h="device.height"
	   :x="device.left"
	   :y="device.top"
	   :min-width="30"
	   :min-height="30"
	   :lock-aspect-ratio="true"
	   :active="device.active"
	   :parent="true"
	   :parentW="Number(rorm.width)"
	   :parentH="Number(form.height)"
	   @resizestop="handleResizeStop"
	   @dragstop="handleDragStop"
	   @activated="dragResizeClick(device.key, device.id)"
	   @deactivated="cancelSelect()"
	   class="device-drag-resize"
	 ></vue-draggable-resizable>

使用方式JS

缩放结束调用

 /**
   * 组件缩放结束后重新赋值
   * @param left 左边距
   * @param top 上边距
   * @param width 宽度
   * @param height 高度
   * @param customId 组件id
   */
  handleResizeStop (left, top, width, height, customId) {
    console.info(111)
  }

拖动结束调用

 /**
   * 组件拖拽结束后重新赋值
   * @param left 左边距
   * @param top 上边距
   * @param customId 组件id
   */
  handleDragStop (left, top, customId) {
    console.info(111)
  },

点击事件调用

  /**
    * 点击事件调用
    *
    * @param id 拖动的组件id
    */
   dragResizeClick (key, id) {
     console.info(1111)
   }

点击组件外部调用

   /**
     * 点击组件外部调用
     */
    cancelSelect () {
      console.info(1111)
    },
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值