Shopify / Shopify/react-native-skia
Canvas keeps the frame painted at the previous size after a resize (iOS)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 8.6k
- Forks
- 647
- Avg merge
- 1d 17h
- Merged PRs (30d)
- 35
Description
Description
When a <Canvas> changes size, it can go on displaying the frame it painted at the previous size. React Native siblings re-lay-out immediately, so a chart shows its axes in the new orientation while the drawing is still the old one — a portrait drawing inside a landscape viewport, or the reverse.
The state is not transient. It persists indefinitely (observed for ~60 s, across a re-render) and clears only when the canvas children change for an unrelated reason — in our case a server push delivering new data.
Reproduces on a physical iPhone; not reproducible on the iOS Simulator over ~28 rotation cycles. Roughly 1 rotation in 5–10 on device.
The discriminator: size, not content
This seems to be the diagnostic part.
- Rotation hands the canvas entirely new children (fresh
Pathobjects, new grid lines, new bars — every scale is recomputed) and a new size → no repaint. - A data update hands it new children at an unchanged size → repaints every time.
So it isn't "the canvas ignores new children". It is specifically the resize that leaves the view holding the old frame, and a later content change is what recovers it.
We instrumented the component to confirm the JS side is healthy at the moment the screen is wrong. At the instant of a stale frame the component had logged:
[TimelineChart] #4 canvas: size=956x440, pricePaths=yes, segments=5, bars=2, gridTicks=4
Correct new size, full drawing list, and the screen still showed the 440-wide portrait frame — measured: content stopped at ~397 pt, which is exactly the right edge of the plot for a 440-wide canvas.
What did not fix it
CanvasRef.redraw()on every size change. Logging confirmed the ref was attached at the moment of the call and that it fired for each size reported. The stale frame remained.- Letting the canvas size itself from native layout (
style={StyleSheet.absoluteFill}) instead of passing a JS-computedwidth/height. No change. - Keeping the component mounted across the rotation. Verified with per-instance mount/unmount logging that the canvas was not being remounted.
What does fix it, and its cost
Keying the <Canvas> by its size, so a rotation builds a new canvas rather than resizing one:
<Canvas key={`${Math.round(width)}x${Math.round(height)}`} style={StyleSheet.absoluteFill}>
This works reliably, but it costs a visible flicker on every rotation, and it forces surface re-creation — which we separately observed occasionally producing a completely blank canvas (nothing painted at all, including an ungated background rect, while the component reported a valid size and a full drawing list; recovered only by another remount).
Question
Is onSize now the only supported way to drive content from the canvas's dimensions?
We measure the parent with onLayout and derive geometry from that, which means our children are re-rendered for the new size before the canvas itself resizes. If the resize discards or precedes that paint, that ordering would explain what we see — and would make onSize the correct fix rather than a stylistic preference. Happy to test a patch or provide a standalone reproduction if useful.
Version
2.4.18
Steps to reproduce
A <Canvas> whose parent changes size on device rotation, with children derived from that size:
<View style={{flex: 1}}>
<Canvas style={StyleSheet.absoluteFill}>
<Path path={pathForCurrentSize} style="stroke" color="blue" strokeWidth={2}/>
</Canvas>
</View>
Rotate the device repeatedly. Intermittently the drawing stops following the viewport and keeps the previous orientation's frame until something unrelated changes the children.
Snack, code example, screenshot, or link to a repository
Environment: @shopify/react-native-skia 2.4.18, react-native 0.83.6 (New Architecture, RN 0.83 default — not explicitly disabled), Expo SDK 55, physical iPhone.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the Canvas component's resize and onSize path, then compare it with CanvasRef.redraw during repeated physical-device rotations. Reproduce the stale frame in the reported parent onLayout setup; done means resizing repaints the current children without requiring a size key or causing flicker or blank surfaces.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- ios, react-native, typescript
- Domain
- computer-graphics, mobile-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100