组件 DragSelectBoard
:
<template>
<div v-if="isVisible" id="canvas_board" class="canvas-board" />
</template>
<script>
export default {
name: 'DragSelectBoard',
props: {
visible: {
type: Boolean,
default: false
}
},
data() {
return {
// 画布
canvas: null,
ctx: null,
// 虚线
lineWidth: 2,
lineColor: 'red',
// 移动
moving: false,
// 虚线起点和终点
startPos: { x: 0, y: 0 },
endPos: { x: 0, y: 0 }
};
},
computed: {
isVisible: {
get() {
return this.visible;
},
set(val) {
this.$emit('update:visible', val);
}
}
},
watch: {
isVisible(val) {
this.$nextTick(() => {
if (val) {
// 创建canvas画布
this.createCanvas();
} else {
// 清空画布
this.clearCanvas();
}
});
}
},
methods: {
// 创建画布
createCanvas() {
const container = document.getElementById('canvas_board');
const containerSize = container.getBoundingClientRect();
this.canvas = document.createElement('canvas');
this.canvas.width = containerSize.width;
this.canvas.height = containerSize.height;
container.appendChild(this.canvas);
this.canvas.addEventListener('mousedown', this.canvasMousedown);
this.canvas.addEventListener('mouseup', this.canvasMouseup);
this.ctx = this.canvas.getContext('2d');
this.ctx.globalAlpha = 0.4;
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
this.ctx.fillStyle = '#000000';
},
// 清除画布
clearCanvas() {
this.canvas.removeEventListener('mousedown', this.canvasMousedown);
this.canvas.removeEventListener('mouseup', this.canvasMouseup);
this.canvas = null;
},
// 绘制虚线选框
drawLine() {
if (!this.ctx) {
return;
}
// 清空
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// 绘制背景色
this.ctx.globalAlpha = 0.4;
this.ctx.fillStyle = '#000000';
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
// 绘制虚线
this.ctx.globalAlpha = 1;
this.ctx.beginPath();
this.ctx.strokeStyle = this.lineColor;
this.ctx.setLineDash([10, 6]);
this.ctx.lineWidth = this.lineWidth;
this.ctx.lineJoin = 'round';
this.ctx.moveTo(this.startPos.x, this.startPos.y);
this.ctx.lineTo(this.endPos.x, this.startPos.y);
this.ctx.lineTo(this.endPos.x, this.endPos.y);
this.ctx.lineTo(this.startPos.x, this.endPos.y);
this.ctx.lineTo(this.startPos.x, this.startPos.y);
this.ctx.stroke();
this.ctx.closePath();
},
// 鼠标按下
canvasMousedown(el) {
this.moving = true;
this.startPos = {
x: el.pageX,
y: el.pageY
};
this.canvas.addEventListener('mousemove', this.canvasMousemove);
},
// 鼠标移动
canvasMousemove(el) {
this.endPos = {
x: el.pageX,
y: el.pageY
};
this.$nextTick(() => {
this.drawLine();
});
},
// 鼠标放开
canvasMouseup() {
this.moving = false;
this.canvas.removeEventListener('mousemove', this.canvasMousemove);
// 选择大的作为结束点,小的为起点,用与解决反向选择的BUG
const params = {
start: { x: Math.min(this.startPos.x, this.endPos.x), y: Math.min(this.startPos.y, this.endPos.y) },
end: { x: Math.max(this.startPos.x, this.endPos.x), y: Math.max(this.startPos.y, this.endPos.y) }
};
this.$emit('select', params);
this.resetLine();
},
// 清空虚线
resetLine() {
this.startPos = { x: 0, y: 0 };
this.endPos = { x: 0, y: 0 };
this.$nextTick(() => {
this.drawLine();
});
},
/**
* 判断两个矩形是否有重叠部分
* @param {Object} pos1 - 矩形1
* @param {Object} pos1.start - 矩形1的左上角坐标
* @param {number} pos1.start.x
* @param {number} pos1.start.y
* @param {Object} pos1.end - 矩形1的右下角坐标
* @param {number} pos1.end.x
* @param {number} pos1.end.y
* @returns {Boolean}
*/
overlap(pos1, pos2) {
// x轴
const beginX = Math.max(pos1.start.x, pos2.start.x);
const endX = Math.min(pos1.end.x, pos2.end.x);
const overlapX = endX - beginX > 0;
// y轴
const beginY = Math.max(pos1.start.y, pos2.start.y);
const endY = Math.min(pos1.end.y, pos2.end.y);
const overlapY = endY - beginY > 0;
// 当X,Y同时存在重叠距离的时候,两个矩形有重叠部分
return overlapX && overlapY;
}
}
};
</script>
<style lang="scss" scoped>
.canvas-board {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 99999999;
}
</style>
父组件:parent
template
:
<drag-select-board
ref="dragSelect"
:visible.sync="dragSelectBoardVisible"
@select="$_multipleSelect"
/>
{
data() {
return {
dragSelectBoardVisible: false
};
},
mounted() {
this.addEvent();
},
beforeDestroy() {
this.removeEvent();
},
methods: {
addEvent() {
document.addEventListener('keydown', this.pressShift);
document.addEventListener('keyup', this.upShift);
},
removeEvent() {
document.removeEventListener('keydown', this.pressShift);
document.removeEventListener('keyup', this.upShift);
},
pressShift(el) {
if (el.key === 'Shift') {
this.dragSelectBoardVisible = true;
}
},
upShift(el) {
if (el.key === 'Shift') {
this.dragSelectBoardVisible = false;
}
},
// 虚线多选
$_multipleSelect(area) {
const cardList = this.$refs.card;
if (cardList) {
this.$refs.card.forEach((item) => {
const rect = item.$el.getBoundingClientRect();
const pos = {
start: { x: rect.left, y: rect.top },
end: { x: rect.left + rect.width, y: rect.top + rect.height }
};
if (this.$refs.dragSelect.overlap(pos, area)) {
item.handleChangeSelect(true);
}
});
}
}
}
}