-
Notifications
You must be signed in to change notification settings - Fork 82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[RFC] Universal animation component library #46
Comments
There has been discussion about native support from Framer Motion: motiondivision/motion#180 I'm not aiming to recreate every feature they have. Only the simple parts. This needs to solve 80% of the problems, not all of them. |
This would honestly be awesome. Reanimated v2 in of itself is a leaps and bounds improvement API over the API in v1 imo. What do you would be the most important features to knock out right out of the gate? |
Agreed, I found v1 almost unusable.
I'm torn between animations and transitions, but I think simple style transitions would be the most important at first. Expo + Next.js is an incredible stack. And yet, achieving this is a hassle: const color = loading ? 'blue' : 'green'
<Text transitionProperties={['color']} sx={{ color }}/> That doesn't make sense to me. Doing this for responsive style arrays, and for function style props would be more difficult. I think that could be step 2. Tracking if this variable changed to drive an animation is more of a challenge on native: const color = loading ? 'blue' : 'green'
<Text transitionProperties={['color']} sx={{ color: [loading && 'blue', loading && 'green'] }}/> On web, doing the above already works for me if I use the I'm not sure if transitions or animations are more important, but I think transitions happen more often, and are currently a bit more difficult to drive on native. You can use Reanimated v1's Another idea could be to use Reanimated's |
It’s possible that running the animations is an easier start than the transitions, I’ll look into both. I wonder if this should live separately from the core Dripsy package. It would maybe be nice if it did, but I’m not sure if that’s possible, since it might have to read in the internal styles after the |
I was wondering the same thing. Maybe it's possible to somehow merge the base style property and sx when the animations are being applied. But that I'm not sure of. It does however make sense for the library to be separate I can definitely see people choosing to use this even if they don't use dripsy. |
Maybe it makes the most sense for it to be its own package that dripsy uses under the hood. If you use dripsy, you get it out of the box, but otherwise you don’t have to. This way, the animation lib is receiving the final parsed style objects from dripsy itself. I’d have to think through how creating a themed component would work in that case. It could be messy if every lib just re-exports React Native in its entirety. One argument in favor of including it in Dripsy (which I’m not stuck to) is that you could use it for only animated styles and not even know about anything else. If you don’t use responsive styles or a theme, dripsy works just like a normal RN element (or at least it should...) |
Another thought here, the animated library would basically only need to export View and Text now that I think about it. And an HOC for making your own. |
I just checked out the Framer Motion docs. Pretty crazy how simple this is: https://www.framer.com/api/motion/animate-shared-layout/ |
That's pretty crazy. Some of those were relatively complex animations with just a few lines. I've never used framer personally but have definitely kept an eye on it. If we could replicate something even close to that it would be super useful in RN. |
Yeah...would be awesome. For now, even looping through object keys isn’t really supported in Reanimated 2, so I’m trying to put together a demo that can loop though each style change. |
I put together an idea here: https://github.com/nandorojo/redrip <View animate={{ width }} /> It takes an |
This is pretty sweet. I have some basic animations that might be able to use this on the app I'm working on I'll try it out. |
If this new library did work closely with Dripsy one cool feature might be to have this library key off theme keys to create animations. For example: const theme = {
durations: {
short: 200
}
}
<View animate={{width }} duration='short' /> |
Agreed. That would be pretty doable. I'm not sure if the transitions should live in the normal |
React spring has discussed integrating reanimated. This seems really promising. However, it hasn’t been updated on npm in 6 months, so not sure if there’s much movement. |
Haven't seen any movement there. React Spring looks pretty solid, here's an example (https://snack.expo.io/@nandorojo/biased-popsicle). I do prefer a component-only API personally. I might try it for now, but I definitely have my eye on Reanimated 2 as a universal solution. I'll be trying both out as Reanimated approaches stability. I haven't actually upgraded to Reanimated 2 in my app yet. I might have to wait for Expo SDK40 (since I assume it'll be more stable then.) That said, I may upgrade sooner if I can justify the refactor. |
Something I haven't been able to figure out here. I like this api from framer-motion: <View initial={{ opacity: 0 }} animate={{ opacity: 1 }} /> The idea is, Right now, the repo I shared that I'm working on does this: <View animate={{ opacity: visible ? 1 : 0 }} /> That works well. I'm not sure how to get the |
Turns out, I got all of this to work pretty gracefully. It's really nice. The only problem is that performance is (or at least seems to be) quite laggy on web. Not sure if Reanimated has really optimized for web yet or not. I'm going to see if using keyframes (or |
Latest changes are pushed here: https://github.com/nandorojo/redrip The example has a side-by-side comparison of Here is a video showing the two on iOS. ThoughtsIt's hard to say definitively which is more performant from such a minor example. Feel free to try them on ios/web to see. I could also try the same with A few takeaways
If it turns out that Reanimated isn't mature enough on web, I think Next stepsWhile I've been hesitant to upgrade my app to Reanimated 2 (due to its alpha stage), I think I'm going to upgrade and try to bring this idea into it. It's possible I could release a beta for the universal animation lib very soon. |
I got a lot of help from @terrysahaidak on Twitter for how I should structure this. I'll be pushing some updates soon, and once software-mansion/react-native-reanimated#1511 is fixed I'll be comfortable merging this into my app. I think I've nailed the ideal API. You can animate your components with either of the following options:
1) props-based animationsimport { View } from 'redrip'
const Loader = ({ isLoading }) => {
return <View initial={{ opacity: 0 }} animate={{ opacity: isLoading ? 1 : 0 }} />
} This API is ideal if you rely on state changes to drive your animations, such as loading states, etc. It's also very clean: any values in I might rename KeyframesThis API also lets you run a simple enter animation, just like CSS keyframes: import { View } from 'redrip'
const FadeIn = () => {
return <View initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ type: 'spring' }} />
} If you never update the values in Super easy 🔥 2) predefined states, better performanceThis API looks more like Predefine static states, and transition to them as you please. const animator = useAnimator({
initial: { opacity: 0 },
open: { opacity: 1 },
pressed: { opacity: 0.7 }
})
const onOpen = () => {
if (animator.current === 'initial') {
animator.transitionTo('open')
}
}
<View animator={animator} /> The benefits of this API become much more apparent as your styles have more than just one value. The pseudocode doesn't really do it justice. TODO
|
Something I always thought has been a pain in React Native: animating dynamic height, such as Even though it's based on a state change, the DX for this is really clean. It's nice for those times when you don't want to set up animated values and just want layout shifts to be smoother. The idea use-case I see: you're showing a skeleton before some content loads, and then you want it to smoothly transition to its next state. function Measure() {
const [{ height }, onLayout] = useLayout()
const [open, toggle] = useReducer((s) => !s, false)
return (
<>
<Drip.View animate={{ height }} style={{ overflow: 'hidden' }}>
<View
onLayout={onLayout}
style={{ height: open ? 100 : 300, backgroundColor: 'green' }}
/>
</Drip.View>
<Button title="toggle" onPress={toggle} />
</>
)
} There's no magic: you just wrap a variable-height element with our library's Simulator.-.iPhone.12.Pro.Max.mp4You'll notice in the video that the shrinking of content is a bit less smooth than the expansion. I think for most situations, this is fine, since placeholder content is usually smaller than real content. If you need more fine-grained control, you can use fixed heights. But I can probably count on one hand the number of times I've used fixed heights in real-world apps, besides images. |
This is almost ready to push online. I just managed to get mount/unmount animations to work using Video: https://twitter.com/FernandoTheRojo/status/1349884929765765123 |
Published: moti.fyi |
Dripsy v3 has docs for usage with Moti: https://github.com/nandorojo/dripsy/blob/typez/README.md#%EF%B8%8F-animated-values We've come full circle. |
@nandorojo I'm starting a component lib using |
My guess is there may be some. As far as I know, creating Animated shared values has some cost to it. For example, if every component has That said, I don't know this to be true with certainty. It's just from stuff I've read on Reanimated issues. So I recommend asking on Reanimated's repo to get a better answer. It would certainly be cool to have both Moti and Dripsy working with every component. When asking on Reanimated, I would want to know if 1) there is cost to |
Gotcha that makes a lot of sense; thanks for the insight. I'll play it safe in the meantime until I know a little bit more. |
Sweet. I recommend using 2.3.x, I heard it has much better performance for many animated nodes. |
This issue is more of a RFC.
What
There is an opportunity to make something like
react-native-animatable
that is powered by Reanimated 2.x. I think it would follow an API similar to CSS transitions. I'm looking toframer/motion
for inspiration on that, since it's super clean and easy to use.How
My top priority would be to achieve transitions at the component level, without any hooks, and with the least amount of code. It should be as simple as possible – no config. This seems like a great DX:
Under the hood, it would only use reanimated on native. Components would intelligently transition properties you tell it to. I don't have experience with Reanimated 2 yet, but it seems like they provide hooks that would make this remarkably doable. We could even default components to have
transitionProperty: all
, such that all you have to write is this:...and you get smooth transitions. Not sure if that's desired, but just spit balling.
We could use CSS transitions and keyframes on web, since RNW supports that. I'm not married to a specific method of solving this problem, though, so maybe Reanimated will work on web too.
In addition to property transitions, animations would be great:
Today
With react native web, CSS animations are really straightforward:
Same goes with transitions:
The problem is that this code is not universal. It only works on web. In my opinion, using
Platform.select
orPlatform.OS === 'web'
is an anti-pattern when it comes to styles. That logic should all be handled by a low-level design system. Dripsy has made strides for responsive styles in this regard. Smooth transitions with a similar DX would be a big addition.Final point: good defaults
In the spirit of zero configuration, this library should have great defaults. It should know the right amount of time for the ideal transition in milliseconds. I would look to well-designed websites to see what the best kind of easing function is, and this would be the default.
There is a plethora of "unopinionated" animation projects for React Native. There are few that are dead-simple, highly-performant, and have professional presets from the get go. I don't want to create an animation library. Only a small component library that animates what you want, smoothly, without any setup.
The text was updated successfully, but these errors were encountered: