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

[css-scroll-animations] Rethinking declarative syntax for scroll-linked animations #6674

Closed
fantasai opened this issue Sep 23, 2021 · 16 comments

Comments

@fantasai
Copy link
Collaborator

fantasai commented Sep 23, 2021

Overview

@mirisuzanne and I reviewed the Scroll-linked Animations ED this week. We have some concerns (which are at a high level similar to Robert O'Callahan's concerns with the initial proposals for “CSS Snap Points” that became the current CSS Scroll Snap Module):

  • The model seems to rely a lot on explicit offsets, rather than on element positions. Typically the reason for using offsets is to align with elements.
  • There is functionality to use element-based offsets, but these rely on ID selectors, which are not particularly portable across pages and makes it impossible for styles to be re-used in multiple places on the same page.

We'd like to propose a replacement of the declarative syntax in this module that addresses these problems. Note: This is part of our larger proposal rethinking various features for animation timelines and interpolation and how to fit them all together.

Our suggestion splits scroll-linked animation timelines into two types: those linked to the scroll timeline as a whole, and those linked to the portion of it in which a particular element participates.

Animations linked to scroll progress as timeline

These are animations whose timeline goes from 0% to 100% based on the scroll position of an ancestor scroll container.

animation-timeline: scroll()

The idea is to use an inline functional notation to identify the timeline using the animation-timeline property, expanding the property's syntax as follows:

animation-timeline: auto | scroll(<scroll-direction>? <scroller>?);
<scroll-direction> = block | inline | vertical | horizontal
<scroller> = root | nearest | <container-name>
  • auto is the initial value of animation-timeline, and behaves as animations have always done, triggering on application of the animation and lasting for the specified duration and delay.
  • nearest references the nearest ancestor scroll container
  • root references the main viewport scroller
  • <container-name> references the container-name property: it filters the lookup to the nearest scroll container with the given container-name. (See css-contain-3.)
  • The default direction is block; the default scroll container lookup is nearest

animation-timeline: NAME

Alternately, some scroll-timeline properties could be defined, which when applied to a scroll container would allow naming its timeline for a more indirect but reusable approach.

scroll-timeline-name: <custom-ident>#;
scroll-timeline-direction: <scroll-direction>#;
scroll-timeline: [<scroll-direction>? <custom-ident>]#;

Such named scroll timelines would be referenceable by name from the animation-timeline property.

animation-timeline: auto | <timeline-name>

Animations linked to view progress as timeline

Often animations are desired to start and end while a particular element is in view within the scroller. This timeline is essentially a snippet of the scroller's complete timeline, with 0% and 100% pinned to the moment the element comes into view and the moment it leaves view.

view-timeline-name: <custom-ident>
view-timeline-inset:  [ auto | <length-percentage> ]{1,4}
view-timeline-fit:  [ cover | contain | <percentage> ]
  • view-timeline-name - Specifies a name for this element's in-view timeline, so that it can be referenced by descendant and sibling elements as their animation-timeline.
  • view-timeline-inset - Specifies an inset (positive) or outset (negative) adjustment of the scrollport when considering whether the element is in view. auto indicates to use the value of scroll-padding. Initial value is zero. Percentages are with reference to the scrollport.
  • view-timeline-fit - Specifies whether the in-view timeline is measured from the moment any part of the box comes into view until all parts leave it (cover/0%) or whether it is measured from the moment all parts of the box come into view until any part leaves it (contain/100%). Initial value is 0% (cover).

Scope of Scroll-linked Timelines

The scope of a scroll-linked timeline (which elements can call it by name via animation-timeline) is defined as:

  • its descendants
  • its siblings (and their descendants)

This basically means its scope is attached to its parent, but the parent can't use it. In case of multiple timelines with the same name being in scope, the timeline associated with its nearest ancestor wins.

(It might also be useful to allow the scope to expand outside this parent, giving ancestors and far cousins access to the timeline across the document. In all cases the timeline reached via the closest ancestor should win in case of conflict. But this kind of global scoping from descendant elements might be difficult to implement.)

@bramus
Copy link
Contributor

bramus commented Sep 24, 2021

Having tinkered with Scroll-Linked Animations quite a lot (See here, here, here, here) I'd like to add some counterweight to the premise of this issue:


The model seems to rely a lot on explicit offsets, rather than on element positions. Typically the reason for using offsets is to align with elements.

I only found myself in need of explicit offsets is some typical yet always very simple situations:

  • Scroll for 100vh (e.g. cover page)
  • Scroll from top to bottom of the entire document (e.g. progress bar)

In all other cases I found myself grabbing to element-based offsets, as they're more dynamic.

Additionally, when absolute offsets are needed they are mostly built using relative units (instead of absolute units), which also keeps them somewhat dynamic.

(I know: the first linked to article uses them a lot but that's because of the structure of the two articles: only in the second article the concept of element-based offsets is introduced. If I were to rewrite some examples from the first article (such as the image gallery) I'd use element-based offsets for it, as such an implementation would adapt itself to the number of slides/images)


There is functionality to use element-based offsets, but these rely on ID selectors, which are not particularly portable across pages and makes it impossible for styles to be re-used in multiple places on the same page.

To me, the root cause here is the fact that selector() only works with ID selectors. If it were to behave like document.querySelector it'd open up lots more possibilities. When also supporting & (from CSS nesting) it'd even allow one to point to “self”, allowing re-use of created scroll-timelines.

This shortcoming of selector() — which was one of the first things that I documented about it — is being discussed in #5884 and by extension #5811

@mirisuzanne
Copy link
Contributor

The need for a selector() function at all stands out to me as an issue — even if it had the ability to select multiple elements. We already have a syntax for attaching properties to selectors (it's the core of the language), so why not use it here? Both scroll-containers and scrolled elements can be selected already, and could establish their timelines in the cascade, using normal CSS syntax. (This is the same reason I pushed against container-queries using a selector syntax, even one without the ID limitation).

@bramus
Copy link
Contributor

bramus commented Sep 26, 2021

Thanks for your feedback @mirisuzanne. It's great to see how you approach this from CSS up, whereas the spec as it stands right now was clearly written from a JS-first stance, and then translated to CSS.

If you don't mind, I'm gonna keep on asking questions here, to fully grasp how this adjusted proposal would work in practice.


The need for a selector() function at all stands out to me as an issue

I see that animation-timeline: scroll(…) accepts a container-name which identifies a container. To me it feels kinda the same, but simply shifted: you still need an identifier. Here in the form of a container-name, instead of an id (or any selector if it were to behave like document.querySelector) with selector().

However, I do see a reduced number of identifiers with this:

main { /* Identifier 1 */
  container-type: size;
  container-name: page-layout; /* Identifier 2 */
}

.foo {
  animation-timeline: scroll(block, page-layout); /* Identifier 2, again */
}

vs.

main { /* Identifier 1 */
  container-type: size;
  container-name: page-layout; /* Identifier 2 */
}

@scroll-timeline my-timeline {  /* Identifier 3 */
  source: selector(main);  /* Identifier 1, again */
  direction: block;
}

.foo {
  animation-timeline: my-timeline; /* Identifier 3, again */
}

That's one identifier less, so I guess that's a win there :)

On the other hand: when using a scroll-timeline-name — when the animated element is not a child of the scroll container — you'd also have three identifiers again:

main { /* Identifier 1 */
  container-type: size;
  container-name: page-layout; /* Identifier 2 */
  scroll-timeline-direction: block;
  scroll-timeline-name: main-scroller: /* Identifier 3 */
}

.foo {
  animation-timeline: scroller; /* Identifier 3, again */
}

As a sidenote: in the snippet above it feels weird to have three identifiers linked to one and the same element. Should more future additions to CSS also require a name, the list of properties with a set name would keep on growing.

Winging back to selector() (sorry, couldn't resist 😅):

main {
  container-type: size;
  scroll-timeline-direction: block;
}

@container selector(main) (block-size > 12em) {
  /* … */
}

.foo {
  animation-timeline: selector(main);
}

Feels much neater, no? (See this as an addendum/alternative syntax, not a replacement for giving names via the *-name properties)


We already have a syntax for attaching properties to selectors (it's the core of the language)

Of course, and we should embrace that where possible.

While working with scroll-timeline (the current version) I often found myself targeting three things. Some using a selector, others using selector():

  1. The scroll-container (using selector())
  2. The element(s) used in element-based offsets (using selector())
  3. The element that need to be animated. (using a selector)

See for example https://codepen.io/bramus/pen/QWGbOBQ which sports a horizontal scrolling section. The code targets:

  1. The viewport scroller as the scroll-container
  2. The #sectionPin as the element-based offset
  3. The .pin-wrap as the element that needs to be animated

With the adjustments, I guess this be the correct way to do it:

  1. Declare view-timeline-name: sectionpin on #sectionPin so that it's the tracked element (inside the main viewport scroller).
  2. Declare animation-timeline: sectionpin on .pin-wrap so that it animates while #sectionPin is visible inside its scroller.

That's correct?


view-timeline-name - Specifies a name for this element's in-view timeline, so that it can be referenced by descendant and sibling elements as their animation-timeline.

For the code example referenced above that does seem to work out (if my understanding is correct there) but for a typical ScrollSpy it would not. See a reduced https://codepen.io/bramus/pen/3d544d1a7866478fae98ef39cf4f9b7f demo where the navigation <nav> positioned underneath the slider #s is not a descendant of the slider #s itself.

  <div>
      <ul id="s">
         <li id="s1">Slide 1</li>
         <li id="s2">Slide 2</li>
         <li id="s3">Slide 3</li>
         <li id="s4">Slide 4</li>
      </ul>
   </div>
   
   <nav>
      <a href="#s1" id="d1">Go to Slide 1</a>
      <a href="#s2" id="d2">Go to Slide 2</a>
      <a href="#s3" id="d3">Go to Slide 3</a>
      <a href="#s4" id="d4">Go to Slide 4</a>
   </nav>

For example: as #s2 slides into its parent #s, the nav element #d2 is animated. With the scope limitation mentioned, declaring view-timeline-name: s2 on #s2 will only make that timeline available for descendants/siblings of #s2.

This would be something I'd miss to be honest.


Often animations are desired to start and end while a particular element is in view within the scroller.

Often it is also required to perform an animation as an element slides into the scroller, or slides out of it — e.g. not only while it is in view. See https://codepen.io/bramus/pen/oNYEgEQ for example where elements are animated as the enter/exit the scroller.

How would this be covered in this proposal?

I guess it has to do with the view-timeline-inset property that's being mentioned. However, it explicitly states that “Percentages are with reference to the scrollport.”, which makes this one not usable for this use-case.

The view-timeline-fit also doesn't seem to work out as — at least that's how I interpreted it — it either allows you to animate from the moment an edge touches the scrollport (”from the moment any part of the box comes into view”) or when the targetted element is entirely visible (contained) inside the scrollport ("from the moment all parts of the box come into view")

Or would one need a combination of both these properties?

Put differently: is it possible to target these 4 typical timelines?

  • #element is intersecting scrollport, even for the tiniest bit: (view-timeline-fit: cover I guess?)
  • #element is in between scrollport edges: (view-timeline-fit: contain I guess?)
  • #element is entering from bottom into scrollport: (?)
  • #element is exiting at top from scrollport: (?)

Must say I kinda liked the re-use of the threshold concept (from IntersectionObserver) in the current proposal which allows you to do this. If there's no way to do this in the adjusted proposal yet, perhaps view-timeline-inset could be extended to also accept a <number> that resembles the threshold?


view-timeline-fit - Specifies whether the in-view timeline is measured from the moment any part of the box comes into view until all parts leave it (cover/0%) or whether it is measured from the moment all parts of the box come into view until any part leaves it (contain/100%). Initial value is 100% (cover).

Is it possible that there's a typo there and that the very last words should be “… 100% (contain).”?

@fantasai
Copy link
Collaborator Author

@bramus Responding to a few of your comments...

when the animated element is not a child of the scroll container — you'd also have three identifiers again

Not quite, the scroll() function looks up to the nearest scroll container ancestor, it doesn't have to be the parent.

With the adjustments, I guess this be the correct way to do it [...]

Yes. Looking at your example, maybe we should have a way for an element to grab the nearest ancestor view-timeline, same as we do for the nearest ancestor scroll-timeline?

where the navigation

positioned underneath the slider #s is not a descendant of the slider #s itself

This seems like it ought to be a transitions demo rather than an animations demo, to be honest! Though I'm not sure how we can capture the state there to make it transition... a problem for another day, I guess.

We could make the view timelines global to the page, but we'd probably want to make sure that multiple view timelines that have the same name are prioritized by proximity (similar to how counters work).

Often it is also required to perform an animation as an element slides into the scroller, or slides out of it — e.g. not only while it is in view.

Interesting! We should definitely make that easily possible somehow. Have to think about that...

Is it possible that there's a typo there and that the very last words should be “… 100% (contain).”?

Good catch. Should be 0% (cover). But we're open to arguments in favor of a different default. :)

@andruud
Copy link
Member

andruud commented Sep 29, 2021

JS-first stance, and then translated to CSS

I support this rethink (I never liked selector() either), but if the CSS approach can't be expressed in terms of reasonable primitives shared with the JS API, this is going to be hard to both spec and implement.

BTW, what happens if we access Animation.timeline on a CSS-created scroll-linked animation? Will we create new interfaces separate from the current ScrollTimeline interface? (E.g. ViewTimeline).

view timelines global to the page

might be difficult to implement

On the one hand, this is Weird and New (boo!). On the other hand, the mode of operation for scroll-linked animations is to base the timeline state on layout information from the previous frame. Assuming we're sticking with that, global/broad names seem possible.

@flackr
Copy link
Contributor

flackr commented Sep 29, 2021

@andruud I think that we would change the JS interface as well to have ViewTimeline and ScrollTimeline as separate timeline types that can be constructed with arguments that roughly match the CSS syntax, except allowing specifying any element as the target as the JS API allows with ScrollTimeline today.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed Rethinking delarative scroll-based animations syntax, and agreed to the following:

  • RESOLVED: Adopt fantasai/miriam's new direction for the declarative side of scroll-linked animations
  • RESOLVED: Add flackr as editor to scroll-animations, move Majid to Former
The full IRC log of that discussion <TabAtkins> Topic: Rethinking delarative scroll-based animations syntax
<TabAtkins> github: https://github.com//issues/6674
<Rossen_> github: https://github.com//issues/6674
<TabAtkins> miriam: So we were looking at scroll-linked animations (sla) as part of a bigger thing
<TabAtkins> miriam: Was looking at container-based interpolations, and ideally being able to interpolate values *in* the cascade rather than juming out to animations which override everything
<fantasai> See full discussion at https://wiki.csswg.org/ideas/timelines ; this crosses multiple issues
<TabAtkins> miriam: current sla proposal seems JS focused and later ported to CSS, so we wanted to discuss this
<fantasai> (into which we posted pieces of that overal proposal)
<TabAtkins> miriam: Rethinking how we could make sure SLAs, already in prototype, fit into our future plans for animations/inteprolation
<TabAtkins> miriam: several parts
<fantasai> SLA = Scroll-Linked Animation
<TabAtkins> miriam: Animations based on where you are in the scroll position of a container
<TabAtkins> miriam: So 0% - 100%, linked to an animation progress
<TabAtkins> miriam: Proposal is thus for an animation-timeline property (already in the SLA proposal), defaults to auto, but can be given a scroll() function.
<TabAtkins> miriam: Defaults to looking at nearest scrollable ancestor, you can specify what direction you want to listen to.
<TabAtkins> miriam: And can opt to look at root scroller, or a given container's name (specified in another property)
<Rossen_> q?
<TabAtkins> miriam: An alt to that syntax is that instead of a scroll() function, could break out scroll-timeline-* properties
<fantasai> s/another property/container-name property, re-using from the @container query proposal/
<TabAtkins> miriam: Using the function in animation-timeline seemed like a an extensible way to do more timeline types in the same property
<TabAtkins> flackr: Is there a functional difference there? You could specify scroll() or a timeline name in animation-timeline?
<TabAtkins> miriam: right
<TabAtkins> flackr: So even if it is a function, we have the ability to specify other timeline types with a timeline name
<TabAtkins> miriam: So I think that's all handled by the larger proposal for SLA
<flackr> q+
<TabAtkins> miriam: Another thing - wanting to naimate on elements coming in or out of view. These element-based timelines work a little differently
<TabAtkins> miriam: Proposing view-timeline-* properties
<TabAtkins> miriam: view-timeline-name specifies the name of the timeline corresponding to the element coming in or out of view
<TabAtkins> miriam: view-timeline-inset to adjust how quickly it considers itself in view
<TabAtkins> miriam: and view-timeline-fit gives the baseline for where 0% is - start when they start to come into view, or start when fully into view
<TabAtkins> miriam: Also a question for if we need a timeline for that in-between space, between when it start to come into view and is fully in view
<TabAtkins> miriam: In terms of scroll-linnked timelines, were thinking these timelines would be visible to descendants and siblings; scope is attached to descendants of the parent
<TabAtkins> fantasai: One suggestion was to expose it to the whole document
<TabAtkins> fantasai: Coudl do this
<TabAtkins> fantasai: But it's important to resolve name conflicts with closer in the tree, not later in stylesheet or tree
<smfr> q+
<TabAtkins> fantasai: Multiple elements styled with the same timeline name should animate independently
<Rossen_> ack flackr
<fantasai> s/elements/subtrees/
<flackr> https://github.com//issues/4912
<TabAtkins> flackr: We did have use-cases initially with scroll-timeline where author wanted to specify for a certain numer of pixels of scroll (largely for parallax effects, but we had others)
<TabAtkins> flackr: I assume that to do this with the new proposal you'd need an element that corresponded to the number of pixels you want to animate over?
<TabAtkins> flackr: Because the scroll() function is always from 0 to max scroll
<TabAtkins> flackr: And specifying in terms of %s is dependent on layout which is complicated
<TabAtkins> miriam: I don't think we covered that use-case, we'll have to think abou tit
<TabAtkins> smfr: A few things
<TabAtkins> smfr: For animations that run when an element comes into view, there's subtlety what designers want
<Rossen_> ack smfr
<TabAtkins> smfr: Possibly some hysteresis; if you wobble it around the trigger point you want a looser organic feel
<TabAtkins> smfr: So feel designers will want more specific control
<TabAtkins> fantasai: I think you're mixing up scroll-linked and scroll-triggered animations
<TabAtkins> fantasai: SLA, the scroll position *is* the timeline. if you move the scroller it scrubs the animations backwards
<TabAtkins> fantasai: scroll-triggered is a timed animation that triggers when it comes into view
<TabAtkins> fantasai: the SLA in general is not targetting that use-case
<TabAtkins> smfr: I think I've seen examples that are scroll-linked but do have hysteresis
<TabAtkins> smfr: Some Apple pages have things that are linked to scroll position but still not precisely linked
<TabAtkins> smfr: Some time delay
<TabAtkins> flackr: I've seen this too, some examples of animations that are specifically lagged behind the timeline to provide some extra smoothness
<TabAtkins> smfr: So my problem is just making sure it's rich enough to address designer use-cases, or else they'll still just write JS
<TabAtkins> Rossen_: So are your examples addressed more if you have some sort of easing function that is part of the change?
<TabAtkins> Rossen_: So you have some easing between hops?
<TabAtkins> smfr: I don't think easing is enough; I think there has to be some time-based aspect
<smfr> https://www.apple.com/iphone-13-pro/
<TabAtkins> flackr: Could just be a lot of scroll triggers with easing...
<TabAtkins> smfr: Here's an example
<TabAtkins> smfr: There's an image showing the camera bumps, it's somewhat scroll-linked, but then it's sticky...
<TabAtkins> fantasai: We do have the *-inset property, which gives a *spatial* delay
<TabAtkins> flackr: Further down where it says "shoot it, cut it, ship it" seems to be the effect Simon's talkinga bout
<TabAtkins> Rossen_: So we can record Simon's concern in the issue, but I want to kepe the convo moving
<TabAtkins> smfr: Is it possible for an SLA to affect the size of the scrolled content, so we get circularity?
<TabAtkins> flackr: Yes, this has always been the case. We choose to handle it similar to :hover examples
<TabAtkins> flackr: You get the scroll position at the start of the frame, and if you change the size, the next frame will get an updated animation progress.
<TabAtkins> smfr: So that'll be specified in terms of the HTML event loop
<fremy> +1 to what florian just said, I was about to say the same
<fantasai> florian: ...
<fantasai> TabAtkins: Nothing in the spec to that effect, could have it flicker madly under the cursor
<Rossen_> q?
<florian> s/.../seems a little worse than :hover, as it can loop without further user interaction/
<TabAtkins> flackr: Back to the lag thing, we could have something like transitions where the animation progress eases over time rather than immediately updates. WE'd have to look into it
<fantasai> https://drafts.csswg.org/scroll-animations-1/
<TabAtkins> fantasai: Worth reminding that there is a SLA ED that was already adopted
<TabAtkins> fantasai: Our issue was just about redesigning how the declarative syntax works
<flackr> q+
<Rossen_> ack flackr
<fantasai> fantasai: question is, do we want to make changes going in this direction
<TabAtkins> flackr: Overall I think this is a nice change. There's some use-cases we shoudl consider that we might be dropping, but I'm supportive of this overall direction.
<TabAtkins> flackr: We should probably change the JS API to roughly match the proposed CSS timelines; view-timeline and scroll-timeline as things you con construct, but also keep the arbitrary-element selection that the JS API can allow
<Rossen_> ack flackr
<TabAtkins> fantasai: Yeah we didn't review the JS part at all; figured we'd hash this out and bring it back to JS
<fantasai> s/bring/then bring
<TabAtkins> Rossen_: Last comments or objections?
<TabAtkins> RESOLVED: Adopt fantasai/miriam's new direction for the declarative side of scroll-linked animations
<TabAtkins> RESOLVED: Add flackr as editor to scroll-animations, move Majid to Former
<TabAtkins> [other editors are all still fine]
@bramus
Copy link
Contributor

bramus commented Sep 29, 2021

As pointed out by @andruud and @flackr above I also think it's preferable to keep the JS and CSS interfaces tied to each other. Right now this is the case: if you know how one works you also know the other, as they share the same concept/approach.


@fantasai Regarding those "element slides into view"-animations.

Note that it can also be about a portion of that element, e.g. "only start when a quarter into view" or "be finished when 75% in view". I've done so in https://www.bram.us/2021/03/04/the-future-of-css-scroll-linked-animations-part-2/#demos--revealing-images for example where the animation runs from 50% into view to 100% into view.

Above that several animations can be attached to several phases of a timeline. See https://www.bram.us/2021/03/04/the-future-of-css-scroll-linked-animations-part-2/#demos--contact-list-revisited for example, where there are different enter and exit animations on the same element.

💡 To implement this it looks like a good idea to me to have one shared view-timeline for an element, and then be able to tap into parts of that view-timeline (e.g. the enter and exit phases) to hook animations on.

I'm thinking of two new properties here:

  • animation-timeline-phase to define which part of the view-timeline you want attach your animation on.
    • Values are enter, exit and auto (= for the entirety of the view-timeline, with respect to view-timeline-fit)
  • animation-timeline-thresholds to define the element-based thresholds for running the animations.
    • As an author you can opt to set two thresholds, e.g. 25% 80%.
      • Both percentages indicate "how much percent of the element should be in view".
      • The first one is the start threshold (e.g. how much percentage does the element need to be in view before the animation can start)
      • The second one is the end threshold (e.g. when the element in that much percentage in view the animation must be finished).
    • Default value is auto.
      • For enter phases that translates to 0% 100% (i.e. from element not in view and touching start edge to element entirely in view).
      • For exit phases that translates to 100% 0% (i.e. from element entirely in view and touch end edge to element entirely out of view).

The adjusted syntax for the https://www.bram.us/2021/03/04/the-future-of-css-scroll-linked-animations-part-2/#demos--contact-list-revisited example would then become:

@keyframes animate-in {
   …
}

@keyframes animate-out {
   …
}

ul {
  overflow-y: scroll;
}

li {
  view-timeline: li-in-ul;
  view-timeline-fit: cover;
}

li > * {
  animation:
    animate-in 1s linear forwards,
    animate-out 1s linear forwards;
  animation-timeline: 
    li-in-ul, 
    li-in-ul;
  animation-timeline-phase: 
    enter,
    exit;
  animation-timeline-thresholds:
    25% 100%, 
    100% 25%;
}

In this example:

  • The animate-in animation would run during the enter phase of the li-in-ul timeline. The animation will start when the watch element (the li) is 25% in view at the start edge and be finished when the watched element is entirely in view.
  • The animate-out animation would run during the exit phase of the li-in-ul timeline. The animation will start when the watch element (the li) is 100% in view at the exit edge (e.g. about to leave) and be finished when the watched element is 75% out of view.

🤔 Braintwist: If these new properties would exist, is view-timeline-fit: cover; still feasible as we're basically delegating that responsibility to animation-timeline-phase? animation-timeline-phase could in that case be extended to accept the values cover and contain, deprecating view-timeline-fit.

🤔 Braintwist on the braintwist: Sayanimation-timeline-phase would be extended as suggested in the braintwist above and a user wants to do something like "run the animation from the moment the element is entering the scrollport until it is halfway in the scrollport", then the enter/exit/cover/contain keywords won't do.

In that case I'm sliding back to a syntax using an edge and a percentage, which is awfully similar to <element-offset> from the current proposal:

animation-timeline: some-element-in-a-scroll-container;
animation-timeline-phase-start: end 0%; /* Start animation when the watched element is sitting at the end edge, with 0% in view*/
animation-timeline-phase-end: 50%; /* Have animation be finished by the time the watched element is in the middle of the scroll-container*/

To combine these in the animation-timeline-phase shorthand a separator other than , would be needed.

Thinking of / right now (e.g. animation-timeline-phase: end 0% / 50%;) as using a space (e.g. animation-timeline-phase: end 0% 50%;) makes it somewhat weird: the first percentage is a threshold but the second percentage is a progression of the view-timeline.

(Apologies for freewheeling here. My mind is playing ping-pong with itself when it comes to Scroll-Linked Animations 😅)


Looking a bit broader, I think it's advised to also keep Scroll-Triggered Animations in mind while figuring out the syntax for Scroll-Linked Animations [edit: it is], so that we don't hit a wall when they are being considered. I'm seeing options with something like animation-timeline-type: trigger here, also being to leverage the proposed animation-timeline-phase if need be.

@smfr
Copy link
Contributor

smfr commented Sep 30, 2021

Here's a good example where animations a kinda-scroll-linked but not directly: https://codepen.io/isladjan/pen/abdyPBw

moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this issue Oct 1, 2021
…le system. r=emilio

This patch adds the animation-timeline longhand property. For
shorthand, we will do that in the next patch.

This patch includes the aut-generated code in
devtools/shared/css/generated/properties-db.js, by `./mach devtools-css-db`.

Note:
1. we will use this property in Bug 1676791. For now, only make sure
   we parse it and serialize it correctly.
2. The syntax of animation-timeline may be updated, based on the spec
   issue: w3c/csswg-drafts#6674.
   However, it's not a big problem to update it later, so we still can
   prototype this property based on the current version of spec.

Differential Revision: https://phabricator.services.mozilla.com/D126450
jamienicol pushed a commit to jamienicol/gecko that referenced this issue Oct 4, 2021
…le system. r=emilio

This patch adds the animation-timeline longhand property. For
shorthand, we will do that in the next patch.

This patch includes the aut-generated code in
devtools/shared/css/generated/properties-db.js, by `./mach devtools-css-db`.

Note:
1. we will use this property in Bug 1676791. For now, only make sure
   we parse it and serialize it correctly.
2. The syntax of animation-timeline may be updated, based on the spec
   issue: w3c/csswg-drafts#6674.
   However, it's not a big problem to update it later, so we still can
   prototype this property based on the current version of spec.

Differential Revision: https://phabricator.services.mozilla.com/D126450
@ogerchikov
Copy link
Collaborator

@fantasai, @bramus

We could make the view timelines global to the page, but we'd probably want to make sure that multiple view timelines that have the same name are prioritized by proximity (similar to how counters work).

I stand with a need to allow for global scroll timelines. We have a use case in mind that synchronizes scroll position of a scroller with transform of an element which is not an ancestor of the scroller.

@flackr
Copy link
Contributor

flackr commented Oct 18, 2021

Here's a good example where animations a kinda-scroll-linked but not directly: https://codepen.io/isladjan/pen/abdyPBw

@smfr For these use cases I propose we properties similar to transition timing functions (transition-duration, transition-timing-function, transition-delay) which will specify a delay over which the scroll input will plug in to the timeline.

E.g. for view-timeline:

view-timeline-transition-duration: <time>
view-timeline-transition-timing-function: <easing-function>
view-timeline-transition-delay: <time>

Then for the above use case the developer would simply specify a view-timeline-transition-duration: 2s and the position of the animation time would smoothly move towards the scroll position over 2s.

@mirisuzanne
Copy link
Contributor

I'm not clear what it means to have a duration that is simultaneously based on a scroll-timeline and a set time? How are those meant to interact in your proposal?

@flackr
Copy link
Contributor

flackr commented Oct 18, 2021

The duration in my proposed property is the length of time it takes the scroll based timeline to catch up to scroll position changes. Here's an example hacked together with javascript using a custom property transition to represent the delayed transition value being plugged in to the timeline:

https://jsbin.com/qekodug/edit?css,js,output

@bramus
Copy link
Contributor

bramus commented Jan 31, 2022

What are the plans for further pursuing this new syntax?

I'm definitely open to all these changes, but with the Chromium Implementation breaking (and not being fixed) I'm hoping to pick up the new syntax in due time.

(And, truth be told, I'm currently cancelling speaking engagements on the @scroll-timeline syntax as it seems irrelevant to spread word about a syntax that will soon(ish) be outdated)

Happy to join a session to exchange additional ideas (such as the proposed animation-timeline-phase above) and help form the new syntax.

@BorisChiou
Copy link
Contributor

BorisChiou commented Jan 31, 2022

Same question as @bramus asked. I'm also waiting for the update of the syntax in the spec.

Due to this spec issue, I implemented @scroll-timeline only partially in Firefox because I expect some features, like scroll-offsets, will be obsolete soon. And so only default values of the descriptors (in @scroll-timeline) are supported in Firefox (behind a preference flag) because I'm not sure how many things we have to update after we change the spec. I am also happy to do join any discussion for more information. Thanks. :)

@fantasai
Copy link
Collaborator Author

fantasai commented Feb 15, 2022

I'm working on rewriting the spec with @flackr's help in the rewrite.bs file in the repo; I'll propose replacing the existing draft once it's a bit more complete.
https://drafts.csswg.org/scroll-animations-1/rewrite

I've also opened up some follow-up issues:

Please redirect your specific comments there, I'll keep this one open for switching up the overall direction and close it out once we're ready to fold in those edits. :) Thanks!!

Loirooriol pushed a commit to Loirooriol/servo that referenced this issue May 30, 2023
This patch adds the animation-timeline longhand property. For
shorthand, we will do that in the next patch.

This patch includes the aut-generated code in
devtools/shared/css/generated/properties-db.js, by `./mach devtools-css-db`.

Note:
1. we will use this property in Bug 1676791. For now, only make sure
   we parse it and serialize it correctly.
2. The syntax of animation-timeline may be updated, based on the spec
   issue: w3c/csswg-drafts#6674.
   However, it's not a big problem to update it later, so we still can
   prototype this property based on the current version of spec.

Differential Revision: https://phabricator.services.mozilla.com/D126450
Loirooriol pushed a commit to Loirooriol/servo that referenced this issue May 30, 2023
This patch adds the animation-timeline longhand property. For
shorthand, we will do that in the next patch.

This patch includes the aut-generated code in
devtools/shared/css/generated/properties-db.js, by `./mach devtools-css-db`.

Note:
1. we will use this property in Bug 1676791. For now, only make sure
   we parse it and serialize it correctly.
2. The syntax of animation-timeline may be updated, based on the spec
   issue: w3c/csswg-drafts#6674.
   However, it's not a big problem to update it later, so we still can
   prototype this property based on the current version of spec.

Differential Revision: https://phabricator.services.mozilla.com/D126450
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment