Skip to content

Instantly share code, notes, and snippets.

@tanopwan
Last active September 19, 2022 13:06
Show Gist options
  • Save tanopwan/b39b3908c36408277c310c4ce42c71f0 to your computer and use it in GitHub Desktop.
Save tanopwan/b39b3908c36408277c310c4ce42c71f0 to your computer and use it in GitHub Desktop.
Javascript/Typescript Canvas - Draw masking rect on image with undo feature
<canvas id="canvas" width="460" height="300" />
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