I've got a mobile web app that uses Zepto to create transitions between pages. Animation is super smooth on desktop, but pretty choppy on my iPhone 4. Are animations more performant than transitions? What's the difference between animations and transitions, aside from granularity of control?
This is not a transition vs. animation question - this is a transform vs. property change question.
Content can be moved around a screen in multiple different ways: the most common two are position property (left, top, margin-left, scroll-position etc.) changes and transforms. On iOS, now in version 5, position property changes are done in pixel increments on the CPU - there is no sub-pixel tweening, so the movement, particularly on older non-retina displays is jerky.
In contrast, transforms - notably 3D transforms are done on the GPU, so you get very smooth movement and sub-pixel tweening.
(Note that some desktop browsers do GPU accelerated position property changes - like IE9 - so there is no difference visually between the two approaches)
I'm no expert, but I assume it's the way the browser handles animations.
It probably takes a more sophisticated and expanded way to handle the multistate animations offer, rather than the simple 2 states transitions have.
Related
I need help with animation performance on RETINA display
I have a swiper slider and on slideTranasitionStart I'm animating a mask (covering the whole slider).
Codepen link here.
The animation is very smooth in all browsers. I'm using an external monitor for work but as soon as I drag the browser window to my retina display (MacBook Pro -Retina, 13-inch, Early 2015, macOS Mojave) the animation looks is very laggy (lowest fps is around 5-6!) !
I tried 2 main options to do the animation:
Adding classes to transform the mask (and only using the most performant css properties transform and opacity on the animated elements). For this option I tried adding will-change property on the animated elements, I replaced translateX with translate3d, I only added transition on the transform property.
Using greensock morphSVG plugin to achieve the transforming of the mask (in the codepen demo I'm using this option as it's way less code)
Unfortunately both options are still laggy on retina display.
Nothing seems to improve the performance on the retina display and I'm running out of options.
I'd be very grateful if someone can help out!
Thank you
I am not sure if this is the proper place to ask this question; I considered Software Recommendations however I figured I'd give it a shot here first.
I am creating a PWA and with every iteration I'd really like to focus on making it look/feel like less janky and as smooth as possible. Well, like a native app.
So here is the problem—I am using transitions (animations) provided by Semantic-UI-React, and while it works beautifully when an element which is occupying its on space, I was wondering if there a practice or pattern which handles the behavior of adjacent DOM elements to one that is being animated?
I supplied a GIF below to illustrate what I mean. I suppose I could just move the animated elements out of the container of the form to prevent jank, but I wondered if there was a more elegant pattern or approach to this...
Merci!
Unfortunately, there is no particularly elegant approach to this.
Regarding the question in the title of your post, a common approach is to animate the height of the new elements. If you don't know the height of the elements, unfortunately animating from 0px to auto does not yet work but you can fudge it by animating max-height from 0px to a value slightly larger than the maximum size you expect the element to be.
But don't do that.
It will cause the browser to layout the page on every animation frame and will almost certainly jank on lower-end devices.
Instead, you're better off to animate transform.
The most common approach is to:
Grab the original position of the elements (using getBoundingClientRect() etc.) that are going to be affected just before you add the new elements to the DOM (perhaps using getSnapshotBeforeUpdate unless you're using hooks, in which case you can use useRef to similar effect).
After you've added the new elements (which you're presumably also animating in by using transform with a suitable scale() function), calculate the delta from where the offset elements are now, compared to where they used to be.
Setup a transform animation from the negative of the delta, to zero (i.e. the FLIP approach). E.g. if the element has been shifted 300px down the page, animate transform from translateY(-300px) to none.
Of course you need to do that for all the elements in the flow that are affected so it might be easiest to put them all in one container and just animate that.
Regarding the third point you have a few choices of technology, none of which are great:
CSS transitions. These are simplest but you'll have to trigger a style flush to get the negative delta starting point to stick. e.g.
elem.style.transform = `translateY(-${offset}px)`;
elem.style.transition = `transform .3s`;
getComputedStyle(elem).transform; // Flush style
elem.style.transform = `none`;
CSS animations. Producing #keyframes rules dynamically using the CSSOM is a pain but at least you don't need to trigger a style flush (and you can set an animation with an implicit to-keyframe for convenience).
#keyframes random-id { from: { translateY(-300px); } }
Web animations. This is probably the most well-suited.
elem.animate({ transform: [ `translateY(-${offset}px)`, 'none' ] }, 300);
// In future when browsers ship support for implicit to/from keyframes:
elem.animate({ transform: `translateY(-${offset}px)`, offset: 0 }, 300);
Unfortunately Safari only has Web Animations support in Tech Preview so Safari users will get the un-animated version until the next Safari release. Edge users will also get the un-animated version until the Chromium based Edge ships. There is a polyfill too but it's not actively maintained at this point in time.
If you're using React, Animating the Unanimatable is a helpful article on all this.
The other tricky part is making sure the scroll doesn't jump for which you might need to look into scroll anchoring.
In our single-page application we have two modes that the user can switch between. The most striking visual difference is that the top navigation bar is completely different, which is why we use crossfade between the two as visual cue to the mode change.
As the animation is playing , there's a lot else happening in the browser - DOM is updated and redrawn, XHRs are done, objects are created&destroyed etc. End result is that the animation becomes quite choppy. In fact, sometimes it seems as if no smooth animation actually happened, because the new navigation bar just appears in two or three steps.
Are there any tips or tricks that one could utilize to achieve smooth animation during time when javascript engine in the browser is under a lot of strain? Are CSS perhaps transitions any benefit? Any suggestions or quality reading material on this are welcome.
I need to smoothly scroll a large amount of text up the screen. Using webkitTransform does this very well, but I would also like to enable the user to change the speed of the scrolling or pause the scrolling based on how fast they're reading. From what I've found, there isn't a way to change the duration of a webkit transition once it has started. Using setInterval and moving the text works, but it gets jumpy and hard to read as the speed increases.
Can anyone recommend a good way to do this that would allow for the user to adjust speed and still give readable text at higher speeds?
I don't know much about modifying currently running CSS3 transformations, but as an alternative solution, I would scroll a large amount of text with JavaScript by using the browser's native scrolling with scrollTop (.scrollTop() if you are using jQuery).
This allows the user to have a familiar interface even without JavaScript, and can be simply implemented with setTimeout() or setInterval(). If the final result is too jumpy, try modifying scrollTop in smaller intervals, but faster. (i.e., instead of adding +100 every 500ms, try adding +10 every 50ms)
I am using jQuery animate to slide in items on a web page. For some reason, only in webkit browsers, there is a trail of artifacts across the space the element was animated over. Is there a way to stop this from happening or hide it?
They are seen on the carousel once you load the page here: http://www.mywebclass.org/~jeff/
In your animate callback, scroll down 1px then back up 1px:
$(this).addClass('active');
scrollTo(document.body.scrollLeft, document.body.scrollTop + 1);
scrollTo(document.body.scrollLeft, document.body.scrollTop - 1);
On this machine here, I can't see any trails, but i know the effect you're talking about.
It's not directly a solution, but I seem to remember that animating the left-property is very resource-heavy on slower systems. It would probably lead to an overall more smooth experience if you would work with a <div> where overflow:hidden; is set and one larger slide, that you move by animating scrollLeft() instead of these animations.
There was a post on jQuery for Designers about that, i'll update once i've found it.
Update: "jQuery for Designers - Fun with Overflows"
I only see the leftovers of the animation in the H1 which I think is caused by using a non standard font.
It's not a solution but if you can't prevent it, you can remove those little bastards by just scrolling the webpage 1 pixel up or down.
You're also using an outdated version of jQuery (1.3 instead of 1.6.2), is there a reason for this? If not, you should use the latest version
Trails on the left side of #font-face text animated using the left property in Chrome & Safari. I found that using some padding left on the text and adjusting the animation to accomodate fixed the problem.