功能2:鼠標拖動矩形變更位置
在鼠標按下時要先判斷該操作是繪製還是拖拽,這裏以鼠標按下時的座標是否在矩形內來判斷,在矩形外則爲繪製(isDrawing=true),在矩形內則爲拖拽(isDragging=true)。若是爲拖拽還要判斷是哪一個矩形,選中的矩形設置isSelected=true。爲了方便區分,爲正在操作的矩形添加黑色邊框。
function mouseDown(e) {
startX = e.offsetX
startY = e.offsetY
rectIndex = rectList.findIndex(item => { // 是否在矩形內,findIndex()查找數組內的對象,找到符合條件的則返回該對象的下標,沒有則返回-1
if (item.startX < item.endX) {
if (item.startY < item.endY) {
return startX > item.startX && startX < item.endX && startY > item.startY && startY < item.endY
} else {
return startX > item.startX && startX < item.endX && startY > item.endY && startY < item.startY
}
} else {
if (item.startY < item.endY) {
return startX > item.endY && startX < item.startY && startY > item.startY && startY < item.endY
} else {
return startX > item.startX && startX < item.endX && startY > item.endY && startY < item.startY
}
}
})
if (rectIndex !== -1) {
currentRect = rectList[rectIndex]
isDragging = true
currentRect.isSelected = true
} else {
isDrawing = true
}
color = colors[randomFromTo(0, 8)] // colors爲存儲顏色的數組,randomFromTo爲一個方法,返回隨機整數
}
function mouseMove(e) {
endX = e.offsetX
endY = e.offsetY
if (isDrawing) {
drawRects()
context.globalAlpha = 0.3
context.beginPath()
context.moveTo(startX, startY)
context.lineTo(endX, startY)
context.lineTo(endX, endY)
context.lineTo(startX, endY)
context.lineTo(startX, startY)
context.fillStyle = color
context.strokeStyle = 'black'
context.fill()
context.stroke()
} else if (isDragging) {
const w = Math.abs(startX - endX)
const h = Math.abs(startY - endY)
if (endX < startX) {
startX -= w
endX -= w
currentRect.startX -= w
currentRect.endX -= w
}
if (endX >= startX) {
startX += w
endX += w
currentRect.startX += w
currentRect.endX += w
}
if (endY < startY) {
startY -= h
endY -= h
currentRect.startY -= h
currentRect.endY -= h
}
if (endY >= startY) {
startY += h
endY += h
currentRect.startY += h
currentRect.endY += h
}
drawRects()
}
}
function mouseUp(e) {
if (isDrawing) {
rectList.unshift(new Rect(startX, startY, endX, endY, color))
isDrawing = false
}
if (isDragging) {
rectList.forEach(item => {
item.isSelected = false
})
isDragging = false
}
}
function drawRects() {
context.clearRect(0, 0, canvas.width, canvas.height)
for (let i = 0; i < rectList.length; i++) {
let rect = rectList[i]
context.globalAlpha = 0.3
context.beginPath()
context.moveTo(rect.startX, rect.startY)
context.lineTo(rect.endX, rect.startY)
context.lineTo(rect.endX, rect.endY)
context.lineTo(rect.startX, rect.endY)
context.lineTo(rect.startX, rect.startY)
context.fillStyle = rect.color
context.fill()
if (rect.isSelected) {
context.strokeStyle = 'black'
context.stroke()
}
}
}
功能3:保存當前畫布的圖像
利用HTMLCanvasElement.toDataURL()將畫布圖像轉換成base64格式,並動態生成節點將圖片展示出來,也可使用file-saver將圖片保存到本地
function save () {
const data = canvas.toDataURL('image/png', 1)
const chileNode =document.createElement('img')
chileNode.src = data
document.getElementById('img-container').appendChild(chileNode)
}
功能4:清空畫布
清空畫布在之前很多代碼中都有使用過,爲了方便把它抽離出來
function clearCanvas() {
context.clearRect(0, 0, canvas.width, canvas.height)
}
但是要完全清空還要把存儲矩形對象的數組也清空掉,如果沒有清空矩形數組的話後續繪製會把之前的矩形都顯示出來
function clearAll () {
rectList = []
clearCanvas()
}
功能5:撤銷與反撤銷
撤銷
用undoArray存儲每一步的操作,把每次繪製後的狀態存儲到數組中,每撤銷一次就把數組末尾的pop出來,恢復到前一個狀態,注意要加上判斷,以免溢出報錯
function mouseDown(e) {
// 省略重複代碼
if (rectIndex !== -1) {
currentRect = rectList[rectIndex]
isDragging = true
currentRect.isSelected = true
undoArray.pop()
const tempRectList = rectList.slice()
const tempCurrentRect = Object.assign({}, currentRect)
tempRectList.splice(rectIndex, 1, tempCurrentRect)
undoArray.push(tempRectList)
} else {
isDrawing = true
}
}
function mouseUp(e) {
if (isDrawing) {
rectList.unshift(new Rect(startX, startY, endX, endY, color))
}
if (isDragging) {
rectList.forEach(item => {
item.isSelected = false
})
}
undoArray.push(rectList.slice()) // 在鼠標鬆開的時候把當前狀態即rectList存進undoArray
isDrawing = false
isDragging = false
}
function undo () {
if (undoArray.length > 0) {
undoArray.pop()
rectList = undoArray[undoArray.length - 1].slice()
} else {
rectList = []
}
drawRects()
}
反撤銷
反撤銷其實和撤銷差不多,這裏用一個redoArray數組來存儲反撤銷的狀態,把撤銷時pop出來的隊形push進redoArray裏面,每執行一次反撤銷也是把數組末尾的pop出來,恢復到前一個狀態,pop出來的對象在存回undoArray裏面,就可以實現連續的撤銷與反撤銷,這裏也要加上判斷,以免溢出報錯
function undo () {
// context.clearRect(0, 0, canvas.width, canvas.height)
if (undoArray.length > 0) {
redoArray.push(undoArray.pop())
rectList = undoArray[undoArray.length - 1].slice()
} else {
rectList = []
}
drawRects()
}
function redo () {
// context.clearRect(0, 0, canvas.width, canvas.height)
if (redoArray.length > 0) {
rectList = redoArray[redoArray.length - 1].slice()
undoArray.push(redoArray.pop())
}
drawRects()
}
以上代碼看起來會迷糊,之後會做優化