Optimizing Konva Performance with Layer Caching and Tiling
Introduction
In previous articles, I have guided you on using Viewport Culling and requestAnimationFrame to improve performance when using Konva to render 1,000 elements. Now, we will continue to improve further with Layer Caching and Tiling techniques to handle up to 10,000 elements.
Prerequisites
This article is extended from my previous articles, so please review the previous articles to grasp the necessary information before continuing.
Detail
Update file type.ts
Update file utility.ts
This code block sets up configuration constants for the canvas (dimensions, a total of 10,000 objects, Tile size) and the generateData function to create mock data for shapes with random positions, colors, and types within the virtual space.
Update file ShapeItem.tsx
This is the component used to render each specific geometric object. It uses React.memo to avoid unnecessary re-renders and optimizes by disabling expensive rendering features like perfectDrawEnabled, helping the vector drawing process occur faster.
Create file Tile.tsx
- This component implements the Layer Caching technique by tile (Tile). When the user zooms out (isZoomedOut), it converts all vector content within the Tile into a single Bitmap image (cache) using requestIdleCallback to avoid causing lag. When zooming in, it clears the cache to return to an interactive vector format.
- We need to use requestIdleCallback or setTimeout as a form of debounce to avoid calling the cache function too many times, which would affect the main thread.
- Using the requestIdleCallback function is different from setTimeout in that it can execute caching when the browser is free (before 2s), and when it reaches 2s and the browser is still not free, it will force the cache function to run (unlike setTimeout, which will always run after 2s).
- Note that the requestIdleCallback function is not yet supported in all browsers; you can check here.
Update file OptimizedCanvas.tsx
- This is the main control center, using Spatial Indexing to partition 10,000 elements into Tiles based on coordinates. It performs "Tile-Level Culling" to render only the tiles located within the viewport (Viewport) and uses requestAnimationFrame to smooth the process of updating coordinates when zooming/dragging.
- Extremely Low Rendering Cost: Instead of tilesMap.forEach (iterating through everything), we use 2 for loops running through the x, y coordinates of the view area. If the screen displays 10 tiles, React renders exactly 10 Tile Components.
- Low Latency: Separating the visibleTileKeys logic helps React "diffing" extremely quickly because the number of elements in the render array is always stable and small.
- Event Preservation: When zooming in (isZoomedOut = false), clearCache() is called, reverting items to their original vector form, helping users click accurately on each element.
The result will be as follows: you can still view 10,000 items simultaneously, at which point the elements are in Bitmap format.
Or when zooming in smaller, the elements are in vector format capable of receiving click events.
Happy coding!
Comments
Post a Comment