ionic-team / ionic-framework

A powerful cross-platform UI toolkit for building native-quality iOS, Android, and Progressive Web Apps with HTML, CSS, and JavaScript.
https://ionicframework.com
MIT License
51.07k stars 13.51k forks source link

bug: overlay animations jump forward near end in ios only #25369

Open liamdebeasi opened 2 years ago

liamdebeasi commented 2 years ago

Prerequisites

Ionic Framework Version

Current Behavior

Certain animations appear to "jump forward" when completing on iOS.

This may be easier to try on an actual device, but here is a video below to illustrate:

native ionic

Expected Behavior

I expect the animations to be smooth through the end of the animation.

Steps to Reproduce

  1. Open http://localhost:3333/src/components/action-sheet/test/basic?ionic:mode=ios in Safari (macOS or iOS).
  2. Click "Basic". Observe that the action sheet container translates in but appears to "jump forward" in the last few frames.

Code Reproduction URL

No response

Ionic Info

N/A

Additional Information

No response

liamdebeasi commented 2 years ago

This is due to a bug in WebKit, the engine that powers Safari and WKWebView: https://bugs.webkit.org/show_bug.cgi?id=241020

In the case of a Web Animation with 2 keyframes, applying the easing to the effect should produce the same visual result as applying the easing to the 0th keyframe. However, on WebKit applying the easing to the effect causes the animation to jump forward near the end.

This does not happen in Chrome or Firefox.


As a workaround, we can apply the easing to the 0th keyframe instead.

liamdebeasi commented 2 years ago

Effect easing and keyframe easing typically produce different results. Effect easing applies the easing to entire effect (over the total of n keyframes). Keyframe easing applies the easing from keyframe n to keyframe n+1.

However, in the case where there are only 2 keyframes total, the following:

square.animate([
  { offset: 0, transform: 'translateY(100vh)' },
  { offset: 1, transform: 'translateY(0vh)' }
], {
  duration: 1000, 
  easing: 'cubic-bezier(0.32,0.72,0,1)' 
});

should produce the same visual result as:

square.animate([
  { offset: 0, transform: 'translateY(100vh)', easing: 'cubic-bezier(0.32,0.72,0,1)' },
  { offset: 1, transform: 'translateY(0vh)' }
], {
  duration: 1000, 
});