See the Pen Canvas手寫Demo by 喇賽人 (@weber87na) on CodePen.
今天沒啥 fu 一直想搞個小畫家之類的, 還有之前在搞啥 FineReport 長官很想要有啥手寫簽名, 無聊就寫看看
本來覺得沒啥, 實際上也是要 try 才曉得一堆雷 XD
手機效果
先說電腦版, 電腦版用到 mousedown mouseup mousemove
事件
我的想法是用 mousedown mouseup
搭配 isDrawing
這個 flag 當作是否正在手寫
如果 mousemove
觸發的話則把它畫出來, 這裡可以直接拿到 offsetX offsetY
這兩個座標
這裡最關鍵就是當 isDrawing 時畫線, 反之讓他的 ctx.beginPath()
還原, 才可以正常做出想要的效果
1 | canvas.addEventListener('mousemove', (e) => { |
最後呼叫 drawLine
就可以畫出來了, 這裡的 drawLine
搞比較久, 換了好幾種作法畫起來都會是一點一點的, 解鎖小畫家筆刷的原理 XD
另外如果每次都用 ctx.beginPath()
就會有怪小怪小的 bug`
1 | function drawLine(ctx, point) { |
手機版則是要依靠 touchstart touchend touchmove
這三個事件, 他們拿座標的方法不太一樣
另外就是要呼叫 ctx.beginPath
的寫法也不太一樣, 實測要下面這樣才正常
1 | canvas.addEventListener( |
最後存檔我直接偷懶用 ChatGPT 來生, 印象中以前搞 openlayers 好像也寫過把很多圖層變成一張圖的
當時折磨得半死還有 CORS 問題, 現在有 AI 來搞這些真的好快阿
1 | btnSave.addEventListener('click', () => { |
Undo/Redo
後來又搞個 Undo/Redo 效果
See the Pen Canvas手寫Demo(可 Undo/Redo) by 喇賽人 (@weber87na) on CodePen.
Undo
主要是依靠 pop
這個函數, 把最後一筆彈出去改變 array 大小
此時 array 的最後一筆即為上一步所繪製的圖
1 | if (imageListUndo.length > 0) { |
Redo
本身不難, 但萬一中間又執行其他動作會發生奇怪的現象
可以觀察小畫家先畫兩筆然後 Undo
之後又接著畫, 就可以得到正確邏輯
為了因應這個操作, 必須要有 canRedo
這個變數來決定可否使用 Redo
的功能
所以當執行 Undo
時將 canRedo
flag 設為 true
1 | if (event.ctrlKey && (event.key === 'z' || event.key === 'Z')) { |
萬一中間有新增筆畫的話需要於 mouseup
加上 canRedo
這個判斷, 需要把 canRedo
回歸為 false
並且 imageListRedo
清空
1 | canvas.addEventListener('mouseup',(e) => { |
最後看到本身的邏輯, 一樣使用 pop
把 imageListRedo
彈出來的最後一筆加入回 imageListUndo
的 array 即可完成
1 | document.addEventListener('keydown', function (event) { |