<template>
<div class="edit-anchor-zone">
<div class="edit-toolbar">
<span class="toolbar-icon-wrap" :class="{'icon_active': isAnchorActive}">
<el-tooltip class="item" effect="light" content="框選錨點" placement="top">
<el-button><i class="iconfont icon-OCR-circleselect toolbar-icon" @click="drawAnchor"></i></el-button>
</el-tooltip>
</span>
<span class="toolbar-icon-wrap" :class="{'icon_active': isTableActive}" v-if="!setAnchorFlag">
<el-tooltip class="item" effect="light" content="框選列表" placement="top">
<el-button><i class="iconfont icon-OCR-table toolbar-icon" @click="drawTable"></i></el-button>
</el-tooltip>
</span>
<span class="toolbar-icon-wrap" :class="{'icon_active': isdrawActive}">
<el-tooltip class="item" effect="light" content="拖拽畫布" placement="top">
<el-button><i class="iconfont icon-OCR-drafting toolbar-icon" @click="dragDraw"></i></el-button>
</el-tooltip>
</span>
<span class="toolbar-icon-wrap" :class="{'icon_active': isZoomInActive}">
<el-tooltip class="item" effect="light" content="放大" placement="top">
<el-button><i class="iconfont icon-Zoomin toolbar-icon" @click="zoomIn"></i></el-button>
</el-tooltip>
</span>
<span class="toolbar-icon-wrap" :class="{'icon_active': isZoomOutActive}">
<el-tooltip class="item" effect="light" content="縮小" placement="top">
<el-button><i class="iconfont icon-Zoomout toolbar-icon" @click="zoomOut"></i></el-button>
</el-tooltip>
</span>
<span class="toolbar-icon-wrap" :class="{'icon_active': isAdaptDrawActive}">
<el-tooltip class="item" effect="light" content="適應畫布" placement="top">
<el-button><i class="iconfont icon-OCR-autoadaptation toolbar-icon" @click="adaptDraw"></i></el-button>
</el-tooltip>
</span>
<span class="toolbar-icon-wrap" :class="{'icon_active': isSetOriginActive}">
<el-tooltip class="item" effect="light" content="原圖" placement="top">
<el-button><i class="iconfont icon-OCR-test toolbar-icon" @click="setOriginSize"></i></el-button>
</el-tooltip>
</span>
</div>
<div class="edit-body">
<div v-if="isLoading" class="init-page-tip">
<i class="el-icon-loading"></i>
<p>模板加載中,請稍後</p>
</div>
<div class="canvas-wrapper" ref='canvasWrapper'>
<canvas width="1000" height="700" ref="baseCanvas" class='canvas'></canvas>
</div>
</div>
<el-dialog title="請選擇分割列數" :visible.sync="tableVisible" width="30%">
<div class="select-wrapper">
<div class="select">列數: <el-input-number v-model="tableCol" :min="2" :max="10" size='small'></el-input-number>
</div>
</div>
</el-dialog>
<div style="margin-top:20px;">
<el-button @click="mark">標記</el-button>
<el-button @click="target">目標</el-button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
//toolbar
isAnchorActive: false,
isTableActive: false,
isdrawActive: false,
isZoomInActive: false,
isZoomOutActive: false,
isAdaptDrawActive: false,
isSetOriginActive: false,
// loading img
isLoading: true,
// refs
wrapperTarget: null,
baseTarget: null,
baseInstance: null,
selectId: null,
currentCursor: null,
centerLineIndex: null,
centerLineShow: true,
//base data
drawWidth: 1000,
drawHeight: 700,
initScaleVal: 1,
currentScaleVal: 1,
circlsRadius: 2,
step: 0.05000,
minSelectArea: 15,
imgWidth: 0,
imgHeight: 0,
imgBase64Code: 0,
movePoint: { x: null, y: null, width: null, height: null },
// 選框
setAnchorFlag: true, // true 爲 rectList1 , false 爲 rectList2
rectList1: [],
rectList2: [],
tableList: [],
tableVisible: false,
tableCol: 2,
color1: '#FF7782',
color2: '#1E82FD',
color3: '#4b1efd',
opacity: 0.3,
lineWidth: 2,
}
},
watch: {
rectList1: function (newVal, oldVal) {
console.log('rectList1: ', newVal)
},
rectList2: function (newVal, oldVal) {
console.log('rectList2: ', newVal)
},
tableList: function (newVal, oldVal) {
console.log('tableList: ', newVal)
},
},
methods: {
mark() {
this.setAnchorFlag = true
this.initDrawRect()
this.drawAnchor()
},
target() {
this.setAnchorFlag = false
this.initDrawRect()
this.drawAnchor()
},
initDraw(data) {
this.isLoading = false;
this.imgWidth = 1913;
this.imgHeight = 1122;
this.imgBase64Code = 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1576560618051&di=b89a65984c6a0e5c4c8a3de5aa8f0fcd&imgtype=0&src=http%3A%2F%2Fimage.biaobaiju.com%2Fuploads%2F20180803%2F20%2F1533300579-gnUBlQZPbt.jpg'
this.baseTarget = this.$refs.baseCanvas
this.wrapperTarget = this.$refs.canvasWrapper
this.computeInitData();
this.drawCanvas();
this.drawAnchor();
},
computeInitData() {
let widthRatio = (this.drawWidth / this.imgWidth).toPrecision(5),
heightRatio = (this.drawHeight / this.imgHeight).toPrecision(5);
this.baseInstance = this.baseTarget.getContext('2d')
this.initScaleVal = this.currentScaleVal = widthRatio >= heightRatio ? heightRatio : widthRatio;
if (this.initScaleVal < 0.10) {
this.initScaleVal = 0.10;
this.currentScaleVal = 0.10;
}
if (this.initScaleVal > 4) {
this.initScaleVal = 4;
this.currentScaleVal = 4;
}
if (this.initScaleVal >= 1) {
this.circlsRadius = 2;
} else {
this.circlsRadius = 4;
}
},
zoomIn() {
this.setToolBarUnactive();
this.isZoomInActive = true;
this.dragDrawX = 0;
this.dragDrawY = 0;
if (this.currentScaleVal > 4) {
return;
};
this.adapt(this.currentScaleVal, parseFloat(this.currentScaleVal) + this.step)
this.currentScaleVal = parseFloat(this.currentScaleVal) + this.step;
this.drawCanvas();
},
zoomOut() {
this.setToolBarUnactive();
this.isZoomOutActive = true;
this.dragDrawX = 0;
this.dragDrawY = 0;
if (this.currentScaleVal <= 0.10) {
return;
}
this.adapt(this.currentScaleVal, parseFloat(this.currentScaleVal) - this.step)
this.currentScaleVal -= this.step;
this.drawCanvas();
},
setOriginSize() {
this.setToolBarUnactive();
this.isSetOriginActive = true;
this.adapt(this.currentScaleVal, 1)
this.currentScaleVal = 1;
this.drawCanvas();
},
adaptDraw() {
this.setToolBarUnactive();
this.isAdaptDrawActive = true;
this.adapt(this.currentScaleVal, this.initScaleVal)
this.currentScaleVal = this.initScaleVal;
this.dragDrawX = 0;
this.dragDrawY = 0;
this.drawCanvas();
},
dragDraw() {
this.setToolBarUnactive();
this.isdrawActive = true;
this.baseTarget.style.cursor = 'move'
this.dragCanvas()
},
drawAnchor() {
this.setToolBarUnactive();
this.isAnchorActive = true
this._drawRect()
},
drawTable() {
this.setToolBarUnactive();
this.isTableActive = true
this.tableVisible = true
this._drawTable()
},
setToolBarUnactive() {
this.isAnchorActive = false;
this.isdrawActive = false;
this.isZoomInActive = false;
this.isZoomOutActive = false;
this.isAdaptDrawActive = false;
this.isSetOriginActive = false;
this.isTableActive = false;
this.baseTarget.style.cursor = 'default'
this.clearHandle()
},
adapt(oldScale, newScale) {
const adapt = (val) => val / oldScale * newScale;
if (this.setAnchorFlag) {
this.rectList1.map(item => {
item.x = adapt(item.x)
item.y = adapt(item.y)
item.width = adapt(item.width)
item.height = adapt(item.height)
})
} else {
this.rectList2.map(item => {
item.x = adapt(item.x)
item.y = adapt(item.y)
item.width = adapt(item.width)
item.height = adapt(item.height)
})
this.tableList.map(item => {
item.x = adapt(item.x)
item.y = adapt(item.y)
item.width = adapt(item.width)
item.height = adapt(item.height)
})
}
},
// 畫布全局方法
deepCopy(obj) {
const result = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] instanceof Object || obj[key] instanceof Array) {
result[key] = deepCopy(obj[key]);
} else {
result[key] = obj[key];
}
}
}
return result;
},
setWrapper() {
this.wrapperTarget.style.left = `${this.movePoint.x}px`
this.wrapperTarget.style.top = `${this.movePoint.y}px`
this.wrapperTarget.style.width = `${this.movePoint.width}px`
this.wrapperTarget.style.height = `${this.movePoint.height}px`
},
setCanvas() {
this.baseTarget.width = this.movePoint.width
this.baseTarget.height = this.movePoint.height
},
throttle(fn, time = 10) {
let timer = null
return (...args) => {
if (!timer) {
timer = setTimeout(() => {
fn.apply(null, args)
timer = null
}, time);
}
}
},
clearHandle() {
this.baseTarget.onmousedown = null
this.baseTarget.onmousemove = null
this.baseTarget.onmouseup = null
this.wrapperTarget.onmousedown = null
this.wrapperTarget.onmousemove = null
this.wrapperTarget.onmouseup = null
},
// 畫布操作
drawCanvas(moveX = 0, moveY = 0) {
const [width, height] = [this.imgWidth * this.currentScaleVal, this.imgHeight * this.currentScaleVal]
const img = new Image()
img.onload = () => {
[this.movePoint.x, this.movePoint.y] = this.isdrawActive ?
[this.movePoint.x + moveX, this.movePoint.y + moveY] :
[(this.drawWidth - width) / 2, (this.drawHeight - height) / 2];
[this.movePoint.width, this.movePoint.height] = [width, height]
this.setWrapper()
this.setCanvas()
this.baseTarget.style.backgroundImage = `url(${img.src})`
this.baseTarget.style.backgroundSize = `${width}px ${height}px`
this.initDrawRect()
}
img.src = this.imgBase64Code
},
dragCanvas() {
let [moveIn, mouseInit, mouse, move] = [false, {}, {}, {}]
this.wrapperTarget.onmousedown = (e) => {
mouseInit = { x: e.clientX, y: e.clientY }
moveIn = true
}
this.wrapperTarget.onmousemove = (e) => {
mouse = { x: e.clientX, y: e.clientY }
move = { x: mouse.x - mouseInit.x, y: mouse.y - mouseInit.y }
if (moveIn) {
mouseInit = { x: mouse.x, y: mouse.y }
this.throttle(this.drawCanvas)(move.x, move.y)
}
}
this.wrapperTarget.onmouseup = (e) => {
moveIn = false
}
},
initDrawRect() {
this.baseInstance.clearRect(0, 0, this.movePoint.width, this.movePoint.height);
if (this.setAnchorFlag) {
this.rectList1.map(item => {
this.baseInstance.beginPath()
this.baseInstance.fillStyle = this.color1
this.baseInstance.globalAlpha = this.opacity
this.baseInstance.fillRect(item.x, item.y, item.width, item.height)
this.baseInstance.closePath()
})
} else {
this.rectList2.map(item => {
this.baseInstance.beginPath()
this.baseInstance.fillStyle = this.color2
this.baseInstance.globalAlpha = this.opacity
this.baseInstance.fillRect(item.x, item.y, item.width, item.height)
this.baseInstance.closePath()
})
this.tableList.map(item => {
this.baseInstance.beginPath()
this.baseInstance.fillStyle = this.color3
this.baseInstance.globalAlpha = this.opacity
this.baseInstance.fillRect(item.x, item.y, item.width, item.height)
this.baseInstance.closePath()
})
}
},
// 選框操作
_drawRect() {
let [moveIn, moved, mouseInit, mouse, move, selectList] = [false, false, {}, {}, {}, []]
this.baseTarget.onmousedown = (e) => {
mouseInit = { x: e.offsetX, y: e.offsetY }
this.selectId = (this.setAnchorFlag ? this.rectList1 : this.rectList2).length
moveIn = true
moved = this.getSelectRect(mouseInit)
}
this.baseTarget.onmousemove = (e) => {
mouse = { x: e.offsetX, y: e.offsetY }
move = { x: mouse.x - mouseInit.x, y: mouse.y - mouseInit.y }
// 新建拖拉選框
if (moveIn && !moved) {
return this.drawRectWithColor(this.baseInstance, mouseInit.x, mouseInit.y, move.x, move.y, this.selectId)
}
// 移動編輯操作
mouseInit = { x: mouse.x, y: mouse.y }
this.dragRect(moved, mouse.x, mouse.y, move.x, move.y, this.selectId)
}
this.baseTarget.onmouseup = (e) => {
moveIn = false
moved = false
this.currentCursor = null
this.reShowRect(0, 0, moved, this.selectId, 'revise')
}
},
getSelectRect(mouse) {
let selectList = (this.setAnchorFlag ? this.rectList1 : this.rectList2).filter(item => {
const xFlag = mouse.x > item.x - this.circlsRadius && mouse.x < item.x + item.width + this.circlsRadius
const yFlag = mouse.y > item.y - this.circlsRadius && mouse.y < item.y + item.height + this.circlsRadius
return xFlag && yFlag
})
if (selectList.length) {
this.selectId = selectList[selectList.length - 1] && selectList[selectList.length - 1].id
this.drawRectBorder(this.baseInstance, this.selectId)
return true
}
return false
},
drawRectWithColor(instance, x, y, width, height, id) {
instance.clearRect(0, 0, this.movePoint.width, this.movePoint.height);
this.initDrawRect();
(this.setAnchorFlag ? this.rectList1 : this.rectList2)[id] = { x, y, width, height, id }
},
drawRectBorder(instance, id) {
instance.clearRect(0, 0, this.movePoint.width, this.movePoint.height);
this.initDrawRect();
(this.setAnchorFlag ? this.rectList1 : this.rectList2).map(item => {
if (item.id === id) {
const [startAngle, endAngle, pointList] = [
0,
2 * Math.PI,
[
{ x: item.x + item.width, y: item.y },
{ x: item.x + item.width, y: item.y + item.height },
{ x: item.x, y: item.y + item.height },
{ x: item.x, y: item.y },
]
]
// 畫邊框
instance.beginPath()
instance.moveTo(item.x, item.y)
pointList.map(item => {
instance.lineTo(item.x, item.y)
})
instance.strokeStyle = this.setAnchorFlag ? this.color1 : this.color2
instance.lineWidth = this.lineWidth
instance.globalAlpha = 1
instance.stroke()
instance.closePath()
// 畫四角圓
pointList.map(item => {
instance.beginPath()
instance.arc(item.x, item.y, this.circlsRadius, startAngle, endAngle)
instance.fillStyle = '#fff'
instance.fill()
instance.strokeStyle = this.setAnchorFlag ? this.color1 : this.color2
instance.lineWidth = this.lineWidth
instance.globalAlpha = 1
instance.stroke()
instance.closePath()
})
}
})
},
dragRect(moved, x, y, moveX, moveY, id) {
if (id === null) return;
(this.setAnchorFlag ? this.rectList1 : this.rectList2).map(item => {
if (item.id === id) {
const getPoint = (a, b, step) => ([a - step, a + step, a + b - step, a + b + step])
const xLine = getPoint(item.x, item.width, this.lineWidth),
yLine = getPoint(item.y, item.height, this.lineWidth),
xCircle = getPoint(item.x, item.width, this.circlsRadius),
yCircle = getPoint(item.y, item.height, this.circlsRadius);
// r:right; l:left; t:top; b:bottom;
const move = x > xLine[1] && x < xLine[2] && y > yLine[1] && y < yLine[2],
lLine = x > xLine[0] && x < xLine[1],
rLine = x > xLine[2] && x < xLine[3],
tLine = y > yLine[0] && y < yLine[1],
bLine = y > yLine[2] && y < yLine[3],
ltCircle = x > xCircle[0] && x < xCircle[1] && y > yCircle[0] && y < yCircle[1],
lbCircle = x > xCircle[0] && x < xCircle[1] && y > yCircle[2] && y < yCircle[3],
rtCircle = x > xCircle[2] && x < xCircle[3] && y > yCircle[0] && y < yCircle[1],
rbCircle = x > xCircle[2] && x < xCircle[3] && y > yCircle[2] && y < yCircle[3];
// 使用當前 cursor 狀態
if (this.currentCursor) {
return this.throttle(this.reShowRect)(moveX, moveY, moved, id, this.currentCursor)
}
if (ltCircle) {
this.throttle(this.reShowRect)(moveX, moveY, moved, id, 'ltCircle')
} else if (lbCircle) {
this.throttle(this.reShowRect)(moveX, moveY, moved, id, 'lbCircle')
} else if (rtCircle) {
this.throttle(this.reShowRect)(moveX, moveY, moved, id, 'rtCircle')
} else if (rbCircle) {
this.throttle(this.reShowRect)(moveX, moveY, moved, id, 'rbCircle')
} else if (lLine) {
this.throttle(this.reShowRect)(moveX, moveY, moved, id, 'lLine')
} else if (rLine) {
this.throttle(this.reShowRect)(moveX, moveY, moved, id, 'rLine')
} else if (tLine) {
this.throttle(this.reShowRect)(moveX, moveY, moved, id, 'tLine')
} else if (bLine) {
this.throttle(this.reShowRect)(moveX, moveY, moved, id, 'bLine')
} else if (move) {
this.throttle(this.reShowRect)(moveX, moveY, moved, id, 'move')
} else {
this.baseTarget.style.cursor = 'default'
}
}
})
},
reShowRect(moveX, moveY, moved, id, cursorStr) {
this.baseTarget.style.cursor = cursorStr
this.currentCursor = moved ? cursorStr : null
this.baseInstance.clearRect(0, 0, this.movePoint.width, this.movePoint.height)
switch (cursorStr) {
case 'move':
this.baseTarget.style.cursor = 'move'
moved && (this.setAnchorFlag ? this.rectList1 : this.rectList2).map(item => { if (item.id === id) { item.x += moveX; item.y += moveY } })
break
case 'lLine':
this.baseTarget.style.cursor = 'e-resize'
moved && (this.setAnchorFlag ? this.rectList1 : this.rectList2).map(item => { if (item.id === id) { item.x += moveX; item.width -= moveX } })
break
case 'rLine':
this.baseTarget.style.cursor = 'e-resize'
moved && (this.setAnchorFlag ? this.rectList1 : this.rectList2).map(item => { if (item.id === id) { item.width += moveX } })
break
case 'tLine':
this.baseTarget.style.cursor = 'n-resize'
moved && (this.setAnchorFlag ? this.rectList1 : this.rectList2).map(item => { if (item.id === id) { item.y += moveY; item.height -= moveY } })
break
case 'bLine':
this.baseTarget.style.cursor = 'n-resize'
moved && (this.setAnchorFlag ? this.rectList1 : this.rectList2).map(item => { if (item.id === id) { item.height += moveY } })
break
case 'ltCircle':
this.baseTarget.style.cursor = 'nw-resize'
moved && (this.setAnchorFlag ? this.rectList1 : this.rectList2).map(item => { if (item.id === id) { item.x += moveX; item.y += moveY; item.width -= moveX; item.height -= moveY } })
break
case 'lbCircle':
this.baseTarget.style.cursor = 'ne-resize'
moved && (this.setAnchorFlag ? this.rectList1 : this.rectList2).map(item => { if (item.id === id) { item.x += moveX; item.width -= moveX; item.height += moveY } })
break
case 'rtCircle':
this.baseTarget.style.cursor = 'ne-resize'
moved && (this.setAnchorFlag ? this.rectList1 : this.rectList2).map(item => { if (item.id === id) { item.y += moveY; item.height -= moveY; item.width += moveX; } })
break
case 'rbCircle':
this.baseTarget.style.cursor = 'nw-resize'
moved && (this.setAnchorFlag ? this.rectList1 : this.rectList2).map(item => { if (item.id === id) { item.width += moveX; item.height += moveY } })
break
case 'revise':
if (this.setAnchorFlag) {
this.rectList1 = (this.setAnchorFlag ? this.rectList1 : this.rectList2).filter(item => item);
} else {
this.rectList2 = (this.setAnchorFlag ? this.rectList1 : this.rectList2).filter(item => item);
};
(this.setAnchorFlag ? this.rectList1 : this.rectList2).map((item, index, arr) => {
if (item.id === id) {
if (Math.abs(item.width) < this.minSelectArea || Math.abs(item.height) < this.minSelectArea) {
this.$message.error('框選區過小,請重新框選!');
arr.splice(index, 1)
this.baseTarget.style.cursor = 'default'
}
}
});
(this.setAnchorFlag ? this.rectList1 : this.rectList2).map((item, index, arr) => {
if (item.id === id) {
if (item.width < 0) {
item.x += item.width
item.width = Math.abs(item.width)
}
if (item.height < 0) {
item.y += item.height
item.height = Math.abs(item.height)
}
}
item.id = index
})
break
};
this.drawRectBorder(this.baseInstance, id)
},
// 列表操作
_drawTable() {
let [moveIn, moved, mouseInit, mouse, move, selectList] = [false, false, {}, {}, {}, []]
this.baseTarget.onmousedown = (e) => {
mouseInit = { x: e.offsetX, y: e.offsetY }
this.selectId = this.tableList.length
moveIn = true
moved = this.getSelectTable(mouseInit)
}
this.baseTarget.onmousemove = (e) => {
mouse = { x: e.offsetX, y: e.offsetY }
move = { x: mouse.x - mouseInit.x, y: mouse.y - mouseInit.y }
// 新建拖拉選框
if (moveIn && !moved) {
return this.drawTableWithColor(this.baseInstance, mouseInit.x, mouseInit.y, move.x, move.y, this.selectId)
}
// 移動編輯操作
mouseInit = { x: mouse.x, y: mouse.y }
this.dragTable(moved, mouse.x, mouse.y, move.x, move.y, this.selectId)
}
this.baseTarget.onmouseup = (e) => {
moveIn = false
moved = false
this.currentCursor = null
this.reShowTable(0, 0, moved, this.selectId, 'revise')
}
},
getSelectTable(mouse) {
let selectList = this.tableList.filter(item => {
const xFlag = mouse.x > item.x - this.circlsRadius && mouse.x < item.x + item.width + this.circlsRadius
const yFlag = mouse.y > item.y - this.circlsRadius && mouse.y < item.y + item.height + this.circlsRadius
return xFlag && yFlag
})
if (selectList.length) {
this.selectId = selectList[selectList.length - 1] && selectList[selectList.length - 1].id
this.drawTableBorder(this.baseInstance, this.selectId)
return true
}
return false
},
drawTableWithColor(instance, x, y, width, height, id) {
instance.clearRect(0, 0, this.movePoint.width, this.movePoint.height);
this.initDrawRect();
this.tableList[id] = { x, y, width, height, id }
},
drawTableBorder(instance, id) {
instance.clearRect(0, 0, this.movePoint.width, this.movePoint.height);
this.initDrawRect();
this.tableList.map(item => {
if (item.id === id) {
const [startAngle, endAngle, pointList] = [
0,
2 * Math.PI,
[
{ x: item.x + item.width, y: item.y },
{ x: item.x + item.width, y: item.y + item.height },
{ x: item.x, y: item.y + item.height },
{ x: item.x, y: item.y },
]
]
// 畫邊框
instance.beginPath()
instance.moveTo(item.x, item.y)
pointList.map(item => {
instance.lineTo(item.x, item.y)
})
instance.strokeStyle = this.color3
instance.lineWidth = this.lineWidth
instance.globalAlpha = 1
instance.stroke()
instance.closePath()
// 畫四角圓
pointList.map(item => {
instance.beginPath()
instance.arc(item.x, item.y, this.circlsRadius, startAngle, endAngle)
instance.fillStyle = '#fff'
instance.fill()
instance.strokeStyle = this.color3
instance.lineWidth = this.lineWidth
instance.globalAlpha = 1
instance.stroke()
instance.closePath()
})
// 畫中間線
if (!this.centerLineShow) return
const rate = 1 / this.tableCol
const x = rate
if (!item.centerLineList) {
item.centerLineList = []
for (let i = 1; i < this.tableCol; i++) {
item.centerLineList.push(x * i)
}
}
item.centerLineList.map(rateX => {
instance.beginPath()
instance.moveTo(item.x + item.width * rateX, item.y)
instance.lineTo(item.x + item.width * rateX, item.y + item.height)
instance.strokeStyle = this.color3
instance.lineWidth = this.lineWidth
instance.globalAlpha = 1
instance.stroke()
instance.closePath()
instance.beginPath()
instance.arc(item.x + item.width * rateX, item.y + item.height / 2, this.circlsRadius, startAngle, endAngle)
instance.fillStyle = '#fff'
instance.fill()
instance.strokeStyle = this.color3
instance.lineWidth = this.lineWidth
instance.globalAlpha = 1
instance.stroke()
instance.closePath()
})
}
})
},
dragTable(moved, x, y, moveX, moveY, id) {
if (id === null) return;
this.tableList.map(item => {
if (item.id === id) {
const getPoint = (a, b, step) => ([a - step, a + step, a + b - step, a + b + step])
const xLine = getPoint(item.x, item.width, this.lineWidth),
yLine = getPoint(item.y, item.height, this.lineWidth),
xCircle = getPoint(item.x, item.width, this.circlsRadius),
yCircle = getPoint(item.y, item.height, this.circlsRadius);
// r:right; l:left; t:top; b:bottom;
const move = x > xLine[1] && x < xLine[2] && y > yLine[1] && y < yLine[2],
lLine = x > xLine[0] && x < xLine[1],
rLine = x > xLine[2] && x < xLine[3],
tLine = y > yLine[0] && y < yLine[1],
bLine = y > yLine[2] && y < yLine[3],
ltCircle = x > xCircle[0] && x < xCircle[1] && y > yCircle[0] && y < yCircle[1],
lbCircle = x > xCircle[0] && x < xCircle[1] && y > yCircle[2] && y < yCircle[3],
rtCircle = x > xCircle[2] && x < xCircle[3] && y > yCircle[0] && y < yCircle[1],
rbCircle = x > xCircle[2] && x < xCircle[3] && y > yCircle[2] && y < yCircle[3];
// 中間線
let index = null
const centerLine = item.centerLineList.some((rateX, i) => {
index = i
const xFlag = x > (item.x + item.width * rateX - this.circlsRadius) && x < (item.x + item.width * rateX + this.circlsRadius)
const yFlag = y > (item.y + item.height / 2 - this.circlsRadius) && y < (item.y + item.height / 2 + this.circlsRadius)
return xFlag && yFlag
})
// 使用當前 cursor 狀態
if (this.currentCursor) {
return this.throttle(this.reShowTable)(moveX, moveY, moved, id, this.currentCursor)
}
if (ltCircle) {
this.throttle(this.reShowTable)(moveX, moveY, moved, id, 'ltCircle')
} else if (lbCircle) {
this.throttle(this.reShowTable)(moveX, moveY, moved, id, 'lbCircle')
} else if (rtCircle) {
this.throttle(this.reShowTable)(moveX, moveY, moved, id, 'rtCircle')
} else if (rbCircle) {
this.throttle(this.reShowTable)(moveX, moveY, moved, id, 'rbCircle')
} else if (lLine) {
this.throttle(this.reShowTable)(moveX, moveY, moved, id, 'lLine')
} else if (rLine) {
this.throttle(this.reShowTable)(moveX, moveY, moved, id, 'rLine')
} else if (tLine) {
this.throttle(this.reShowTable)(moveX, moveY, moved, id, 'tLine')
} else if (bLine) {
this.throttle(this.reShowTable)(moveX, moveY, moved, id, 'bLine')
} else if (centerLine) {
this.throttle(this.reShowTable)(moveX, moveY, moved, id, 'centerLine', index)
} else if (move) {
this.throttle(this.reShowTable)(moveX, moveY, moved, id, 'move')
} else {
this.baseTarget.style.cursor = 'default'
}
}
})
},
reShowTable(moveX, moveY, moved, id, cursorStr, index = null) {
this.baseTarget.style.cursor = cursorStr
this.currentCursor = moved ? cursorStr : null
this.baseInstance.clearRect(0, 0, this.movePoint.width, this.movePoint.height)
if (cursorStr === 'ltCircle' || cursorStr === 'lbCircle' || cursorStr === 'rtCircle' || cursorStr === 'rbCircle' || cursorStr === 'lLine' || cursorStr === 'rLine' || cursorStr === 'tLine' || cursorStr === 'bLine') {
this.centerLineShow = !moved
}
switch (cursorStr) {
case 'centerLine':
this.baseTarget.style.cursor = 'e-resize'
this.centerLineIndex = index === null ? this.centerLineIndex : index
moved && this.tableList.map(item => {
const rateX = item.centerLineList[this.centerLineIndex]
if (item.x + item.width * rateX + moveX <= item.x + this.circlsRadius) {
return item.centerLineList[this.centerLineIndex] = (this.circlsRadius + 1) / item.width
}
if (item.x + item.width * rateX + moveX >= item.x + item.width - this.circlsRadius) {
return item.centerLineList[this.centerLineIndex] = (item.width - this.circlsRadius - 1) / item.width
}
if (item.id === id && this.centerLineIndex !== null) {
item.centerLineList[this.centerLineIndex] += moveX / item.width
}
})
break
case 'move':
this.baseTarget.style.cursor = 'move'
moved && this.tableList.map(item => { if (item.id === id) { item.x += moveX; item.y += moveY } })
break
case 'lLine':
this.baseTarget.style.cursor = 'e-resize'
moved && this.tableList.map(item => { if (item.id === id) { item.x += moveX; item.width -= moveX } })
break
case 'rLine':
this.baseTarget.style.cursor = 'e-resize'
moved && this.tableList.map(item => { if (item.id === id) { item.width += moveX } })
break
case 'tLine':
this.baseTarget.style.cursor = 'n-resize'
moved && this.tableList.map(item => { if (item.id === id) { item.y += moveY; item.height -= moveY } })
break
case 'bLine':
this.baseTarget.style.cursor = 'n-resize'
moved && this.tableList.map(item => { if (item.id === id) { item.height += moveY } })
break
case 'ltCircle':
this.baseTarget.style.cursor = 'nw-resize'
moved && this.tableList.map(item => { if (item.id === id) { item.x += moveX; item.y += moveY; item.width -= moveX; item.height -= moveY } })
break
case 'lbCircle':
this.baseTarget.style.cursor = 'ne-resize'
moved && this.tableList.map(item => { if (item.id === id) { item.x += moveX; item.width -= moveX; item.height += moveY } })
break
case 'rtCircle':
this.baseTarget.style.cursor = 'ne-resize'
moved && this.tableList.map(item => { if (item.id === id) { item.y += moveY; item.height -= moveY; item.width += moveX; } })
break
case 'rbCircle':
this.baseTarget.style.cursor = 'nw-resize'
moved && this.tableList.map(item => { if (item.id === id) { item.width += moveX; item.height += moveY } })
break
case 'revise':
this.centerLineShow = true
this.tableList = this.tableList.filter(item => item);
this.tableList.map((item, index, arr) => {
if (item.id === id) {
if (Math.abs(item.width) < this.minSelectArea || Math.abs(item.height) < this.minSelectArea) {
this.$message.error('框選區過小,請重新框選!');
arr.splice(index, 1)
this.baseTarget.style.cursor = 'default'
}
}
});
this.tableList.map((item, index, arr) => {
if (item.id === id) {
if (item.width < 0) {
item.x += item.width
item.width = Math.abs(item.width)
}
if (item.height < 0) {
item.y += item.height
item.height = Math.abs(item.height)
}
}
item.id = index
})
break
};
this.drawTableBorder(this.baseInstance, id)
},
},
mounted() {
this.$nextTick(() => {
this.initDraw()
})
},
}
</script>
<style lang='scss' scoped>
@import "@/assets/scss/variables.scss";
.edit-anchor-zone {
width: 1000px;
.edit-toolbar {
height: 60px;
background-color: #565559;
display: flex;
justify-content: flex-end;
align-items: center;
.toolbar-icon-wrap {
margin-right: 10px;
.item {
background-color: transparent;
border-radius: 0;
padding: 8px;
font-size: 18px;
color: #e5e5e5;
border: 1px solid transparent;
}
&.icon_active {
.item {
color: $tlt-btn-active;
border-color: $tlt-btn-active;
background-color: $nav-left-bg;
}
}
}
}
.edit-body {
height: 720px;
padding: 10px 0;
box-sizing: border-box;
background-color: $nav-left-bg;
position: relative;
overflow: hidden;
.init-page-tip {
position: absolute;
top: 40%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
color: #e5e5e5;
.el-icon-loading {
font-size: 40px;
margin-bottom: 20px;
}
}
.canvas-wrapper {
width: 1000px;
height: 700px;
position: absolute;
top: 0;
left: 0;
.canvas {
position: absolute;
top: 0;
left: 0;
}
}
}
}
</style>
canvas 實現拖拽選框、放大縮小等功能
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.