A mistake that is worth 30 extra frames per second
Following my Cross-platform email client with nothing but HTML article, we’ve been told by many people that they’d love to hear more about some tricks that make our app feel so native and fast.
Our dirty little secret: Cross-platform email client with nothing but HTML
Amazingly fast, receiving 5-star reviews and featured by Apple.
Take our conversation list for example and the steps we took to get a 60 FPS on (almost) every mobile device. For the simplicity of this article, we’ll just assume that we have a list (
ul) of items (
li). Also note that no transition or animation are involved when I’m talking about swiping; I’m just referring to the items following the touch as you move your finger.
Initial naive version
li are normally positioned. Upon swiping, each of the targeted
li (hello Multi-Swipe™) has its
transform: translateX() updated. Chances are that if you test on a very powerful and recent mobile or Chrome with device toolbar enabled, it’ll be as good as native. On my iPhone 6, we thought it felt pretty good for a JS swipe. Only then did we buy a Google Pixel to develop the Android version and realized how much more responsive the swiping was on that device. My phone not being that old, we knew there was some improvements that could be done.
I didn’t plan on talking about this one and start the initial version above with
translate3d, but truth is I did use
translateX at first. It was my understanding that nowadays
translateX is just as hardware-accelerated as its 3d equivalent. However it felt a little bit more fluid with
translate3d, especially on mobile. 🙌🏼
2. Absolutely positioned items
An issue on my phone that we didn’t feel on the Pixel was that there would be some delay between the moment the touch move began and the first rendering of the swiped elements. The UI was frozen during the first 10px and then the items jumped to the position they were supposed to be. 🤢
We figured it was the hardware accelerating rocket booting up. 🚀
So we put the
li in an already “accelerated” state by absolutely positioning them and
tranlate3d to their position. It worked! No more delay on my phone at the beginning of the gesture. 🙌🏼
p.s. I know about the infamous
backface-visibility: hidden trick, but in our case it didn’t seem to change anything.
3. The x-axis & the mistake 🙊
Now that the items are translated on the y-axis, all we need to do is translate the
x when swiping while keeping the
y and we’ll be done!
But here comes “the mistake”. I somehow mixed some React
refs and ended up translating the underlying
We could instantly feel the difference and knew we had finally reached that native feeling that we were looking for. See the difference for yourself:
If you think this is video recording lag, wait until you see the next one.