实现 组件: 拖拽、缩放、 生成辅助线 以及吸附功能。

主要介绍实现拖拽时生成辅助线 以及吸附功能。
注意的是 鼠标按下的mousedown 鼠标移动时事件处理 document.onmousemove 和 鼠标抬起document.onmouseup事件处理

生成辅助线和实现吸附功能
当拖拽元素时,通过比较拖拽元素与画布上其他元素的位置,当某个元素与拖拽元素的距离接近时,显示辅助线

拖拽元素在垂直方向上不同对齐方式 5种

底对顶:拖拽元素的底部与对比元素的顶部对齐
底对底:拖拽元素的底部与对比元素的底部对齐
中对中:拖拽元素的中部与对比元素的中部对齐
顶对顶:拖拽元素的顶部与对比元素的顶部对齐
顶对底:拖拽元素的顶部与对比元素的底部对齐

在这里插入图片描述
拖拽元素在水平方向上不同对齐方式 5种

右对左:拖拽元素的右侧与对比元素的左侧对齐
右对右:拖拽元素的右侧与对比元素的右侧对齐
中对中:拖拽元素的水平中心与对比元素的水平中心对齐
左对左:拖拽元素的左侧与对比元素的左侧对齐
左对右:拖拽元素的左侧与对比元素的右侧对齐

在这里插入图片描述
当前拖拽元素除外,保存其他所有元素的位置信息和判断条件用于生成辅助线。

在拖拽过程中,监听 drag 事件,并将拖拽元素的位置与之前保存的数据进行比较。如果满足生成辅助线的条件,则显示相应的辅助线。

解析:函数中使用了一个 lines 对象来保存辅助线的位置信息。遍历画布上的所有元素(除了当前拖拽元素),并为每个元素计算并保存辅助线的位置。

对于垂直方向(y 轴),生成了五种辅助线的位置信息:

顶对顶:拖拽元素的顶部与对比元素的顶部对齐
顶对底:拖拽元素的顶部与对比元素的底部对齐
中:拖拽元素的中部与对比元素的中部对齐
底对顶:拖拽元素的底部与对比元素的顶部对齐
底对底:拖拽元素的底部与对比元素的底部对齐
对于每种对齐方式,lines 对象保存了两个位置信息:

showTop:用于显示辅助线的位置
top:用于计算对齐位置的参考位置
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

我这个项目是组件化拖拽需求。全部都是组件形式进行拖拽的。
在这里插入图片描述

代码 如下。主要展示辅助线和吸附功能。如果在编辑的时候需要加方格遮罩,就需要加样式

.es-container {
  border: 1px solid #ccc;
  background:
        -webkit-linear-gradient(top, transparent calc(var(--es-grid-height) - 1px), #ccc var(--es-grid-height)),
        -webkit-linear-gradient(left, transparent calc(var(--es-grid-width) - 1px), #ccc var(--es-grid-width))
        ;
    background-size: var(--es-grid-width) var(--es-grid-height);
  width: 800px;
  height: 600px;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);

我这里去除了,因为不好看。
具体代码如下:

 <template>
	 <div  :id="esContainer ? 'es-container' : 'interact-container'"  >
	   <div
	     v-for="(item, i) in componentsList"
	      :id="item.id"
	      :key="item.id"
	      class="interact"
	      :class="{ 'is-edit': editState === 'edit' && activeEle.id === item.id }"
	      :data-x="item.x"
	      :data-y="item.y"
	      :data-w="item.w"
	      :data-h="item.h"
	      :style="{
	        transform: `translate(${item.x}px, ${item.y}px)`,
	        width: `${item.w}px`,
	        height: `${item.h}px`,
	        zIndex: item.zIndex,
	        background: item.color
	      }"
	      @mousedown="editState === 'edit' && handleMouseDown(item, i)"
	    >
	      <component
	        :is="item.is"
	        ref="component"
	        :echartsId="item.id"
	        :style="{ background: ( editState === 'edit' ? 'rgba(255,255,255,0.05)' : item.color), overflow: 'hidden'}"
	        :dataw="item.w"
	        :datah="item.h"
	        :dataInfo.sync="item.dataInfo"
	        :dataTypeList.sync="item.dataTypeList"
	        :dataTime="dataTime"
	      ></component>
	    </div>
		 <div
		     v-show="markLine.left"
		      class="es-markline-left"
		      :style="{ left: markLine.left + 'px' }"
		    ></div>
	    <div
	      v-show="markLine.top"
	      class="es-markline-top"
	      :style="{ top: markLine.top + 'px' }"
	    ></div>
		<slot></slot>
	</div>
 </template>
 <script>
	import interact from 'interactjs'
	import { mapGetters } from 'vuex'
	
	export default {
	  name: 'interact',
	  props: {
	    modules: {
	      type: Array,
	      required: true,
	      default: () => []
	    }
	  },
	  data () {
	    return {
	      dataTime: 0,
	      inverterMonTimer: null, // 设置刷新时间 2 分钟一次
	      activeEle: {},
	      componentsList: [],
	      esContainer: false, // 背景网格编辑
	      markLine: {
	        left: null,
	        top: null
	      },
	      lines: {
	        x: [],
	        y: []
	      },
	      currentIndex: -1 // 选中的元素下标
	    }
	  },
	  computed: {
	    ...mapGetters(['workFaceInfo', 'editState', 'historyState', 'currentPage'])
	  },
	  watch: {
	    currentPage: {
	      handler () {
	        this.activeEle = {}
	      }
	    },
	    editState: {
	      handler (value) {
	        if (value === 'save') {
	          this.esContainer = false
	          this.savePageJson()
	        } else if (value === 'exit' || value === '') {
	          this.esContainer = false
	          this.componentsList = this.modules.map((v, i) => {
	            return { ...v, id: i }
	          })
	          this.init()
	        } else {
	          this.activeEle = {}
	          this.init()
	        }
	      }
	    },
	    historyState: {
	      deep: true,
	      handler (value) {
	        if (value) {
	          this.savePageJson(value)
	        }
	      }
	    },
	    modules: {
	      deep: true,
	      handler (value) {
	        if (value?.length) {
	          this.componentsList = value.map((v, i) => {
	            return { ...v, id: i }
	          })
	        } else {
	          this.componentsList = []
	        }
	        this.init()
	      }
	    }
	  },
	  mounted () {
	    this.componentsList = this.componentsList.concat(this.modules).map((v, i) => {
	      return { ...v, id: i }
	    })
	    window.$eventBus.$on('dragItem', param => {
	      const id = window.$dayjs().valueOf()
	      this.activeEle = window.$utils.cloneDeep(param)
	      const allZIndex = this.componentsList.map(v => v.zIndex)
	      const max = Math.max(...allZIndex)
	      this.componentsList.push({ ...param, id, zIndex: max > 0 ? max : 1  })
	      this.dragMoveFn()
	      this.init()
	    })
	    window.$eventBus.$on('elementHandler', (type) => {
	      if (type === 'del') {
	        this.deleteComponent()
	      } else {
	        this.edit()
	      }
	    })
	    this.getInverterMonTimer()
	  },
	  beforeDestroy () {
	    if (this.inverterMonTimer) {
	      clearInterval(this.inverterMonTimer)
	      this.inverterMonTimer = null
	    }
	  },
	  methods: {
	    getInverterMonTimer () {
	      // 判断定时刷新是否存在,存在先清除
	      if (this.inverterMonTimer) {
	        clearInterval(this.inverterMonTimer)
	        this.inverterMonTimer = null
	      }
	      // 实现轮询 两分钟
	      this.inverterMonTimer = window.setInterval(() => {
	        this.dataTime = new Date().getTime()
	      }, 120000)
	    },
	    numberFormat: window.$utils.numberFormat,
	    edit () {
	      const index = this.componentsList.findIndex(v => v.id === this.activeEle.id)
	      if (this.componentsList[index]?.isDataInfo) {
	        this.$message.warning('该组件不存在测点配置,请重新选择')
	      } else {
	        this.$refs['component'][index].edit()
	      }
	    },
	    init: window.$utils.debounce(function () {
	      console.log('初始化========', this.componentsList)
	      const zoomItem = interact('.interact')
	      zoomItem.resizable(this.editState === 'edit' && this.resizableFn())
	      zoomItem.draggable(this.editState === 'edit' && this.dragMoveFn())
	    }, 200, false),
	    setPos (data) {
	      let { target, x, y, w, h } = data
  const index = this.componentsList.findIndex(v => v.id === this.activeEle.id)
  if (index >= 0) {
    x = Math.ceil(x)
    y = Math.ceil(y)
    // target.style.transform = `translate(${x}px, ${y}px)`
    target.setAttribute('data-x', x)
    this.componentsList[index].x = x

    target.setAttribute('data-y', y)
    this.componentsList[index].y = y

    if (w && h) {
      w = Math.ceil(w)
      h = Math.ceil(h)
      // target.style.width = w + 'px'
      target.setAttribute('data-w', w)
      this.componentsList[index].w = w

      // target.style.height = h + 'px'
      target.setAttribute('data-h', h)
      this.componentsList[index].h = h
    }
  }
},
handleMouseDown (item, i) {
  window.$eventBus.$emit('activeElement', item)
  this.activeEle = window.$utils.cloneDeep(item)
  // 设置辅助线
  this.esContainer = true
  this.currentIndex = i
  let items = item // 选中的组件
  // 获取当前页面的所有对比值
  this.lines = this.calcLines()
  // console.log(this.lines, 'xy数组')
  // 鼠标移动时事件处理
  document.onmousemove = (event) => {
    this.markLine.top = null
    this.markLine.left = null
    for (let i = 0; i < this.lines.y.length; i++) {
      const { top, showTop } = this.lines.y[i]
      if (Math.abs(top - items.y) < 5) {
        this.markLine.top = showTop
        // console.log(this.markLine.top, 'this.markLine.top')
        break
      }
    }
    for (let i = 0; i < this.lines.x.length; i++) {
      const { left, showLeft } = this.lines.x[i]
      if (Math.abs(left - items.x) < 5) {
        this.markLine.left = showLeft
        // console.log(this.markLine.left, 'this.markLine.left')
        break
      }
    }
  }
  // 鼠标抬起时结束
  document.onmouseup = (event) => {
    // 吸附功能
    this.adsorb(event, items, this.markLine.left, this.markLine.top)
    document.onmousemove = document.onmouseup = null
    this.markLine.top = null
    this.markLine.left = null
  }
},
adsorb (event, items, left, top) {
  let DISTANCE = 5 // 距离
  const target = event.target
  // console.log(event, '选中的组件items', items, '上边线top', top, '左侧边线', left)
  if (top && left) { // 同时存在两条边线
    // 完成: 左上对右下, 左上对右上, 左上对左上, 左上对左下
    if (Math.abs(items.y - top) <= DISTANCE && Math.abs(items.x - left) <= DISTANCE) {
      this.setPos({ target, x: left, y: top })
      return
    }
    // 完成: 右下对右上, 右下对左上, 右下对左下, 右下对右下
    else if (Math.abs((items.x + items.w) - left) <= DISTANCE && Math.abs((items.y + items.h) - top) <= DISTANCE) {
      this.setPos({ target, x: (left - (items.x + items.w)) + items.x, y: (top - (items.y + items.h)) + items.y })
      return
    }
    // 完成: 左下对左上, 左下对右上, 左下对左下, 左下对右下
    else if (Math.abs((items.y + items.h) - top) <= DISTANCE && Math.abs(items.x - left) <= DISTANCE) {
      this.setPos({ target, x: left, y: top - items.h })
    }
    // 完成: 右上对左下, 右上对右上, 右上对左下, 右上对做上
    else if (Math.abs((items.x + items.w) - left) <= DISTANCE && Math.abs((items.y) - top <= DISTANCE)) {
      this.setPos({ target, x: (left - (items.x + items.w)) + items.x, y: top })
    } else if (items.x === left && items.y === top) {
      this.setPos({ target, x: left, y: top })
    }
  } else {
    if (top) { // 只有top值变化 则 x 赋值 移动的 items.x, Y轴需要计算
      console.log('选中的组件items', items, '上边线top', top)
      // 上 对 下
      if (Math.abs(items.y - top) <= DISTANCE) {
        this.setPos({ target, x: items.x, y: top })
        return
      }
      // 下边框 对 上边框
      else if (Math.abs((items.y + items.h) - top) <= DISTANCE) {
        this.setPos({ target, x: items.x, y: (top - (items.y + items.h)) + items.y })
        return
      }
    }

    if (left) { // 只有left值变化 Y轴赋值 items.y, x轴需要计算
      console.log('选中的组件items', items, '左侧边线', left)
      // 左对左,左对右
      if (Math.abs(items.x - left) <= DISTANCE) {
        this.setPos({ target, x: left, y: items.y })
        return
      }
      // 右对左,右对右
      else if (Math.abs((items.x + items.w) - left) <= DISTANCE) {
        this.setPos({ target, x: (left - (items.x + items.w)) + items.x, y: items.y })
        return
      }
    }
  }
},

calcLines () {
  const lines = { x: [], y: [] } // w:[], h:[]
  // 当前选中要拖拽元素大小
  const { w, h, x, y } = this.componentsList[this.currentIndex]
  // console.log("当前组件宽", w, "当前组件高", h, "当前组件x轴", x, "当前组件y轴", y)
  // 循环遍历画布所有元素,将除当前拖拽元素外所有其它元素生成辅助线的位置保存,每个元素x和y都会有5种
  this.componentsList.forEach((item, i) => {
    if (this.currentIndex === i) { return }
    // 非当前元素
    const { y: ATop, x: ALeft, w: AWidth, h: AHeight } = item
    // console.log(
    //   '点击时获取非当前组件其他y轴高度', ATop,
    //   '点击时获取非当前组件其他x轴宽', ALeft,
    //   '点击时获取非当前组件其他width', AWidth,
    //   '点击时获取非当前组件其他height',  AHeight
    // )
    lines.x.push({ left: ALeft, showLeft: ALeft })
    lines.x.push({ left: ALeft + parseInt(AWidth), showLeft: ALeft + parseInt(AWidth) })
    lines.x.push({ left: ALeft + parseInt(AWidth) / 2 - parseInt(w) / 2, showLeft: ALeft + parseInt(AWidth) / 2 })
    lines.x.push({ left: ALeft + parseInt(AWidth) - parseInt(w), showLeft: ALeft + parseInt(AWidth) })
    lines.x.push({ left: ALeft - parseInt(w), showLeft: ALeft })

    lines.y.push({ showTop: ATop, top: ATop })
    lines.y.push({ showTop: ATop, top: ATop - parseInt(h) })
    lines.y.push({ showTop: ATop + parseInt(AHeight) / 2, top: ATop + parseInt(AHeight) / 2 - parseInt(h) / 2 })
    lines.y.push({ showTop: ATop + parseInt(AHeight), top: ATop + parseInt(AHeight) })
    lines.y.push({ showTop: ATop + parseInt(AHeight), top: ATop + parseInt(AHeight) - parseInt(h) })
  })
  return lines
},

    // 删除当前元素
    deleteComponent () {
      this.componentsList = this.componentsList.filter(v => v.id !== this.activeEle.id)
      this.activeEle = {}
    },
    // 拖拽方法
    dragMoveFn () {
      const self = this
      return {
        inertia: false,
        modifiers: [
          interact.modifiers.restrictRect({
            restriction: 'parent',
            endOnly: true
          }),
          interact.modifiers.snap({
            targets: [
              interact.snappers.grid({ x: 1, y: 1 })
            ],
            range: Infinity,
            relativePoints: [{ x: 0, y: 0 }]
          })
        ],
        autoScroll: true,
        listeners: {
          move (event) {
            const target = event.target
            const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx
            const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy
            self.setPos({ target, x, y })
          }
        }
      }
    },
    // 缩放方法
    resizableFn () {
      const self = this
      return {
        inertia: false,
        edges: {
          left: true,
          right: true,
          bottom: true,
          top: true
        },
        listeners: {
          move (event) {
            const target = event.target
            const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.deltaRect.left
            const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.deltaRect.top
            const w = (parseFloat(target.getAttribute('data-w')) || 0) + event.deltaRect.width
            const h = (parseFloat(target.getAttribute('data-h')) || 0) + event.deltaRect.height
            self.setPos({ target, x, y, w, h })
          }
        },
        modifiers: [
          interact.modifiers.restrictEdges({
            outer: 'parent'
          }),
          interact.modifiers.restrictSize({
            min: {
              width: 30,
              height: 30
            }
          })
        ]
      }
    },
    savePageJson (data) {
      let value = null
      if (data) {
        this.$store.commit('setHistoryState', '')
        value = JSON.parse(data)
      } else {
        value = window.$utils.cloneDeep(this.componentsList)
      }
      value.forEach(v => {
        delete v.is
      })
      const params = {
        workFaceCode: this.workFaceInfo.workFaceCode,
        userName: '',
        dataType: this.currentPage.pageId,
        value: JSON.stringify(value)
      }
      window.$axiosPost('savePageConfig', params).then(() => {
        this.$message.success('保存成功')
        this.$emit('refresh', true)
      }).catch(() => {
        this.$message.error('保存失败')
      })
    }
}
}
</script>

<style scoped lang="scss">
	#interact-container {
	  position: relative;
	  width: 100%;
	  height: 100%;
	}
	
	.interact {
	  position: absolute;
	  user-select: none;
	  touch-action: none;
	  //transition: background-color 0.3s;
	  box-sizing: border-box;
	  border-radius: 4px;
	  padding: 3px;
	
	  & > div {
	    width: 100%;
	    height: 100%;
	    // background: #152536;
	    background-color: rgba(255, 255, 255, 0.05);
	    border-radius: 2px;
	    // position: absolute;
	  }
	
	  .shade-box {
	    width: auto;
	    height: 20px;
	    background: rgba(47, 62, 79, 0.95);
	    color: yellow;
	    position: absolute;
	    left: 50%;
	    top: -20px;
	    transform: translateX(-50%);
	    font-size: 12px;
	    padding: 5px;
	    display: flex;
	    align-items: center;
	
	    span + span {
	      margin-left: 3px;
	    }
	  }
	
	  &.is-edit {
	    box-shadow: 0 2px 12px 0 RGBA(120, 206, 233, 0.1);
	
	    &:after {
	      content: "";
	      width: 100%;
	      height: 100%;
	      border-radius: 5px;
	      position: absolute;
	      left: 0;
	      top: 0;
	      background: linear-gradient(#78cee9, #78cee9) left top,
	      linear-gradient(#78cee9, #78cee9) left top,
	      linear-gradient(#78cee9, #78cee9) right top,
	      linear-gradient(#78cee9, #78cee9) right top,
	      linear-gradient(#78cee9, #78cee9) right bottom,
	      linear-gradient(#78cee9, #78cee9) right bottom,
	      linear-gradient(#78cee9, #78cee9) left bottom,
	      linear-gradient(#78cee9, #78cee9) left bottom;
	      background-size: 5px 15px, 15px 5px;
	      background-repeat: no-repeat;
	      z-index: 99;
	    }
	
	    .handler-box {
	      width: auto;
	      height: 30px;
	      position: absolute;
	      top: -25px;
	      right: 0;
	      z-index: 100;
	      //display: flex;
	      align-items: center;
	      justify-content: space-around;
	      border: dashed #1a395b;
	      border-width: 2px 2px 0 2px;
	      padding: 3px 5px;
	      display: none;
	
	      i {
	        font-size: 16px;
	        cursor: pointer;
	        margin: 0 5px;
	      }
	    }
	
	    &:hover .handler-box {
	      display: flex;
	    }
	  }
	}
	
	.ele-config {
	  background: #0a2744;
	  display: flex;
	  align-items: center;
	  justify-content: center;
	}
	
	// 辅助线样式
	#es-container {
	  position: relative;
	  width: 100%;
	  height: 100%;
	  left: 50%;
	  top: 50%;
	  transform: translate(-50%, -50%);
	
	  [class^="es-markline"] {
	    position: absolute;
	    z-index: 9999;
	    background-color: #3a7afe;
	  }
	
	  .es-markline-left {
	    height: 100%;
	    width: 1px;
	    top: 0;
	  }
	
	  .es-markline-top {
	    width: 100%;
	    height: 1px;
	    left: 0;
	  }
	}
</style>

布局线的代码在这里,可以直接搬去用,记得拿样式

    <div
	  v-show="markLine.left"
	  class="es-markline-left"
	  :style="{ left: markLine.left + 'px' }"
	></div>
    <div
      v-show="markLine.top"
      class="es-markline-top"
      :style="{ top: markLine.top + 'px' }"
    ></div>

这里是样式。

    // 辅助线样式
		#es-container {
		  position: relative;
		  width: 100%;
		  height: 100%;
		  left: 50%;
		  top: 50%;
		  transform: translate(-50%, -50%);
		
		  [class^="es-markline"] {
		    position: absolute;
		    z-index: 9999;
		    background-color: #3a7afe;
		  }
		
		  .es-markline-left {
		    height: 100%;
		    width: 1px;
		    top: 0;
		  }
		
		  .es-markline-top {
		    width: 100%;
		    height: 1px;
		    left: 0;
		  }
		}

辅助线和吸附的事件处理代码在这里

    handleMouseDown (item, i) {
      window.$eventBus.$emit('activeElement', item)
      this.activeEle = window.$utils.cloneDeep(item)
      // 设置辅助线
      this.esContainer = true
      this.currentIndex = i
      let items = item // 选中的组件
      // 获取当前页面的所有对比值
      this.lines = this.calcLines()
      // console.log(this.lines, 'xy数组')
      // 鼠标移动时事件处理
      document.onmousemove = (event) => {
        this.markLine.top = null
        this.markLine.left = null
        for (let i = 0; i < this.lines.y.length; i++) {
          const { top, showTop } = this.lines.y[i]
          if (Math.abs(top - items.y) < 5) {
            this.markLine.top = showTop
            // console.log(this.markLine.top, 'this.markLine.top')
            break
          }
        }
        for (let i = 0; i < this.lines.x.length; i++) {
          const { left, showLeft } = this.lines.x[i]
          if (Math.abs(left - items.x) < 5) {
            this.markLine.left = showLeft
            // console.log(this.markLine.left, 'this.markLine.left')
            break
          }
        }
      }
      // 鼠标抬起时结束
      document.onmouseup = (event) => {
        // 吸附功能
        this.adsorb(event, items, this.markLine.left, this.markLine.top)
        document.onmousemove = document.onmouseup = null
        this.markLine.top = null
        this.markLine.left = null
      }
    },
    adsorb (event, items, left, top) {
      let DISTANCE = 5 // 距离
      const target = event.target
      // console.log(event, '选中的组件items', items, '上边线top', top, '左侧边线', left)
      if (top && left) { // 同时存在两条边线
        // 完成: 左上对右下, 左上对右上, 左上对左上, 左上对左下
        if (Math.abs(items.y - top) <= DISTANCE && Math.abs(items.x - left) <= DISTANCE) {
          this.setPos({ target, x: left, y: top })
          return
        }
        // 完成: 右下对右上, 右下对左上, 右下对左下, 右下对右下
        else if (Math.abs((items.x + items.w) - left) <= DISTANCE && Math.abs((items.y + items.h) - top) <= DISTANCE) {
          this.setPos({ target, x: (left - (items.x + items.w)) + items.x, y: (top - (items.y + items.h)) + items.y })
          return
        }
        // 完成: 左下对左上, 左下对右上, 左下对左下, 左下对右下
        else if (Math.abs((items.y + items.h) - top) <= DISTANCE && Math.abs(items.x - left) <= DISTANCE) {
          this.setPos({ target, x: left, y: top - items.h })
        }
        // 完成: 右上对左下, 右上对右上, 右上对左下, 右上对做上
        else if (Math.abs((items.x + items.w) - left) <= DISTANCE && Math.abs((items.y) - top <= DISTANCE)) {
          this.setPos({ target, x: (left - (items.x + items.w)) + items.x, y: top })
        } else if (items.x === left && items.y === top) {
          this.setPos({ target, x: left, y: top })
        }
      } else {
        if (top) { // 只有top值变化 则 x 赋值 移动的 items.x, Y轴需要计算
          console.log('选中的组件items', items, '上边线top', top)
          // 上 对 下
          if (Math.abs(items.y - top) <= DISTANCE) {
            this.setPos({ target, x: items.x, y: top })
            return
          }
          // 下边框 对 上边框
          else if (Math.abs((items.y + items.h) - top) <= DISTANCE) {
            this.setPos({ target, x: items.x, y: (top - (items.y + items.h)) + items.y })
            return
          }
        }

        if (left) { // 只有left值变化 Y轴赋值 items.y, x轴需要计算
          console.log('选中的组件items', items, '左侧边线', left)
          // 左对左,左对右
          if (Math.abs(items.x - left) <= DISTANCE) {
            this.setPos({ target, x: left, y: items.y })
            return
          }
          // 右对左,右对右
          else if (Math.abs((items.x + items.w) - left) <= DISTANCE) {
            this.setPos({ target, x: (left - (items.x + items.w)) + items.x, y: items.y })
            return
          }
        }
      }
    },

    calcLines () {
      const lines = { x: [], y: [] } // w:[], h:[]
      // 当前选中要拖拽元素大小
      const { w, h, x, y } = this.componentsList[this.currentIndex]
      // console.log("当前组件宽", w, "当前组件高", h, "当前组件x轴", x, "当前组件y轴", y)
      // 循环遍历画布所有元素,将除当前拖拽元素外所有其它元素生成辅助线的位置保存,每个元素x和y都会有5种
      this.componentsList.forEach((item, i) => {
        if (this.currentIndex === i) { return }
        // 非当前元素
        const { y: ATop, x: ALeft, w: AWidth, h: AHeight } = item
        // console.log(
        //   '点击时获取非当前组件其他y轴高度', ATop,
        //   '点击时获取非当前组件其他x轴宽', ALeft,
        //   '点击时获取非当前组件其他width', AWidth,
        //   '点击时获取非当前组件其他height',  AHeight
        // )
        lines.x.push({ left: ALeft, showLeft: ALeft })
        lines.x.push({ left: ALeft + parseInt(AWidth), showLeft: ALeft + parseInt(AWidth) })
        lines.x.push({ left: ALeft + parseInt(AWidth) / 2 - parseInt(w) / 2, showLeft: ALeft + parseInt(AWidth) / 2 })
        lines.x.push({ left: ALeft + parseInt(AWidth) - parseInt(w), showLeft: ALeft + parseInt(AWidth) })
        lines.x.push({ left: ALeft - parseInt(w), showLeft: ALeft })

        lines.y.push({ showTop: ATop, top: ATop })
        lines.y.push({ showTop: ATop, top: ATop - parseInt(h) })
        lines.y.push({ showTop: ATop + parseInt(AHeight) / 2, top: ATop + parseInt(AHeight) / 2 - parseInt(h) / 2 })
        lines.y.push({ showTop: ATop + parseInt(AHeight), top: ATop + parseInt(AHeight) })
        lines.y.push({ showTop: ATop + parseInt(AHeight), top: ATop + parseInt(AHeight) - parseInt(h) })
      })
      return lines
    },

完成

  • 11
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Unity是一款专业的游戏开发引擎,但也可以用来实现窗口拖拽缩放功能。在Unity中,可以使用RectTransform组件来控制界面元素的位置和大小。接下来,我将介绍一种简单的方法来实现窗口的拖拽缩放功能。 首先,在Unity中创建一个UI界面,并添加一个Image组件作为窗口的背景。然后,给窗口的Image组件添加一个拖拽脚本和一个缩放脚本。 拖拽脚本的作用是控制窗口的拖拽功能。在脚本中,我们可以通过监听鼠标按下、鼠标拖动和鼠标放开的事件来实现窗口的拖拽。具体步骤如下: 1. 在脚本中定义一个bool类型的变量,用于判断是否可以进行拖拽操作。 2. 在Update函数中,通过Input.GetMouseButtonDown和Input.GetMouseButtonUp函数判断鼠标是否按下或放开。如果鼠标按下,将上述bool变量设置为true,如果鼠标放开,将bool变量设置为false。 3. 还需要判断鼠标是否在窗口的范围内。可以使用RectTransform组件的rect属性获取窗口的矩形区域,然后使用RectTransformUtility类的ScreenPointToLocalPointInRectangle函数判断鼠标的位置是否在窗口的范围内。 4. 如果鼠标在窗口范围内并且bool变量为true,可以通过RectTransform组件的anchoredPosition属性来设置窗口的位置。 缩放脚本的作用是实现窗口的缩放功能。在脚本中,我们可以通过鼠标滚轮的输入来控制窗口的缩放。具体步骤如下: 1. 在脚本中定义一个float类型的变量,用于控制窗口的缩放比例。 2. 在Update函数中,通过Input.GetAxis函数来获取鼠标滚轮的输入值。 3. 根据鼠标滚轮的输入值来增加或减少窗口的缩放比例。可以使用RectTransform组件的sizeDelta属性来设置窗口的大小。 通过上述步骤,我们可以实现Unity中窗口的拖拽缩放功能。这样用户就可以通过鼠标来拖动窗口并调整窗口的大小,提升用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值