鼠标框选/ctrl+单击选中多个元素拖拽,缩放单个元素

在这里插入图片描述1. 鼠标框选或ctrl+单击选中多个元素进行拖拽
2. 基于vue-drag-resize实现单个元素的大小缩放

(做的过程中比较坑的是vue-drag-resize)的拖动效果会和自己写的多个拖动事件之间有影响,所以就直接将vue-drag-resize的isDraggable设置为false,直接自己写拖动效果了)

<template>
  <div class="body">
    <div class="left">
      <div>
        上移:
        <el-input-number
          v-model="toTop"
          controls-position="right"
          @change="val => handleChange(val, 'top')" 
          :min="-1080"
          :max="1080">
        </el-input-number>
      </div>
      <div>
        下移:
        <el-input-number
          v-model="toBottom"
          controls-position="right"
          @change="val => handleChange(val, 'bottom')" 
          :min="-1080"
          :max="1080">
        </el-input-number>
      </div>
       <div>
        左移:
        <el-input-number
          v-model="toLeft"
          controls-position="right"
         @change="val => handleChange(val, 'left')" 
          :min="-1800"
          :max="1800">
        </el-input-number>
      </div>
       <div>
        右移:
        <el-input-number
          v-model="toRight"
          controls-position="right"
          @change="val => handleChange(val, 'right')" 
          :min="-1800"
          :max="1800">
        </el-input-number>
      </div>
    </div>
    <div class="main-container"
      @mousedown="handleMouseDown"
      @mousemove="handleMouseMove"
      @mouseup="handleMouseUp"
      ref="selectNodeExeCLC">
      <vue-drag-resize
        :dataChartId="item.id"
        v-for="item in datalist" :key="item.id"
        :isDraggable="false"
        :isResizable="item.dragging!==false && (!currentSelectChartIdArr.length || currentSelectChartIdArr.includes(item.id))"
        :w="item.w"
        :h="item.h"
        :x="item.x"
        :y="item.y"
        :parentLimitation="true"
        @mousedown.native="e => onChartDragStart(e, item)"
        @mousemove.native="e => onChartDragging(e, item)"
        @mouseup.native="e=> onChartDragStop(e, item)"
        @resizestop="data => onChartResizestop(data, item)"
        >
        {{currentSelectChartIdArr}}
        {{item.id}}
        {{item.dragging}}
      </vue-drag-resize>
    </div>
    <div class="right"></div>
    <SelectArea :startPoint="startPoint" :endPoint="endPoint"></SelectArea>
  </div>
</template>

<script>
import $ from 'jquery'
import vueDragResize from 'vue-drag-resize'
import SelectArea from './SelectArea.vue'
export default {
  components: {
    vueDragResize,
    SelectArea
  },
  data() {
    return {
      isPressCtrl: '',
      toTop: 0,
      toBottom: 0,
      toLeft: 0,
      toRight: 0,
      startPoint: {x: 0, y: 0},
      endPoint: {x: 0, y: 0},
      mouseKey: false,
      mouseComplete: false,
      currentSelectChartIdArr: [],
      currentChartDataArr: [],
      oldCurrentChartDataArr: [],
      selectedChartsDOM: [],
      datalist: [
        {
          id: '0',
          w: 100,
          h: 200,
          x: 0,
          y: 0,
          color:'rgba(10,33,190,.1)'
        },
        {
          id: '1',
          w: 200,
          h: 300,
          x: 200,
          y: 50,
          color:'rgba(10,83,190,.1)'
        },
        {
          id: '2',
          w: 200,
          h: 300,
          x: 600,
          y: 50,
          color:'rgba(10,83,190,.1)'
        }
      ],
      dragFlag: false,
      startX: 0,
      startY: 0,
      clickStartTime: 0,
      clickEndTime: 0,
      containerClickStartTime: 0,
      containerClickEndTime: 0,
      isSingleFlag: true
    }
  },
  watch: {
    currentSelectChartIdArr: {
      handler() {
        this.toTop = 0
        this.toBottom = 0
        this.toLeft = 0
        this.toRight = 0
      },
      deep: true
    }
  },
  mounted() {
    this.setup();
  },
  beforeDestroy() {
    window.removeEventListener('mouseup', this.handleMouseUp);
    window.removeEventListener('keydown', this.handleKeydown);
    window.removeEventListener('keyup', this.handleKeyup);
  },
  methods: {
    handleChange(value, type) {
      this.getOutermost()
      if(type === 'left') {
        this.moveChart(0-value, 0)
      } else if(type === 'right') {
        this.moveChart(value, 0)
      } else if(type === 'top') {
        this.moveChart(0, 0-value)
      } else if(type === 'bottom') {
        this.moveChart(0, value)
      }
    },
    setup() {
      this.mouseKey = false; // 是否监听鼠标移动(移出编辑区范围,不再监听鼠标移动事件)
      this.mouseComplete = false; // 鼠标移动事件是否完成(鼠标按下到抬起的流程)
      window.addEventListener('mouseup', this.handleMouseUp);
      window.addEventListener('keydown', this.handleKeydown)
      window.addEventListener('keyup', this.handleKeyup)
    },
    handleKeydown(e) {
      console.log(e,e.keyCode)
      if(e.keyCode == 17) {
        this.isPressCtrl = true
      }
    },
    handleKeyup(e){
      this.isPressCtrl = false
    },
    // 画选中框 - 鼠标按下
    handleMouseDown(e) {
      this.containerClickStartTime = +new Date()
      this.startPoint.x = e.clientX;
      this.startPoint.y = e.clientY;
      this.mouseKey = true;
      this.mouseComplete = false;
    },
    // 画选中框 - 鼠标按下并移动
    handleMouseMove(e) {
      if (this.mouseKey && !this.mouseComplete) {
        this.endPoint.x = e.clientX
        this.endPoint.y = e.clientY
        this.getSelectCharts()
      }
    },
     // 根据选中的内容,处理数据和选中图表的样式
    getSelectCharts() {
      const selectAreaBox = document.getElementById('selectAreaBox')
      let {left, right, bottom, top} = selectAreaBox.getBoundingClientRect()

      let allCharts = document.querySelectorAll('.vdr')
      let selectedChartsDOM = []
      let currentSelectChartIdArr = []
      let currentChartDataArr = []
      allCharts.forEach(item => {
        item.classList.remove('active')
        item.classList.remove('inactive')
        item.classList.add('inactive')
        let child = item.getBoundingClientRect()
        if (child.left > left && child.top > top && child.bottom < bottom && child.right < right) {
          item.classList.remove('inactive')
          item.classList.add('active')
          item.style.backgroundColor='#00aaff'
          selectedChartsDOM.push(item);

          let chartId = item.getAttribute('dataChartId')
          currentSelectChartIdArr.push(chartId)
          let chartData = this.datalist.find(item => item.id === chartId)
          if(chartData) {
            currentChartDataArr.push(chartData)
          }
        } else {
          item.style.backgroundColor='#fff'
        }
      })
      this.selectedChartsDOM = selectedChartsDOM
      this.currentSelectChartIdArr = currentSelectChartIdArr
      // 在这里把所有数据的dragging重置为false,避免vueDragResize和自己写的拖动事件发生冲突
      currentChartDataArr.forEach((item, index) => {
        item.dragging = false
      })
      this.currentChartDataArr = currentChartDataArr
      this.oldCurrentChartDataArr = JSON.parse(JSON.stringify(currentChartDataArr))
      if(this.currentChartDataArr.length === 0) {
        this.isSingleFlag = true
      } else {
        this.isSingleFlag = false
      }
    },
    // 画选中框 -- 鼠标抬起, 如果是点击主区域除图表外的其他地方,取消所有图表的选中状态
    handleMouseUp(e){
      this.mouseKey = false;
      this.mouseComplete = true;
      this.startPoint.x = 0;
      this.startPoint.y = 0;
      this.endPoint.x = 0;
      this.endPoint.y = 0;
      this.containerClickEndTime = +new Date()
      if(this.containerClickEndTime - this.containerClickStartTime <= 300) {
        this.resetSelectMuti()
        this.isSingleFlag = true
      }
    },
    // 拖拽图表 -- 鼠标按下
    onChartDragStart(e, data) {
      this.clickStartTime = +new Date()
      if(this.isPressCtrl) {
        return false
      }
      if(this.currentSelectChartIdArr.length === 0 || this.isSingleFlag) {
        this.resetSelectMuti(e, data)
      }
      
      this.dragFlag = true
      this.startX = e.clientX
      this.startY = e.clientY

      // 找出最左,最右,最上,最下的元素
      this.getOutermost()
    },
    getOutermost() {
      let leftArr = [], rightArr = [], topArr = [], bottomArr = []
      this.oldCurrentChartDataArr.forEach(item => {
        leftArr.push(item.x)
        topArr.push(item.y)
        rightArr.push(item.x + item.w)
        bottomArr.push(item.y + item.h)
      })
      this.leftMax = Math.min(...leftArr)
      this.rightMax = Math.max(...rightArr)
      this.topMax = Math.min(...topArr)
      this.bottomMax = Math.max(...bottomArr)
    },
    moveChart(moveX, moveY) {
      const {width, height} = document.querySelector('.main-container').getBoundingClientRect()
      let {leftMax, rightMax, topMax, bottomMax} = this
      if(this.currentSelectChartIdArr.length) {
        this.currentChartDataArr.forEach((item, index) => {
          if(moveX < 0 && leftMax + moveX >= 0 || moveX > 0 && rightMax + moveX <= width) {
            item.x = this.oldCurrentChartDataArr[index].x + moveX
          }
          if(moveY < 0 && topMax + moveY >= 0 || moveY > 0 && bottomMax + moveY <= height) {
            item.y = this.oldCurrentChartDataArr[index].y + moveY
          }
        })
      }
    },
    // 多选时拖拽图表 -- 鼠标按住拖拽
    onChartDragging(e) {
      if(!this.dragFlag) {
        return false
      }
      if(this.isPressCtrl) {
        return false
      }
      let moveX = e.clientX - this.startX
      let moveY = e.clientY - this.startY
      this.moveChart(moveX, moveY)
    },
    // 多选时拖拽图表 -- 拖拽结束, 如果是单击了某个图表,取消其他图表的选中状态
    onChartDragStop(e, data) {
      this.clickEndTime = +new Date()
      this.oldCurrentChartDataArr = JSON.parse(JSON.stringify(this.currentChartDataArr))
      this.dragFlag = false
      this.clickEndTime = +new Date()
      if(this.clickEndTime - this.clickStartTime <=300) {
        // ctrl + 单击 多选
        if(this.isPressCtrl) {
          data.dragging = false
          let vdrBox = $(e.target).parents(".vdr")[0]
          
          let chartId = vdrBox.getAttribute('dataChartId')
          let index = this.currentSelectChartIdArr.indexOf(chartId)
          if(index > -1) {
            this.currentSelectChartIdArr.splice(index, 1)
            this.currentChartDataArr.splice(index, 1)
            this.selectedChartsDOM.splice(index, 1)
            vdrBox.style.backgroundColor = '#fff'
          } else {
            this.currentSelectChartIdArr.push(chartId)
            this.currentChartDataArr.push(data)
            this.selectedChartsDOM.push(vdrBox)
            vdrBox.style.backgroundColor = '#00aaff'
          }
          this.oldCurrentChartDataArr = JSON.parse(JSON.stringify(this.currentChartDataArr))
          this.isSingleFlag = this.currentSelectChartIdArr.length === 0
        } else {
          this.resetSelectMuti(e, data)
          this.isSingleFlag = true
        }
      }
    },
    // 重置图表的选中状态
    resetSelectMuti(e, data) {
      this.currentSelectChartIdArr = []
      this.datalist.forEach(item => {
        item.dragging = false
      })
      this.selectedChartsDOM.forEach(item => {
        item.style.backgroundColor = '#fff'
      })
      this.currentChartDataArr = []
      this.selectedChartsDOM = []
      this.oldCurrentChartDataArr = []
      if(data) {
        data.dragging = true
        let vdrBox = $(e.target).parents(".vdr")[0]
        let chartId = vdrBox.getAttribute('dataChartId')
        this.currentSelectChartIdArr.push(chartId)
        this.currentChartDataArr.push(data)
        this.selectedChartsDOM.push(vdrBox)
        this.oldCurrentChartDataArr = JSON.parse(JSON.stringify(this.currentChartDataArr))
      }
    }
  }
}
</script>

<style lang="less">
* {
  margin: 0;
  padding: 0;
}
.body {
  width: 100%;
  height: 100vh;
  position: relative;
  >div {
    height: 100%;
    &.left {
      position: absolute;
      width: 300px;
      left: 0;
      top: 0;
      background: rgb(228, 125, 189);
    }
    
    &.main-container {
      margin: 0 100px 0 300px;
      position: relative;
      background: rgb(224, 247, 182);
      >div{
        background: #fff;
      }
    }
    
    &.right {
      position: absolute;
      width: 100px;
      right: 0;
      top: 0;
      background: rgb(228, 125, 189);
    }
  }
}
</style>

组件selectArea

<template>
  <div
    id="selectAreaBox"
    class="select-area"
    :style="[
      { width: size.width + 'px' },
      { height: size.height + 'px' },
      { top: Point.y + 'px' },
      { left: Point.x + 'px' },
    ]"
  >
  </div>
</template>

<script>
export default {
  name: 'SelectArea',
  props: {
    startPoint: {
      type: Object,
      required: true,
    },
    endPoint: {
      type: Object,
      required: true,
    },
  },
  computed:{
    Point() {
      let x = this.endPoint.x === 0 ? this.startPoint.x : Math.min(this.startPoint.x, this.endPoint.x);
      let y =  this.endPoint.y === 0  ? this.startPoint.y : Math.min(this.startPoint.y, this.endPoint.y);
      return {
        x,
        y,
      };
    },
    size(){
      let width = this.endPoint.x === 0 ? 0 : Math.abs(this.startPoint.x - this.endPoint.x);
      let height = this.endPoint.y === 0 ? 0 : Math.abs(this.startPoint.y - this.endPoint.y);
      return {
        width,
        height,
      };
    }
  },
}
</script>
<style lang="less">
.select-area {
  position: fixed;
  background-color: rgba(255, 192, 203, 0.1);
  border: 1px solid red;
  z-index: 9;
}
</style>
  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值