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>