Frontned Development/FE 회고
Konva vs Fabric vs Canvas API 라이브러리 선택과정
데비시
2023. 7. 20. 10:28
Drawing 기능 +@를 구현하기 위해서 canvas 라이브러리를 선택하는 과정에 고민이 생겼다.
이 기능을 제공하는 여러 라이브러리가 있지만, 리액트와 충돌 이슈가 있는 라이브러리를 모두 제외하니 Konva, Fabric, 자체 구현 세 가지 방식이 남았다. 이 중 자체 구현은 기능이 확장 될 때마다 새로 만드는 부분에서 시간적인 효율이 나오지 않는다 생각하여 제외했다.
결론을 먼저 말하면, Konva를 선택하였다.
(이후 나올 모든 그림의 순서는 canvas - konva - fabric 순입니다.)
1. 성능 비교
lighthouse 렌더링 성능 비교
- lightHouse 성능은 다 고만고만하다. fabric이 미세하게 빠르다.
- 최적화가 안되어있을 때는 canvas 라이브러리들을 사용하면 SI가 굉장히 느리게 나온다. 특히, Fabric이 느리고 내장 canvas API가 빨랐다.
Perfomance 비교
- 모든 과정은 해당 네모 그리기를 하였으며 일부 오차가 있을 수 있지만 테이프로 모니터에 표시하고 동일한 영역을 수행.
- 퍼센트를 비교했을 때, Fabric이 두 배 가량 빠르지만, 절대값으로 생각하면 모든 라이브러리가 압도적으로 빠르다.
체감 성능
- 셋 다 비슷하다. fabric.js ≥ konva ≥ canvas API
2. 생태계 및 공식문서
- 1번 그림처럼 Konva의 생태계가 훨씬 크며, fabric을 2년 전부터 압도하였다. 개인적으로 커뮤니티를 돌아다니며 찾은 이유는 아래와 같다.
- react, svelte, vue에 알맞게 계속 라이브러리 및 문서의 업데이트 (특히, react 생태계가 커짐)
- 반응형, 이벤트 버블링 등 각종 편의 기능 지원
- 잘 만들어진 공식 문서와 객체 지향 API
- 스트레스 부하 테스트를 통한 안정성 검증
- 물론 Fabric도 공식문서가 잘 되어있으며, react를 사용할 수 있게 업데이트가 진행되고 있다. 단지, Konva가 더 잘 했을 뿐이다.
3. 가독성 / 난이도
https://dev.to/lico/react-comparison-of-js-canvas-libraries-konvajs-vs-fabricjs-1dan
이 분이 쓴 글을 보면 더 다양한 비교를 알 수 있는 데, 가독성과 난이도 자체는 fabric.js가 쉽고 좋아보인다.
// konva free drawing
import { useEffect, useRef, useState } from "react";
import styled from "styled-components";
import Konva from "konva";
export default function KonvaCanvas() {
// konva
const stageRef = useRef<Konva.Stage | null>(null);
const layerRef = useRef<Konva.Layer | null>(null);
const lastLineRef = useRef<Konva.Line | null>(null);
const [isPainting, setIsPainting] = useState(false);
const [lineColor, setLineColor] = useState<string>("#f80000");
const [lineWidth, setLineWidth] = useState<number>(10);
// function
const startPaint = () => {
if (!stageRef.current || !layerRef.current) return;
setIsPainting(true);
const pos = stageRef.current.getPointerPosition();
if (pos) {
const newLine = new Konva.Line({
stroke: lineColor,
strokeWidth: lineWidth,
globalCompositeOperation: "source-over",
lineCap: "round",
lineJoin: "round",
points: [pos.x, pos.y, pos.x, pos.y],
});
layerRef.current.add(newLine);
lastLineRef.current = newLine;
}
};
const paint = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
if (!isPainting || !stageRef.current || !lastLineRef.current) return;
e.preventDefault();
e.stopPropagation();
const pos = stageRef.current.getPointerPosition();
if (pos) {
const newPoints = lastLineRef.current.points().concat([pos.x, pos.y]);
lastLineRef.current.points(newPoints);
layerRef.current?.batchDraw();
}
};
const stopPaint = () => {
setIsPainting(false);
};
useEffect(() => {
const stage = new Konva.Stage({
container: "mainCanvas",
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
stageRef.current = stage;
layerRef.current = layer;
}, []);
return (
<Container>
<div
id="mainCanvas"
onMouseDown={() => startPaint()}
onMouseMove={(e) => paint(e)}
onMouseUp={() => stopPaint()}
/>
</Container>
);
}
// fabric
import { useEffect, useRef, useState } from "react";
import styled from "styled-components";
import { fabric } from "fabric";
export default function FabricCanvas() {
const fabricRef = useRef<any>(null);
const [lineColor, setLineColor] = useState<string>("#f80000");
const [lineWidth, setLineWidth] = useState<number>(10);
useEffect(() => {
const canvas = new fabric.Canvas("c", {
isDrawingMode: true,
});
fabricRef.current = canvas;
// canvas setting
canvas.setWidth(window.innerWidth);
canvas.setHeight(window.innerHeight);
fabric.Object.prototype.cornerStyle = "circle";
canvas.freeDrawingBrush = new fabric["PencilBrush"](canvas);
if (canvas.freeDrawingBrush) {
const brush = canvas.freeDrawingBrush;
brush.color = lineColor;
brush.width = lineWidth;
}
return () => {
canvas.dispose();
};
}, []);
return (
<Container>
<div>
<canvas id="c" ref={fabricRef} />
</div>
</Container>
);
}
4. bundle size (konva + react-konva vs fabric)
- 번들 사이즈는 konva가 이겼다.
결론
- 성능 차이는 절대값에서 크지 않았기 때문에, 성능은 예외로 하였다. (사실, 내가 아직 성능에 대해서 판단할만큼 수준이 높지 않아서도 맞는 거 같다..)
- 안정성(부하테스트)과 번들 사이즈에서 konva에 가산점을 부여했다.
- react-konva와 같이 react에 초점을 맞춘 라이브러리가 추가되면서, 장기적인 관점에서 추가 가산점이 들어갔다.
- 단순히 freedrawing 기능 구현에만 초점을 맞추면 fabric이 쉽고 낫다. 하지만, 기능의 확장, 안정성 등 장기적인 관점에서 konva가 유리할 것으로 생각된다. 우리 프로젝트는 기획이 확장될 가능성들이 있기 때문에 Konva를 채택했다