Skip to content
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

[web-animations-2][css-animations-2] What to do with keyframe offsets outside of animations. #7825

Closed
flackr opened this issue Oct 4, 2022 · 9 comments

Comments

@flackr
Copy link
Contributor

flackr commented Oct 4, 2022

In #7044 we #7044 (comment) to add phase linked offsets to the animations APIs. This allows tying both the animation start/end and keyframes to particular parts of the overall timeline time. However, this also allows accidentally specifying keyframe offsets which are before 0% or after 100% of the animation, e.g.

@keyframes fade-in-animation {
  enter 0% { opacity: 0; }
  enter 100% { opacity: 1; }
}
.elem {
  animation: fade-in-animation;
  animation-timeline: view-timeline;
  animation-delay: enter 50% enter 100%;
}

The above is obviously a mistake, though you could imagine cases if lengths or calculations are allowed (e.g. #7575) we could end up in this situation dynamically in more legitimate incidental cases, e.g.

@keyframes fade-in-animation {
  /* When 20% of the enter phase is
   * less than 50px this will be < 0% progress. */
  enter 20% { opacity: 0; }
  enter 100% { opacity: 1; }
}
.elem {
  animation: fade-in-animation;
  animation-timeline: view-timeline;
  animation-delay: enter 50px enter 100%;
}

I can think of 4 options for how to handle this:

  1. Treat keyframes resolving outside of 0 - 100% as an error. This is what is specified in css-animations-1:

    Values less than 0% or higher than 100% are invalid and cause their <keyframe-block> to be ignored.

    and is also specified by web-animations-1:

    Specifying an offset outside the range [0.0, 1.0] will cause a TypeError to be thrown.

    However, if we allow length calculated offsets, it will not be possible at animation construction time to know whether a future size of scrolling box will result in an invalid keyframe offset. Cancelling the animation seems dangerous as this state would often be ephemeral. Naively, we could simply skip the animation when this happens (the same as if the timeline was currently inactive). This could result in sudden flips in visible styles when box sizes pass this threshold.

  2. Allow animation offsets outside of 0 - 100% and interpolate from their offset. These would not change the timing of the animation but at 0% animation progress we would interpolate between the keyframes. E.g. if a -50% keyframe specified opacity: 0 and a 50% keyframe specified opacity: 1 then at 0% progress when the animation starts it would produce opacity: 0.5. This would result in a continuous effect as the box size changed and probably matches developer expectations.

    To do this we would have to change the implicit from and to keyframes to only exist when we don't have a computed keyframe <= 0% or >= 100%. Relevant text of css-animations-1 is:

    If a 0% or from keyframe is not specified, then the user agent constructs a 0% keyframe using the computed values of the properties being animated. If a 100% or to keyframe is not specified, then the user agent constructs a 100% keyframe using the computed values of the properties being animated.

    The relevant text of web-animations-1:

    9. If there is no keyframe in property-specific keyframes with a computed keyframe offset of 0, create a new keyframe with a computed keyframe offset of 0, a property value set to the neutral value for composition, and a composite operation of add, and prepend it to the beginning of property-specific keyframes.
    10. Similarly, if there is no keyframe in property-specific keyframes with a computed keyframe offset of 1, create a new keyframe with a computed keyframe offset of 1, a property value set to the neutral value for composition, and a composite operation of add, and append it to the end of property-specific keyframes.

    The challenge is that when this condition occurs is dynamic so these 0% / 100% keyframes may be needed some of the time and not other times.

  3. Clamp calculated animation offsets outside of 0 - 100% to 0 - 100%. This is similar to the above except that when an animation offset would resolve outside of 0 - 100% it is computed to 0 or 100%. E.g. if a -50% keyframe specified opacity: 0 and a 50% keyframe specified opacity: 1 then at 0% progress when the animation starts it would produce opacity: 0 as the -50% keyframe would have been clamped to 0%. This would result in a continuous effect as box sizes change, but the compressing of effects likely doesn't match developer expectations.

  4. Skip keyframes with offsets outside of 0 - 100%. When a keyframe offset computes outside of 0 - 100%, exclude that keyframe. E.g. if a -50% keyframe specified opacity: 0 and a 50% keyframe specified opacity: 1 then it would be as if only the 50% keyframe were specified, so at 0% progress when the animation starts it would produce the underlying opacity through the implicit keyframe constructed by css-animations-1 or web-animations-1.

My proposal would be to go with option 2 as I believe it leads to the least developer surprise and allows the effect to be continuous even as block sizes change. This is also consistent with the logic from #7432 where we resolved to not clamp the view timeline progress range to the actually scrollable range.

@bramus
Copy link
Contributor

bramus commented Oct 4, 2022

+1 for option 2

@ydaniv
Copy link
Contributor

ydaniv commented Oct 5, 2022

I suppose we expected developers to omit animation-delay from the corresponding declarations altogether, right?

Then what would be the expected behavior for @keyframes offsets that are "contained" within the declared animation-delay? E.g:

@keyframes fade-in-animation {
  contain 0% { opacity: 0; }
  contain 100% { opacity: 1; }
}
.elem {
  animation: fade-in-animation;
  animation-timeline: view-timeline;
  animation-delay: enter 50% exit 50%;
}

Does the animation-delay here overrides the offsets? Vice versa?
Isn't having both problematic regardless of the values?
If so, then is it really worth the trouble to try and fix it?

I don't think I'd expect neither clamping nor mapping the values of one to the other.
Perhaps a more expected behavior would be that of a cascading nature, meaning, that one overrides the other. Because I also don't see how allowing both can provide any more value to the author.

@fantasai
Copy link
Collaborator

I think the most expected thing to do is to pin the keyframes to wherever it is they declare themselves to be, i.e. option 2. That is, raw percentages are relative to the animation duration, but absolute keyframe locations aren't.

To address @ydaniv's case, once all the frames are pinned onto the timeline, we identify the start and endpoints of the animation, and if they are outside the range of the first/last frames, we pin the neutral value. And then wherever we don't have a value, we interpolate among these pinned values.

@birtles
Copy link
Contributor

birtles commented Oct 31, 2022

At first I was a little bit concerned about option 2 because:

  1. Web compatibility. Changing the conditions under which implicit keyframes are generated seems a bit scary. I was wondering if it would it make any sense to limit the scope of this change to when these enter-like keywords are involved but maybe it's fine as-is? Perhaps the bigger concern is JS code that is expecting out-of-range offsets to throw?
  2. Group effects, specifically sequence effects. I was concerned about multiple effects being active at once but provided the child effects still don't have any effect outside their active interval, maybe it's fine?

@flackr
Copy link
Contributor Author

flackr commented Oct 31, 2022

  1. Web compatibility. Changing the conditions under which implicit keyframes are generated seems a bit scary. I was wondering if it would it make any sense to limit the scope of this change to when these enter-like keywords are involved but maybe it's fine as-is?

I was thinking that even with option 2, it would still not be allowed to have regular percentage keyframes outside of 0-100% which would mean that no existing animations would hit this. As such, this would only affect new syntaxes like keyframes linked to scroll offsets (or in the future possibly absolute times) where they may dynamically fall outside of the animation's 0 - 100% range.

Perhaps the bigger concern is JS code that is expecting out-of-range offsets to throw?

These could continue to throw given regular percentage keyframes which are known to be outside of the animation bounds.

  1. Group effects, specifically sequence effects. I was concerned about multiple effects being active at once but provided the child effects still don't have any effect outside their active interval, maybe it's fine?

That's right, outside of the active interval it would still use the fill behaviour so it should be able to be sequenced with no issue.

@fantasai
Copy link
Collaborator

fantasai commented Nov 7, 2022

Proposed Resolution: Adopt option 2, allowing keyframes to be pinned outside the 0%-100% range and interpolating from them to find 0% and 100%. Keyframes specified at < 0% or > 100% remain invalid.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed Keyframe offsets outside of the animations, and agreed to the following:

  • RESOLVED: Accept proposal to allow keyframes outside of the animation's duration.
The full IRC log of that discussion <TabAtkins> Topic: Keyframe offsets outside of the animations
<TabAtkins> github: https://github.com//issues/7825#issuecomment-1306090794
<TabAtkins> Rossen_: Proposed resolution linked in the agenda
<TabAtkins> flackr: with sla, especiallyw ith dynamicly defined keyframes, you can end up with offsets outside the range
<TabAtkins> flackr: Proposed solution is to allow these. They don't change the animation's active duration, but their effects still apply and just get clipped to the end of the animation duration.
<TabAtkins> flackr: And allow it for classic animations too (negative %, >100%)
<TabAtkins> +1 to this
<TabAtkins> we allow overshoot in other places like gradients
<TabAtkins> RESOLVED: Accept proposal to allow keyframes outside of the animation's duration.
<TabAtkins> s/And allow/But don't allow/
<TabAtkins> s/animations too/animations, due to possible compat/

@fantasai
Copy link
Collaborator

fantasai commented Dec 7, 2022

@flackr I drafted a PR at #8202 but... I'm not sure if I got the terminology right, or if this is enough to define what we resolved on.

@flackr
Copy link
Contributor Author

flackr commented Mar 10, 2023

Fixed by #8202

@flackr flackr closed this as completed Mar 10, 2023
fantasai added a commit to fantasai/csswg-drafts that referenced this issue Apr 11, 2023
fantasai added a commit to fantasai/csswg-drafts that referenced this issue Apr 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants