Last active
September 19, 2022 13:06
-
-
Save tanopwan/b39b3908c36408277c310c4ce42c71f0 to your computer and use it in GitHub Desktop.
Javascript/Typescript Canvas - Draw masking rect on image with undo feature
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<canvas id="canvas" width="460" height="300" /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D | null | |
let savedCanvases: CanvasImageSource[] = [] | |
let startX: number, startY: number, endX: number, endY: number | |
let currentX: number, currentY: number | |
let isDragging: boolean = false | |
url = ''; // url | |
let img = new Image() | |
img.onload = function () { | |
init(img) | |
} | |
img.src = url | |
canvas = document.getElementById('canvas') as HTMLCanvasElement | |
ctx = canvas.getContext('2d') | |
if (ctx) { | |
ctx.fillStyle = '#000000' | |
} | |
ctx?.clearRect(0, 0, canvas.width, canvas.height) | |
// try to fit image to canvas | |
if (img) { | |
var hRatio = canvas.width / img.width | |
var vRatio = canvas.height / img.height | |
var ratio = Math.min(hRatio, vRatio) | |
var centerShift_x = (canvas.width - img.width * ratio) / 2 | |
var centerShift_y = (canvas.height - img.height * ratio) / 2 | |
ctx?.drawImage( | |
img, | |
0, | |
0, | |
img.width, | |
img.height, | |
centerShift_x, | |
centerShift_y, | |
img.width * ratio, | |
img.height * ratio | |
) | |
} | |
// save initial state | |
let savedCanvas = document.createElement('canvas') as HTMLCanvasElement | |
savedCanvas.width = canvas.width | |
savedCanvas.height = canvas.height | |
let savedCtx = savedCanvas.getContext('2d') | |
savedCtx?.drawImage(canvas, 0, 0) | |
savedCanvases = [savedCanvas] | |
canvas.addEventListener( | |
'mousemove', | |
function (e) { | |
process('move', e) | |
}, | |
false | |
) | |
canvas.addEventListener( | |
'mousedown', | |
function (e) { | |
process('down', e) | |
}, | |
false | |
) | |
canvas.addEventListener( | |
'mouseup', | |
function (e) { | |
process('up', e) | |
}, | |
false | |
) | |
canvas.addEventListener( | |
'mouseout', | |
function (e) { | |
process('out', e) | |
}, | |
false | |
) | |
function process(res: string, e: MouseEvent) { | |
let rect = canvas.getBoundingClientRect() | |
currentX = e.clientX - rect.left | |
currentY = e.clientY - rect.top | |
if (res === 'down') { | |
startX = currentX | |
startY = currentY | |
isDragging = true | |
} | |
if (res === 'up' || res === 'out') { | |
if (isDragging) { | |
endX = e.clientX - rect.left | |
endY = e.clientY - rect.top | |
isDragging = false | |
drawAt() | |
// draw final output | |
ctx?.fillRect(startX, startY, endX - startX, endY - startY) | |
// save canvas | |
let savedCanvas = document.createElement('canvas') as HTMLCanvasElement | |
savedCanvas.width = canvas.width | |
savedCanvas.height = canvas.height | |
let savedCtx = savedCanvas.getContext('2d') | |
savedCtx?.drawImage(canvas, 0, 0) | |
savedCanvases.push(savedCanvas) | |
} | |
} | |
if (res === 'move') { | |
if (isDragging) { | |
endX = e.clientX - rect.left | |
endY = e.clientY - rect.top | |
// reset prev image while dragging | |
drawAt() | |
ctx?.fillRect(startX, startY, endX - startX, endY - startY) | |
} | |
} | |
} | |
function drawAt() { | |
ctx?.clearRect(0, 0, canvas.width, canvas.height) | |
ctx?.drawImage(savedCanvases.at(-1)!, 0, 0) | |
} | |
function undo() { | |
savedCanvases.pop() | |
drawAt() | |
} | |
function resetToOriginal() { | |
savedCanvases = [savedCanvases[0]] | |
drawAt() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment