vue-matchup
Version:
A rollup + vue component development template
225 lines (219 loc) • 7.13 kB
JavaScript
import {
on,
off,
getDomClientRect,
getParentNodes
} from "utils/dom";
export default {
data() {
return {
lineMoveId: null,
lineCheckedId: null,
lineCheckedIds: [],
lineCheckedIdsOld: [],
readyLines: [],
scrollTargets: [],
scrollTop: 0,
scrollLeft: 0,
cacheCanvas: null
}
},
watch: {
lineCheckedId(val, oldVal) {
this.drawAllLines();
if (val !== oldVal) {
if (oldVal) {
this._toggleTableRows(oldVal, 'remove')
this.$emit('unCheckLine', oldVal)
}
if (val) {
this._toggleTableRows(val, 'add')
this.$emit('checkLine', val)
}
this.$nextTick(() => {
this.$refs.input.focus();
})
} else {
this.$emit('unCheckLine', oldVal)
}
},
lineCheckedIds(val) {
this._toggleTableRows(this.lineCheckedIdsOld, 'remove')
this._toggleTableRows(val, 'add')
this.drawAllLines();
this.lineCheckedIdsOld = JSON.parse(JSON.stringify(val))
},
lineMoveId() {
this.drawAllLines();
}
},
computed: {
matchup() {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== "VueMatchup") {
parent = parent.$parent;
parentName = parent.$options.name;
}
return parent;
}
},
methods: {
onScroll() {
this.scrollTargets = getParentNodes(this.matchup.$el.parentNode);
this.scrollTargets.forEach(d => {
on(d, "scroll", this.windowScroll);
})
},
// 画布鼠标移动
move(e) {
e = e || event;
if (!this.lines.length || !this.readyLines.length) return;
this.inLine(e, 'move');
},
leave() {
this.lineMoveId = null;
this.drawAllLines();
},
click(e) {
e = e || event;
if (!this.lines.length || !this.readyLines.length) return;
this.lineCheckedIds = [];
this.inLine(e, 'click');
},
dblclick() {
if (!this.lineMoveId) return;
this.$emit('dblclick', this.lineMoveId);
},
drawAllLines() {
this.ctx.clearRect(0, 0, this.width, this.height);
this.readyLines.forEach(d => {
let lineColor = d.color;
let lineWidth = 1.6;
if (this.lineCheckedId && this.lineCheckedId === d.id || this.lineCheckedIds.find(id => id === d.id)) {
lineColor = '#139bd2';
lineWidth = 3;
}
if (this.lineMoveId && this.lineMoveId === d.id) {
lineColor = 'orange';
lineWidth = 3;
}
this.drawArrow(this.ctx, d.p1.x, d.p1.y, d.p2.x, d.p2.y, lineColor, lineWidth);
})
},
inLine(e, type) {
// 鼠标点击的坐标
const px = e.clientX - getDomClientRect(this.$refs.canvas).left
const py = e.clientY - getDomClientRect(this.$refs.canvas).top
// 逐条线确定是否有点中
const lineOffset = 5; // 可接受(偏移)范围
for (let i = 0; i < this.readyLines.length; i++) {
const line = this.readyLines[i];
const p1 = line.p1; // 直线起点
const p2 = line.p2; // 直线终点
const minX = Math.min(p1.x, p2.x); // 较小的X轴坐标值
const maxX = Math.max(p1.x, p2.x); // 较大的X轴坐标值
const minY = Math.min(p1.y, p2.y); // 较小的Y轴坐标值
const maxY = Math.max(p1.y, p2.y); // 较大的Y轴坐标值
if (p1.y === p2.y) {
// 水平线
if ((px >= minX && px <= maxX) && (py >= minY - lineOffset && py <= maxY + lineOffset)) {
this.setHandleId(line, type);
break;
}
} else if (p1.x === p2.x) {
// 垂直线
if ((py >= minY && py <= maxY) && (px >= minX - lineOffset && px <= maxX + lineOffset)) {
this.setHandleId(line, type);
break;
}
} else {
if ((px >= minX && px <= maxX) && (py >= minY - lineOffset && py <= maxY + lineOffset)) {
var hudu = Math.atan2(p2.y - p1.y, p2.x - p1.x); // 直线的弧度(倾斜度)
// 用三角函数计出直线上的交叉点
var r = (px - p1.x) / Math.cos(hudu); // 直角三角形的斜边(或理解成圆的半径)
var y = Math.sin(hudu) * r; // Y轴坐标
var p = {
x: px,
y: p1.y + y
};
if ((Math.abs(px - p.x) <= lineOffset) && (Math.abs(py - p.y) <= lineOffset)) {
this.setHandleId(line, type);
break;
}
}
}
type === 'move' && (this.lineMoveId = null);
type === 'click' && this.lineCheckedId === line.id && (this.lineCheckedId = null);
}
},
setHandleId(line, type) {
if (type === 'move') {
this.lineMoveId = line.id
}
if (type === 'click') {
this.lineCheckedId = this.lineCheckedId === line.id ? null : line.id;
}
},
deleted() {
if (this.lineCheckedId) {
// 删除线
const index = this.lines.findIndex(d => d.id === this.lineCheckedId);
this.$emit("delete", index);
}
this.lineCheckedId = null;
this.lineMoveId = null;
},
checkLines(ids) {
this.lineCheckedIds = ids;
this.drawAllLines();
},
unCheckedLines(ids) {
if (!ids || !ids.length) return;
for (let i = 0; i < this.lineCheckedIds.length; i++) {
const checkedId = this.lineCheckedIds[i];
if (ids.find(id => id === checkedId)) {
this.lineCheckedIds.splice(i, 1);
i--;
}
}
ids.find(id => id === this.lineCheckedId) && (this.lineCheckedId = null);
this.drawAllLines();
},
clearCheckedLines() {
this.lineCheckedIds = [];
this.lineCheckedId = null;
this.drawAllLines();
},
// 选中线时对应的table列高亮选中
_toggleTableRows(lineIds, type) {
lineIds = Array.isArray(lineIds) ? lineIds : [lineIds]
lineIds.forEach(lineId => {
const ids = lineId.split('--')
document.getElementById(ids[0]).parentElement.classList[type]('line-check')
document.getElementById(ids[1]).parentElement.classList[type]('line-check')
})
},
windowScroll() {
this.scrollTop = 0;
this.scrollLeft = 0;
this.scrollTargets.forEach(d => {
const scrollTop = d === window ? window.pageYOffset : d.scrollTop
const scrollLeft = d === window ? window.pageXOffset : d.scrollLeft
this.scrollTop += scrollTop;
this.scrollLeft += scrollLeft;
})
}
},
mounted() {
this.$nextTick(() => {
this.canvas = this.$refs.canvas;
this.ctx = this.canvas.getContext("2d");
})
},
beforeDestroy() {
this.scrollTargets.forEach(d => {
off(d, "scroll", this.windowScroll);
})
}
}