Implementing FoldView in React Native
What’s most amazing to me about React Native is how quickly one can go from idea to product. Applying the state-of-the-art in Web to native app development is a game changer.
This allows you to embrace the component mentality and create small, composable components to build complex native UIs. And, in those rare occasions, when React Native’s capabilities fall short, it also provides you with an escape hatch that can be used to make platform-specific performance tweaks in native code.
Animations in React Native
Although Native Animated is still a work-in-progress, it’s obvious that the native driver as well as React Fiber’s incremental rendering capabilities will both play a key role in building performant applications in React Native in the future. Until then, though, we are still left to deal with the drawbacks associated with building such animations into user experiences.
Recently, a member of the React Native Facebook community posted a GIF of a folding view animation calling for help from Obj-C and Java developers to implement it in React Native. The post quickly attracted a lot of attention and highlighted the common interest in building interactive experiences in React Native.
No Transform Origin Support
The first challenge in implementing the FoldView was to figure out how to rotate the element around its x-axis counterclockwise and apply perspective distortions to activate 3D space.
Both the rotateX and perspective transforms are members of the React Native subset of the available CSS styles. But we will also need to ensure the element’s transform origin occurs at the bottom of our view. And unfortunately for us, transform-origin is not a supported property in React Native. So if we were to use the built-in rotateX transformation by itself, the pivot point for the rotation would occur at the center of the element which is the default behavior and that’s not what we want.
In a perfect world, React Native would support transform-origin out of the box. But the current implementation of the layout system doesn’t easily allow for this. React Fiber will possibly fix many of these layout issues via its Integrated Layout feature. In the meantime, we’ll have to hack our way through to achieve the desired result.
The matrix property has to be one of the least used in CSS. Matrix transformations allow for repositioning, scaling, skewing and rotation giving you complete control over how an element is displayed in its coordinate space. Transformations such as translate, scale, skew and rotate are simply abstractions to matrices and matrix operations.
Although Animated does not have support for the matrix property, it’s a valid React Native style property that can be used to define mathematical matrix transformations on your views.
Taking the Red Pill
In this demo, we will be dealing with 4x4 matrices for the 2D transforms we want to compute.
Let’s finally write some code.
To rotate an object by dx degrees about the x-axis, we would have to apply the following transformation.
This will spin an object around its horizontal axis and is essentially what the built-in rotateX does for you less the origin transformation step we will do next to achieve the “flip fold” effect.
Note that since Math.cos and Math.sin take angles in radians, we also had to convert degrees to radians in our rotateX function.
Rotations are based from the origin. So applying rotation to an element that is centered at the origin will give a different result than an element that has been translated away from the origin.
Since the transform origin of a view is at its horizontal and vertical center by default, to rotate it in x-space along the bottom, we need to first shift our view’s origin on the y-axis by 50% of the view’s height, then apply rotation, then shift it back to the original center.
To do that, we will multiply our matrix by an identity matrix of which the y-component is equal to 50% of the height of our object, and then multiply the resulting matrix once more, where y = -(h/2) to restore origin.
Luckily, React Native has a private module named MatrixMath that we can use to avoid doing dot multiplication by hand. So here’s the transform-origin helper function that we will use in composing the result of our final transform.
Finally, to create perception of depth, we need to set the perspective property so that points in z-space are not flattened onto the same 2D plane when the transformation is applied. As I mentioned earlier, React Native has built-in support for perspective. So we’ll simply set it to produce an illusion of depth in the resulting transform.
Add all these these together and just like that you should have a beautiful flip animation.
There are a lot of other details that I did not cover in this post. I strongly urge you to read the FoldView source code on Github. The code is pretty short and easy to grasp.
I might be repeating myself, but React Native is awesome. As with many other things, there are times and places where maxims and rules may need to be altered to best answer a question. Although, React Native abstracts away a great deal of the complexity for you, you should not be afraid to actually look under the hood and may need to tweak a thing or two to make it work for you.
Did you enjoy the article? Please don’t forget to click the ❤ to recommend it to others and share.