Created
December 7, 2021 08:15
-
-
Save duongdam/8bf602c7bd11cdc5aa09986ad1670304 to your computer and use it in GitHub Desktop.
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
import { useCallback, useRef, useState } from "react"; | |
export const usePainter = () => { | |
const canvas = useRef<HTMLCanvasElement>(); | |
const [isReady, setIsReady] = useState(false); | |
const [isRegularMode, setIsRegularMode] = useState(true); | |
const [isAutoWidth, setIsAutoWidth] = useState(false); | |
const [isEraser, setIsEraser] = useState(false); | |
const [currentColor, setCurrentColor] = useState("#000000"); | |
const [currentWidth, setCurrentWidth] = useState(50); | |
const autoWidth = useRef(false); | |
const selectedSaturation = useRef(100); | |
const selectedLightness = useRef(50); | |
const selectedColor = useRef("#000000"); | |
const selectedLineWidth = useRef(50); | |
const lastX = useRef(0); | |
const lastY = useRef(0); | |
const hue = useRef(0); | |
const isDrawing = useRef(false); | |
const direction = useRef(true); | |
const isRegularPaintMode = useRef(true); | |
const isEraserMode = useRef(false); | |
const ctx = useRef(canvas?.current?.getContext("2d")); | |
const drawOnCanvas = useCallback((event: any) => { | |
if (!ctx || !ctx.current) { | |
return; | |
} | |
ctx.current.beginPath(); | |
ctx.current.moveTo(lastX.current, lastY.current); | |
ctx.current.lineTo(event.offsetX, event.offsetY); | |
ctx.current.stroke(); | |
[lastX.current, lastY.current] = [event.offsetX, event.offsetY]; | |
}, []); | |
const handleMouseDown = useCallback((e: any) => { | |
isDrawing.current = true; | |
[lastX.current, lastY.current] = [e.offsetX, e.offsetY]; | |
}, []); | |
const dynamicLineWidth = useCallback(() => { | |
if (!ctx || !ctx.current) { | |
return; | |
} | |
if (ctx.current.lineWidth > 90 || ctx.current.lineWidth < 10) { | |
direction.current = !direction.current; | |
} | |
direction.current ? ctx.current.lineWidth++ : ctx.current.lineWidth--; | |
setCurrentWidth(ctx.current.lineWidth); | |
}, []); | |
const drawNormal = useCallback( | |
(e: any) => { | |
if (!isDrawing.current || !ctx.current) return; | |
if (isRegularPaintMode.current || isEraserMode.current) { | |
ctx.current.strokeStyle = selectedColor.current; | |
setCurrentColor(selectedColor.current); | |
autoWidth.current && !isEraserMode.current | |
? dynamicLineWidth() | |
: (ctx.current.lineWidth = selectedLineWidth.current); | |
isEraserMode.current | |
? (ctx.current.globalCompositeOperation = "destination-out") | |
: (ctx.current.globalCompositeOperation = "source-over"); | |
} else { | |
setCurrentColor( | |
`hsl(${hue.current},${selectedSaturation.current}%,${selectedLightness.current}%)`, | |
); | |
ctx.current.strokeStyle = `hsl(${hue.current},${selectedSaturation.current}%,${selectedLightness.current}%)`; | |
ctx.current.globalCompositeOperation = "source-over"; | |
hue.current++; | |
if (hue.current >= 360) hue.current = 0; | |
autoWidth.current | |
? dynamicLineWidth() | |
: (ctx.current.lineWidth = selectedLineWidth.current); | |
} | |
drawOnCanvas(e); | |
}, | |
[drawOnCanvas, dynamicLineWidth], | |
); | |
const stopDrawing = useCallback(() => { | |
isDrawing.current = false; | |
}, []); | |
const init = useCallback(() => { | |
ctx.current = canvas?.current?.getContext("2d"); | |
if (canvas && canvas.current && ctx && ctx.current) { | |
canvas.current.addEventListener("mousedown", handleMouseDown); | |
canvas.current.addEventListener("mousemove", drawNormal); | |
canvas.current.addEventListener("mouseup", stopDrawing); | |
canvas.current.addEventListener("mouseout", stopDrawing); | |
canvas.current.width = window.innerWidth - 196; | |
canvas.current.height = window.innerHeight; | |
ctx.current.strokeStyle = "#000"; | |
ctx.current.lineJoin = "round"; | |
ctx.current.lineCap = "round"; | |
ctx.current.lineWidth = 10; | |
setIsReady(true); | |
} | |
}, [drawNormal, handleMouseDown, stopDrawing]); | |
const handleRegularMode = useCallback(() => { | |
setIsRegularMode(true); | |
isEraserMode.current = false; | |
setIsEraser(false); | |
isRegularPaintMode.current = true; | |
}, []); | |
const handleSpecialMode = useCallback(() => { | |
setIsRegularMode(false); | |
isEraserMode.current = false; | |
setIsEraser(false); | |
isRegularPaintMode.current = false; | |
}, []); | |
const handleColor = (e: any) => { | |
setCurrentColor(e.currentTarget.value); | |
selectedColor.current = e.currentTarget.value; | |
}; | |
const handleWidth = (e: any) => { | |
setCurrentWidth(e.currentTarget.value); | |
selectedLineWidth.current = e.currentTarget.value; | |
}; | |
const handleClear = useCallback(() => { | |
if (!ctx || !ctx.current || !canvas || !canvas.current) { | |
return; | |
} | |
ctx.current.clearRect(0, 0, canvas.current.width, canvas.current.height); | |
}, []); | |
const handleEraserMode = (e: any) => { | |
autoWidth.current = false; | |
setIsAutoWidth(false); | |
setIsRegularMode(true); | |
isEraserMode.current = true; | |
setIsEraser(true); | |
}; | |
const setCurrentSaturation = (e: any) => { | |
setCurrentColor( | |
`hsl(${hue.current},${e.currentTarget.value}%,${selectedLightness.current}%)`, | |
); | |
selectedSaturation.current = e.currentTarget.value; | |
}; | |
const setCurrentLightness = (e: any) => { | |
setCurrentColor( | |
`hsl(${hue.current},${selectedSaturation.current}%,${e.currentTarget.value}%)`, | |
); | |
selectedLightness.current = e.currentTarget.value; | |
}; | |
const setAutoWidth = (e: any) => { | |
autoWidth.current = e.currentTarget.checked; | |
setIsAutoWidth(e.currentTarget.checked); | |
if (!e.currentTarget.checked) { | |
setCurrentWidth(selectedLineWidth.current); | |
} else { | |
setCurrentWidth(ctx?.current?.lineWidth ?? selectedLineWidth.current); | |
} | |
}; | |
return [ | |
{ | |
canvas, | |
isReady, | |
currentWidth, | |
currentColor, | |
isRegularMode, | |
isAutoWidth, | |
isEraser, | |
}, | |
{ | |
init, | |
handleRegularMode, | |
handleSpecialMode, | |
handleColor, | |
handleWidth, | |
handleClear, | |
handleEraserMode, | |
setAutoWidth, | |
setCurrentSaturation, | |
setCurrentLightness, | |
}, | |
] as any; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment