refactor drawing
implement shareable cache logic improve caching fix drawing bug
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { FC } from "react";
|
||||
import { FC, useMemo } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { invoke } from "@tauri-apps/api";
|
||||
import { useTimelineStore } from "stores/timeline.store";
|
||||
@@ -16,6 +16,7 @@ import drawStaggeredText, {
|
||||
calculateLetters,
|
||||
} from "drawers/staggered-text";
|
||||
import useMap from "hooks/useMap";
|
||||
import { Drawer } from "drawers/draw";
|
||||
|
||||
type CanvasProps = {};
|
||||
|
||||
@@ -28,13 +29,7 @@ function typedArrayToBuffer(array: Uint8Array): ArrayBuffer {
|
||||
|
||||
const CanvasComponent: FC<CanvasProps> = () => {
|
||||
const canvas = useRef<HTMLCanvasElement>(null);
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [canvasKit, setCanvasKit] = useState<CanvasKit>();
|
||||
const [fontData, setFontData] = useState<ArrayBuffer>();
|
||||
const surface = useRef<Surface>();
|
||||
const staggeredTextCache = useMap<string, StaggeredTextCache>();
|
||||
const isLocked = useRef<boolean>(false);
|
||||
const [didInit, setDidInit] = useState(false);
|
||||
const renderState = useRenderStateStore((store) => store.renderState);
|
||||
const { fps, size, duration } = useTimelineStore((store) => ({
|
||||
fps: store.fps,
|
||||
@@ -46,126 +41,24 @@ const CanvasComponent: FC<CanvasProps> = () => {
|
||||
updateEntityById: store.updateEntityById,
|
||||
}));
|
||||
|
||||
const drawer = useMemo(() => new Drawer(), []);
|
||||
|
||||
useEffect(() => {
|
||||
InitCanvasKit({
|
||||
locateFile: (file) =>
|
||||
"https://unpkg.com/canvaskit-wasm@latest/bin/" + file,
|
||||
}).then((CanvasKit) => {
|
||||
setCanvasKit(CanvasKit);
|
||||
|
||||
/* fetch("https://storage.googleapis.com/skia-cdn/misc/Roboto-Regular.ttf")
|
||||
.then((response) => response.arrayBuffer())
|
||||
.then((arrayBuffer) => {
|
||||
setLoading(false);
|
||||
setFontData(arrayBuffer);
|
||||
}); */
|
||||
|
||||
invoke("get_system_font", { fontName: "Helvetica-Bold" }).then((data) => {
|
||||
console.log(data);
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
const u8 = new Uint8Array(data as any);
|
||||
const buffer = typedArrayToBuffer(u8);
|
||||
setFontData(buffer);
|
||||
setLoading(false);
|
||||
|
||||
if (canvas.current) {
|
||||
const CSurface = CanvasKit.MakeWebGLCanvasSurface(canvas.current);
|
||||
if (CSurface) {
|
||||
surface.current = CSurface;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
if (canvas.current && !didInit) {
|
||||
drawer
|
||||
.init(canvas.current)
|
||||
.then(() => {
|
||||
setDidInit(true);
|
||||
})
|
||||
.catch((e) => console.error(e));
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
//console.time("calculation");
|
||||
const parsedEntities = AnimatedEntities.parse(entities);
|
||||
|
||||
if (!loading && !isLocked.current) {
|
||||
isLocked.current = true;
|
||||
invoke("calculate_timeline_entities_at_frame", {
|
||||
timeline: {
|
||||
entities: parsedEntities,
|
||||
render_state: renderState,
|
||||
fps,
|
||||
size,
|
||||
duration,
|
||||
},
|
||||
}).then((data) => {
|
||||
const entitiesResult = Entities.safeParse(data);
|
||||
|
||||
if (canvasKit && canvas.current && surface.current && fontData) {
|
||||
surface.current.flush();
|
||||
|
||||
surface.current.requestAnimationFrame((skCanvas) => {
|
||||
skCanvas.clear(canvasKit.WHITE);
|
||||
if (entitiesResult.success) {
|
||||
const entities = entitiesResult.data;
|
||||
|
||||
entities.reverse().forEach((entity) => {
|
||||
switch (entity.type) {
|
||||
case EntityType.Enum.Rect:
|
||||
drawRect(canvasKit, skCanvas, entity);
|
||||
break;
|
||||
case EntityType.Enum.Ellipse:
|
||||
drawEllipse(canvasKit, skCanvas, entity);
|
||||
break;
|
||||
case EntityType.Enum.Text:
|
||||
drawText(canvasKit, skCanvas, entity, fontData);
|
||||
break;
|
||||
case EntityType.Enum.StaggeredText:
|
||||
{
|
||||
let cache: StaggeredTextCache;
|
||||
if (!entity.cache.valid) {
|
||||
const _cache = staggeredTextCache[0].get(entity.id);
|
||||
|
||||
if (_cache !== undefined) {
|
||||
canvasKit.Free(_cache.glyphs);
|
||||
}
|
||||
|
||||
cache = calculateLetters(canvasKit, entity, fontData);
|
||||
|
||||
staggeredTextCache[1].set(entity.id, cache);
|
||||
updateEntityById(entity.id, { cache: { valid: true } });
|
||||
} else {
|
||||
const _cache = staggeredTextCache[0].get(entity.id);
|
||||
if (_cache) {
|
||||
cache = _cache;
|
||||
} else {
|
||||
cache = calculateLetters(canvasKit, entity, fontData);
|
||||
}
|
||||
}
|
||||
|
||||
drawStaggeredText(
|
||||
canvasKit,
|
||||
skCanvas,
|
||||
entity,
|
||||
cache.font,
|
||||
cache.letterMeasures,
|
||||
cache.metrics
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
isLocked.current = false;
|
||||
});
|
||||
} else {
|
||||
isLocked.current = false;
|
||||
console.log(entitiesResult.error);
|
||||
}
|
||||
});
|
||||
}
|
||||
//console.timeEnd("draw");
|
||||
});
|
||||
if (didInit) {
|
||||
drawer.update(entities);
|
||||
}
|
||||
}, [entities, loading, renderState.curr_frame]);
|
||||
}, [entities, renderState.curr_frame, didInit]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user