Skip to content

Reanimated Context#153

Merged
elliottkember merged 14 commits intothomas/reanimatedfrom
elliott/reanimated-context
Jan 6, 2026
Merged

Reanimated Context#153
elliottkember merged 14 commits intothomas/reanimatedfrom
elliott/reanimated-context

Conversation

@elliottkember
Copy link
Collaborator

@elliottkember elliottkember commented Dec 25, 2025

This isn't a Reanimated thing, but since we're looking at refactoring the way our zooms work, it seems like a great time to add this!

We've always had to do this awkward thing where we define a zoom value, then provide to the zoomable view, but then we define our own context so we can use the zoom inside it.

This is what Contexts are great for — accessing contextual values provided by a component. In this case, the Zoomable view can provide a context to the child elements, which are provided by the consumer. I've refactored the example Dot component to use this context value.

This refactor ended up being dead simple and I think will help us a lot!

FixedSize

It's a common thing to want something to remain the same size on screen regardless of zoom. The dots in the example app are a good example of this. So I also added a FixedSize component, which inverts the zoomLevel in the zoomable view to keep the elements at a fixed size.

{[20, 40, 60, 80].map((left) =>
  [20, 40, 60, 80].map((top) => (
    <FixedSize left={left} top={top} key={`${left}x${top}`}>
      <View style={styles.marker} />
    </FixedSize>
  ))
)}

I've found that nesting this with other transforms can result in really bad framerates in the New Architecture iOS, so this is also provided as an fixedSizeStyle attribute in the context for that use-case.

// Inverse zoom style gives us a style we can use to "invert" zoom and have the component stay the same size
const unzoomStyle = useAnimatedStyle(() => ({
  transform: [{ scale: 1 / zoom.value }],
}));

// Usage
const Marker = () => {
  const context = useContext(ReactNativeZoomableViewContext);

  return (<Animated.View style={context?.unzoomStyle} />);
};

Note

Introduces a context API for ReactNativeZoomableView and a utility for fixed‑size overlays.

  • New ReactNativeZoomableViewContext provides zoom, inverseZoom, inverseZoomStyle, offsetX, offsetY; ReactNativeZoomableView now wraps children in the provider
  • Switches main transform to useAnimatedStyle with .value access for scaleX/scaleY and translations
  • Adds components/FixedSize that uses inverseZoomStyle to keep children a constant on-screen size; exported from index
  • Example app replaces manual animated scaling with <FixedSize> markers and removes ad-hoc scale handling
  • Types and maps updated accordingly

Written by Cursor Bugbot for commit a327775. Configure here.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment @cursor review or bugbot run to trigger another review on this PR

@elliottkember
Copy link
Collaborator Author

bugbot run

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment @cursor review or bugbot run to trigger another review on this PR

@thomasttvo thomasttvo added the enhancement New feature or request label Jan 2, 2026
const zoom = useSharedValue(1);
const inverseZoomStyle = useAnimatedStyle(() => ({
transform: [{ scale: 1 / zoom.value }],
}));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can probably be moved into the context to keep the zoomable view logic clean

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, might be better to only expose it as a DerivedValue, then let Unzoom use it however it wants (in this case, it can be in charge of plugging the value in the style)

Notice that you don't need useAnimatedStyle here

<Animated.View
      style={{
		transform: [{ scale: inverseZoom }],

Copy link
Collaborator Author

@elliottkember elliottkember Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can probably be moved into the context to keep the zoomable view logic clean

I'm not sure what you mean by moving this into the context — the zoomable view creates the context and provide the context values to the children. Do you mean that we should create a new ReactNativeZoomableViewContextProvider component in another file that creates inverseZoom and inverseZoomStyle derived values?

Notice that you don't need useAnimatedStyle here

Unfortunately without useAnimatedStyle we get a warning on every frame:

[Reanimated] Reading from value during component render. Please ensure that you don't access the value property nor use get method of a shared value while React is rendering a component.

So we have to useAnimatedStyle for reanimated style attributes.

only expose it as a DerivedValue

We could do this, but unless I got something wrong with that warning, every component would have to create its own inverseZoomStyle with useAnimatedStyle. With the inverse zoom style exported directly, we can just use it directly like:

<Animated.View style={[context?.inverseZoomStyle, { transform: [...] }]} />

This may be helpful for New Architecture iOS, which seems to have some bugs with nested animations — nesting the components inside a FixedZoom component absolutely murders the framerate. I had no luck figuring out why, and sometimes it was fine which leads me to believe it's some weird React Native or even Reanimated bug!

@elliottkember
Copy link
Collaborator Author

bugbot run

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment @cursor review or bugbot run to trigger another review on this PR

@elliottkember elliottkember merged commit 5193634 into thomas/reanimated Jan 6, 2026
1 check passed
@elliottkember elliottkember deleted the elliott/reanimated-context branch January 6, 2026 19:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants