canvas와 video tag를 활용하여 영상을 비교하며 볼수 있는
react component를 만드는 과정에 대해 설명하는 글이다
canvas 는 HTML5에서 등장한 tag이다
canvas 위에는 2d 혹은 3d 의 다양한 도형과 이미지파일을 렌더링할 수 있다
사각형을 렌더링 과정은 아래와 같다
getContext()
를 실행하여 2d 혹은 3d 를 결정하는 context 값을 얻는다위 과정을 통해 알 수 있듯이
canvas는 context를 가진다
이 context 위에서 다양한 도형 혹은 이미지파일을 그리게 된다
이 canvas 위에서 움직이는 도형을 그리는 방법은
setInterval을 사용하여 변화한 값을 반영하여
반복적으로 다시 렌더링하는 것이다
react hook과 함께 setInterval을 사용하기 위해서는 useEffect
가 필요하다
useEffect는 componentDidMount와 componentDidUpdate, componentWillUnmount까지 합친것과 비슷하다
useEffect(() => {
//componentDidMount
return (
//componentWillUnmount
)
}, ['이 배열에 추가한 item이 바뀔때, componentDidUpdate 실행'])
위 코드에서처럼 componentDidMount 에서 setInterval을 설정하고
component가 unmount 될 때 clearInterval 해준다
또한 바뀌는 video와 state 를 배열에 추가한다
이를 통해 마치 setInterval을 실행하는 것처럼 동작하게 만든다
실제 코드를 보자
useEffect(() => {
const interval = setInterval(() => { if (video) {
devineVideoToCanvasWithBar(video, canvasRef.current, video.videoWidth / 4)
}
})
return () => clearInterval(interval)}, [video, barX])
첫번째 하이라이팅된 부분
setInterval이 실행되었다 이때 canvas에 video 캡쳐이미지를 그리는 함수
devineVideoToCanvasWithBar
가 실행되었다
두번째 하이라이팅된 부분
component가 unmount될 때 실행되는 부분으로
clearInterval 을 실행하고 있다
세번째 하이라이팅된 부분
배열안에 video와 barX 2개의 item 이 있다
video는 video tag이다
barX는 bar element의 x 좌표이다
이 두가지가 변할때 useEffect는 실행된다
동작 순서는 아래와 같다
그림에서 확인할 수 있듯이 Source image가 video tag 화면이다
video tag에 송출되는 영상을 캡쳐해 Destinaion canvas에 그리려고 한다
이때 주의할 점은 캡쳐해오는 image와 그릴 image의 넓이를 변수로 두는 것이다
왜냐하면 움직이는 bar의 x좌표 즉 barX
에 따라 image의 넓이가 바뀌기 때문이다
이 drawImage 를 활용하여 아래의 과정을 거쳐 video를 2개로 나눠 렌더링했다 위에서 보여준 그림 즉 전체 과정이다
const barMove = (e: any) => {
if (dragabble) {
setBarX(e.pageX - canvasX)
}
}
const mouseDown = (e: any) => {
if (barX < e.pageX - canvasX && e.pageX - canvasX < barX + barWidth) {
setBarX(e.pageX - canvasX - barWidth / 2)
dragabble = true
canvas.onmousemove = barMove
}
}
const mouseUp = (e: any) => {
dragabble = false
canvas.onmousemove = null
if (e.pageX - canvasX >= canvas.width - barWidth) {
setBarX(canvas.width - barWidth)
} else {
}
}
const drawBar = (x: number, y: number, w: number, h: number, style: string) => {
ctx.fillStyle = style
ctx.rect(x, y, w, h)
ctx.fill()
}
ctx.clearRect(0, 0, video.videoWidth / 2, video.videoHeight)
ctx.beginPath()
ctx.drawImage(...Object.values(backgroundVideoConfig))
ctx.beginPath()
ctx.drawImage(...Object.values(overrideVideoConfig))
ctx.beginPath()
drawBar(barX, 0, barWidth, video.videoHeight, '#444444')
다
canvas.onmousedown = mouseDown
canvas.onmouseup = mouseUp
전체 코드는 마지막에 다시한번 읽어보자
아래에서는 함수를 하나씩 따로 설명할 것이다
const barMove = (e: any) => {
if (dragabble) {
setBarX(e.pageX - canvasX)
}
}
바가 움직일때 실행되는 함수이다
setBarX로 barX 즉 bar의 x좌표 값을 바꾼다
즉 현재 마우스의 x좌표 값에서 canvas tag의 x좌표값을 빼면
canvas 위에서의 x좌표 값이 나온다
canvas 위에서의 x좌표 값으로 barX의 값을 수정한다
const mouseDown = (e: any) => {
if (barX < e.pageX - canvasX && e.pageX - canvasX < barX + barWidth) {
setBarX(e.pageX - canvasX - barWidth / 2)
dragabble = true
canvas.onmousemove = barMove
}
}
mouseDown은 bar 위에 mouse를 올린 후
click하는 event까지 2개의 event가 모두 발생했을 경우 실행되는 함수이다
const mouseUp = (e: any) => {
dragabble = false
canvas.onmousemove = null
if (e.pageX - canvasX >= canvas.width - barWidth) {
setBarX(canvas.width - barWidth)
} else if (e.pageX - canvasX <= 0) {
setBarX(0)
}
}
mouseUp은 canvas 바깥으로 마우스가 나가고 click을 그만하면 실행되는 함수이다
const drawBar = (x: number, y: number, w: number, h: number, style: string) => {
ctx.fillStyle = style
ctx.rect(x, y, w, h)
ctx.fill()
}
ctx.clearRect(0, 0, video.videoWidth / 2, video.videoHeight)
ctx.beginPath()
ctx.drawImage(...Object.values(backgroundVideoConfig))
ctx.beginPath()
ctx.drawImage(...Object.values(overrideVideoConfig))
ctx.beginPath()
drawBar(barX, 0, barWidth, video.videoHeight, '#444444')
canvas.onmousedown = mouseDown
canvas.onmouseup = mouseUp
drawBar는 bar를 그리는 함수이다
동작 순서는 아래와 같다
ctx.drawImage(...Object.values(backgroundVideoConfig))
를 실행하여 SR video를 background에 깐다ctx.drawImage(...Object.values(overrideVideoConfig))
를 실행하여 original video를 그 위에 override 한다위 과정을 setInterval을 통해 무한 반복하여 마치 재생되는 것처럼, bar가 움직이는 것처럼 보이게 한다
ctx.beginPath()
const backgroundVideoConfig = {
video,
sourceX: video.videoWidth / 2 + barX, sourceY: 0,
sourceWidth: video.videoWidth / 2 - barX,
sourceHeight: video.videoHeight,
drawX: barX,
drawY: 0,
drawWidth: video.videoWidth / 2 - barX,
drawHeight: video.videoHeight,
}
ctx.drawImage(...Object.values(backgroundVideoConfig))
ctx.beginPath()
const overrideVideoConfig = {
video,
sourceX: 0,
sourceY: 0,
sourceWidth: barX,
sourceHeight: video.videoHeight,
drawX: 0,
drawY: 0,
drawWidth: barX,
drawHeight: video.videoHeight,
}
ctx.drawImage(...Object.values(overrideVideoConfig))
ctx.beginPath()
drawBar(barX, 0, barWidth, video.videoHeight, '#444444')
하이라이팅된 부분들이 barX가 들어간 곳이다
위 코드가 진행되면서 canvas에 그려지는 element의 순서는 아래와 같다
이때 2개의 video element가 그려질때 그 비율을
barX로 즉, bar의 x좌표로 조절할 수 있다
또한 bar가 그려지는 위치또한 x좌표로 조절할 수 있다
이를 통해 video가 보여지는 비율과 bar의 위치를
조절하는 것이 가능해진다