flackr / scroll-timeline

A polyfill of ScrollTimeline.
Apache License 2.0
909 stars 87 forks source link

ScrollTimeline offsets are no longer respected after last update. #64

Closed adaptifyDesigns closed 11 months ago

adaptifyDesigns commented 2 years ago

@flackr @bramus @kevers-google

After the latest update (#60) the ScrollTimeline -> scrollOffsets are no longer honored. No matter how I configure the animation bounding offsets, either by element or by static css units, the animation timeline is based on the length of the page, NOT the offsets.

I don't know what to do about this, and all my animations are broken.

Please, if anyone can help me out I would really appreciate it.

Here is an example animation I'm using:

element.animate(
  [
    { clipPath: 'inset(0% 60% 0% 0%)' },
    { clipPath: 'inset(0% 0% 0% 0%)', offset: 0.46 },
    { clipPath: 'inset(0% 0% 0% 0%)', offset: 0.46 },
    { clipPath: 'inset(0% 60% 0% 0%)' }
  ],
  {
    duration: 1,
    easing: 'cubic-bezier(0,.44,1,.57)',
    fill: "both",
    timeline: new ScrollTimeline({
        scrollSource: document.documentElement,
        scrollOffsets: [
          { target: element, edge: 'end', threshold: 0 },
          { target: element, edge: 'start', threshold: 0 }
        ],
        fill: 'both',
    }),
  }
);

The goal is to have the above keyframes start as the top of the element first comes into the bottom of the viewport, and end as the bottom of the element leaves the top of the viewport. This was working as expected up until recently, but now it is not.

How do I remedy this? Thanks in advance!

bramus commented 2 years ago

Scroll Offsets have recently been deleted, as those are no longer part of the spec. This polyfill stays in sync with spec changes, hence it being deleted from the polyfill as well in commit 4b78bb8cc55d5323da76dc92243bc0c3e629ad95.

I’m assuming you are directly linking to the contents of the repo and loading the polyfill from there. This is not a good idea, as breaking changes can occur (due to the spec still being in flux). Best is to make a local copy of a specific version that you want to use.

For you, that would mean a commit before 4b78bb8cc55d5323da76dc92243bc0c3e629ad95, which is 3063e156535f3ab1ffc8a4000ffdd3290232c121. You can browse the repository at that point through this link: https://github.com/flackr/scroll-timeline/tree/3063e156535f3ab1ffc8a4000ffdd3290232c121. Download the built file from that link and use that version.

If you do still want to load that file from a CDN, GitHack – amongs other similar services – supports commit IDs in the URLs. The direct link to the file would then be https://rawcdn.githack.com/flackr/scroll-timeline/3063e156535f3ab1ffc8a4000ffdd3290232c121/dist/scroll-timeline.js

adaptifyDesigns commented 2 years ago

@bramus Thank you so much for this suggestion. Much appreciated.

I would also be interested in learning how to create the equivalent of scrollOffsets in the most up to date version of the spec polyfill. What has it been replaced by? Where can I learn how to use it?

Also, why is it, do you think, that I have been unable to get the ViewTimeline syntax to work? That seems to be ignored by the up-to-date script. Is this also a remnant from a past version that the demo page is still using?

Thank you in advance for your help!

bramus commented 2 years ago

It should work. I have a bunch of demos that's using it up at https://codepen.io/collection/jbwpJB

adaptifyDesigns commented 2 years ago

@bramus Thanks again for your response, and for the link to those demos.

I still can't figure out how to work with ViewTimeline... I'm using the latest version of the script, and my animation config is below (I commented out the previous ScrollTimeline version):

let $animateClipPathReveals = document.querySelectorAll('.animate-clip-path-reveal');
if( $animateClipPathReveals.length )
{
  $animateClipPathReveals.forEach( ( $reveal ) => {

    $reveal.animate(
      [
        { clipPath: 'inset(0% 60% 0% 0%)' },
        { clipPath: 'inset(0% 0% 0% 0%)', offset: 0.46 },
        { clipPath: 'inset(0% 0% 0% 0%)', offset: 0.46 },
        { clipPath: 'inset(0% 60% 0% 0%)' }
      ],
      {
        timeline: new ViewTimeline({
          source: document.documentElement,
          subject: $reveal,
          axis: 'vertical'
        }),
        timeRange: 'enter', 
      }
      // {
      //   duration: 1,
      //   easing: 'cubic-bezier(0,.44,1,.57)',
      //   fill: "both",
      //   timeline: new ScrollTimeline({
      //       scrollSource: document.documentElement,
      //       scrollOffsets: [
      //         { target: $reveal, edge: 'end', threshold: 0 },
      //         { target: $reveal, edge: 'start', threshold: 0 }
      //       ],
      //       fill: 'both',
      //   }),
      // }
    );
  });
}

When I switch to the ViewTimeline version, it doesn't matter which timeRange keyword I use, the animation does not occur at all. None. I do see one console error that says:

"DevTools failed to load source map: Could not load content for chrome-extension://gighmmpiobklfepjocnamgkkbiglidom/browser-polyfill.js.map: System error: net::ERR_FILE_NOT_FOUND"

But I don't think this is related (is it?). I included the map file in the same directory as the script, nonetheless. There are no other console errors.

Since the above ViewTimeline object matches the syntax exactly from one of your demos, and I'm using the latest version of the polyfill script, I'm completely stumped as to why the animation isn't working at all. It's not that it's partially working... just nothing. Any ideas?

Also I'd like to get more familiar with the ViewTimeline object options, but I don't see any mention of the enter, exit, contain keywords in the spec. I also don't see any mention of the ScrollTimeline being removed... am I looking at the correct spec?

Where can I learn more about this? And how can I troubleshoot the ViewTimeline method?

Thank you so much for your assistance.

adaptifyDesigns commented 2 years ago

@bramus

UPDATE: I've been playing around with ViewTimeline, and the problem seems to be that it requires a source to be defined other than document.documentElement. It has to be a div with overflow-y: scroll; defined.

I noticed you have a demo that uses ViewTimeline without defining a source... the Polar Bear demo... I have copied this demo exactly into my sandbox page (scroll to the bottom of the page), and the animation does not function.

What could be different about my environment that would prevent the animation from recognizing the document window as the view source? Can you see anything on the page that would be causing this?

kevers-google commented 2 years ago

timeRange has not been fully specced as of yet and the name may change. the Polyfill is a bit ahead of the spec in order to aid in driving the spec discussion.

The proposed ranges/phases are: cover: From when the element first overlaps the viewport to when the element no longer overlaps the viewport. enter: From when the element first enters the viewport until it is fully within the viewport. contain: The range of scroll offsets where the element is fully contained within the viewport. exit: From when the element starts to leave the viewport until totally outside of the viewport.

I am hopeful that the naming will be fully resolved shortly within the CSS working group.

From reviewing the script, I see two minor issues, which may be contributing to the problem. 'source' is not a ViewTimeline option. Instead, 'source' is inferred from the subject as the nearest scroll container ancestor. There is a TODO in the code as the code for finding the scroll ancestor is not 100% correct. This might be the issue. Try removing 'source' as an option, and if that doesn't fix the problem. Try adding the following JavaScript.

const timeline = document.getAnimations()[0].timeline; // assuming all animations are driven by ViewTimelines. console.log(timeline.source);

If this is null/undefined, that would suggest that 'source' is not being correctly calculated. If you have a minimum test case that you can share, I'd be happy to author a web platform test to ensure that the configuration is properly tested.

The second issue is that 'fill' is missing as an animation option.

As Bramus mentioned, it may be safest to avoid using the tip of tree build, and instead roll back to an earlier version of the library. The spec is in a state of rapid development at the moment and is likely to incur a few breaking changes over the next few months.

adaptifyDesigns commented 2 years ago

@kevers-google Thank you for all this clarification.

I have setup a simple page in my sandbox environment which has two demos. One in which the source is defined as a div with overflow-y: scroll, and another in which no source is defined. They are both demos copied directly from Bramus. Both of the demos function correctly in Bramus' demos, but only one of them functions properly in my dev environment, the one without a defined source.

Here is the javascript for the page:

const $listView = document.querySelector('#list-view');
const $listItems = document.querySelectorAll('#list-view > ul > li');

$listItems.forEach(($listItem) => {
    $listItem.animate(
        {
            opacity: [0, 1],
            transform: ["translateX(-100%)", "translateX(0)"],
        },
        {
            timeline: new ViewTimeline({
                source: $listView,
                subject: $listItem,
                axis: 'vertical'
            }),
            timeRange: 'enter',
        }
    );
});

const $images = document.querySelectorAll(".revealing-image");
$images.forEach(($image) => {
    $image.animate(
        {
            opacity: [0, 1],
            clipPath: ['inset(45% 20% 45% 20%)', 'inset(0% 0% 0% 0%)']
        },
        {
            timeline: new ViewTimeline({
                subject: $image,
                axis: 'vertical',
            }),
            timeRange: 'enter',
      fill: 'both',
        }
    );
});

const timeline = document.getAnimations()[0].timeline; // assuming all animations are driven by ViewTimelines.
console.log(timeline.source);

As you suspected, timeline.source returns as undefined in the console.

I supposed Bramus' Polar Bear demo works in Codepen because the scrolling ancestor is more clearly defined, whereas on a production webpage, the document body/window is not...?

I would love to hear what you find.


Regarding the spec and the rapidly changing polyfill... I have taken Bramus' advice and I'm now using a previous version of the script for my production site. The animations are working as intended. Thanks!

kevers-google commented 2 years ago

Found a bug in getScrollParent that breaks for your use case where document.scrollingElement should be used. This element will have overflow visible by default, but is still a valid scroll container. This is the exception to the rule. Should have a patch shortly.

kevers-google commented 2 years ago

https://github.com/flackr/scroll-timeline/pull/65

adaptifyDesigns commented 2 years ago

@kevers-google

Great. I really appreciate you looking into this.

So now, what would the correct ViewTimeline syntax be for the Polar Bear demo in my use case?

I.e. should I use source: document.documentElement, or omit source altogether, or something else?

Thanks again.

kevers-google commented 2 years ago

As source is not a ViewTimeline option (it's a readonly property of the timeline), it should be omitted to avoid potential confusion. The correct syntax has not been finalized, but should be fully specced out shortly. The rules for source and subject are pretty much resolved, but properties like insets and whatever we end up calling the time ranges is still unresolved.

bramus commented 1 year ago

I think this issue may be closed, as it’s about an old feature that’s no longer part of the spec.

benderillo commented 1 year ago

@bramus if scrollOffsets no longer part of the spec. Is there any place explaining what is now part of the spec that allows for similar use? Say I previously could set the offsets to 10% to 90% and do animation only for that part of the whole scrolling. What's the new way of achieving this?

Also, the readme in this repo is misleading by still containing example code that uses the offsets.

kevers-google commented 1 year ago

scrollOffsets are being replaced by the CSS property animation-range (drop the 'animation-' prefix when using the JS API). Currently, animation-range is wired up for view timelines, and they should soon be available for non-view scroll-timelines. The polyfill implementation is also a bit out of date given recent spec changes. I hope to free up some time to work on the polyfill soon. In the meantime, a more complete implementation is available in Chrome canary (requires enabling experimental-web-platform-features). Over the coming months, the gap will be closed between the polyfill and native implementations. Not enough hours in the day. :)

There are two compelling reasons for the API change. Firstly, it moves the active range from being a timeline property to an animation property and facilitates being able to attach multiple animations with differing ranges to the same timeline. Secondly, scrollOffsets did not have a clear CSS counterpart. With the redesign, you can do things like:

.animated {
  animation-name: anim;
  animation-duration: auto;
  animation-timeline: scroll(nearest block);
  animation-range:  10% 90%;
}

Naturally, the JavaSscript approach via element.animate will also be supported.

Side note: The 'enter' range was renamed 'entry'. Not sure if the name change is reflected in the polyfill yet. Another relatively recent spec change. Spotted in scrolling back through the comments.

benderillo commented 1 year ago

Side note: The 'enter' range was renamed 'entry'. Not sure if the name change is reflected in the polyfill yet. Another relatively recent spec change. Spotted in scrolling back through the comments.

I skimmed through the source code and it seems like the naming was not updated. https://github.com/flackr/scroll-timeline/blob/29b7b6e1be4bb40192f987d7c001f6e2910e0912/src/proxy-animation.js#L11

Also, I may not be reading it right, but only delay and endDelay can be set in JS at the moment (in a syntax that is undocumented anywhere but in the examples on the demo page. Perhaps I am not looking in the right place...

kevers-google commented 1 year ago

Alas, delay and endDelay was a previously explored and proposed way of setting the active range for the animation. Ultimately, the CSSWG rejected that approach in favor of animation-range.

bramus commented 11 months ago

@benderillo https://developer.chrome.com/articles/scroll-driven-animations/ holds all the info you need.

adaptifyDesigns commented 11 months ago

Scroll Offsets have recently been deleted, as those are no longer part of the spec. This polyfill stays in sync with spec changes, hence it being deleted from the polyfill as well in commit 4b78bb8.

I’m assuming you are directly linking to the contents of the repo and loading the polyfill from there. This is not a good idea, as breaking changes can occur (due to the spec still being in flux). Best is to make a local copy of a specific version that you want to use.

For you, that would mean a commit before 4b78bb8, which is 3063e15. You can browse the repository at that point through this link: https://github.com/flackr/scroll-timeline/tree/3063e156535f3ab1ffc8a4000ffdd3290232c121. Download the built file from that link and use that version.

If you do still want to load that file from a CDN, GitHack – amongs other similar services – supports commit IDs in the URLs. The direct link to the file would then be https://rawcdn.githack.com/flackr/scroll-timeline/3063e156535f3ab1ffc8a4000ffdd3290232c121/dist/scroll-timeline.js

@bramus Hello. I noticed today that the above mentioned githack link is now referencing a different version. Unfortunately all my animation ranges and offsets are broken on an old site... do you know what might have happened? And how I might be able to reference the version that was working for me up until a few weeks ago?

This is the link I have been using: https://rawcdn.githack.com/flackr/scroll-timeline/3063e156535f3ab1ffc8a4000ffdd3290232c121/dist/scroll-timeline.js

bramus commented 11 months ago

The spec and syntax have changed. The polyfill now supports this syntax. See https://developer.chrome.com/articles/scroll-driven-animations/ for up-to-date info.

If you want to use a specific/old version of the polyfill, you can replace that commit has in the URL you posted with one of the commit hashes in the history of this repo.

kevers-google commented 11 months ago

Using an old version of the polyfill is great for quickly getting a demo up and running or for pinpointing what has changed, but is a losing battle in the long run since other browser vendors will eventually implement in accordance with the updated spec. One of the dangers with a bleeding-edge polyfill is that the spec can change rather suddenly.
Chrome has released native support for scroll-driven animations in alignment with (https://www.w3.org/TR/scroll-animations-1/) and the polyfill is not too far behind. On the bright side, the spec, though evolving, is extremely unlikely to change in a way that breaks backward compatibility from this point onward. It is safe to develop demos against the latest version of the spec.

To gauge why a demo no longer works in the polyfill, my recommendation is actually to test in a Chromium-based browser with the element inspector (dev tools) open. Depending on whether it is a changed CSS property or a JavaScript API option, you should see appropriate warnings that are less obscure than errors in the polyfill (if any). If a demo works in Chrome natively, but not in the polyfill, then please let us know.

adaptifyDesigns commented 11 months ago

The spec and syntax have changed. The polyfill now supports this syntax. See https://developer.chrome.com/articles/scroll-driven-animations/ for up-to-date info.

If you want to use a specific/old version of the polyfill, you can replace that commit has in the URL you posted with one of the commit hashes in the history of this repo.

Right now I just need to get the animations to function as they were a couple weeks ago. The commit hash in the URL was the one that has been working for over a year... how is it possible that the specific commit has been altered to the extent of breaking my JS animations?

I've been trying to re-implement the same animations using the latest version of the polyfill and the new ViewTimeline but I have not had any luck. In fact, even when I copy/paste from the chrome demo page I still can't get any of the scroll animations to work. The target elements or "subjects" appear to be frozen mid-animation, but will not respond to the scroll event or position. But that's a whole other issue. In the mean time I just need to figure out how to revert back to the correct commit/version so that the animations work again, while I figure out how to implement the newest syntax.

I thought I had avoided this situation by using the githack link with the specific commit hash... now I'm not sure how to track down the commit which will allow my animations to function. Any ideas?

bramus commented 11 months ago

The polyfill only attaches itself when no scroll-timeline support is detected. Since Chrome now natively has scroll-timeline support, the polyfill - including the version you are using - does not do anything as it assumes the browser can handle it.

As @kevers-google suggested: try and get code that uses the new syntax working in Chrome, without the polyfill. Then, once it works in Chrome, load up the polyfill to cater for Firefox/Safari.

The frozen issue you describe is something that has been fixed in a recent patch.

adaptifyDesigns commented 11 months ago

@bramus ok, I think I'm understanding now.

But regarding the frozen issue... you say it was fixed in a recent patch... a recent patch of Chrome, or of the polyfill?

If ViewTimeline is not working in my version of Chrome, even when I'm loading the latest scroll-timeline.js, then that must mean that the polyfill is detecting partial browser support, even though full support isn't present, correct? So my JS animations would break on most relatively recent versions of Chrome, right?

I checked Safari just now and none of the animations are working at all. I'm getting console errors, both for the older version of the polyfill I was using, and for the newest version, which I'm currently testing on a staging site:

image

Anything look familiar? Any help is much appreciated.

adaptifyDesigns commented 11 months ago

I tried running the following JS from the chrome demo page without loading the polyfill script:

const $images = document.querySelectorAll('.revealing-image');

$images.forEach(($image) => {
    $image.animate(
        {
            opacity: [0, 1],
            clipPath: ['inset(45% 20% 45% 20%)', 'inset(0% 0% 0% 0%)'],
        },
        {
            fill: 'both',
            timeline: new ViewTimeline({
                subject: $image,
            }),
            rangeStart: 'entry 25%',
            rangeEnd: 'cover 50%',
        }
    );
});

I encountered the exact same frozen animation issue. I have three .revealing-image images on the page. The first two are unaffected. The last one is frozen mid-animation. None of them respond to the scroll event. What am I doing wrong?

kevers-google commented 11 months ago

When debugging an issue, it is often best to limit the number of moving parts. Have you tried running in Chrome without the polyfill loaded? If the demo still doesn't work in Chrome 116 (earlier versions do not support scroll-driven animations), it likely means that the demo is using obsolete syntax, and error messages in the dev-tools console should help in pinpointing the offending syntax. Once working in Chrome without the polyfill loaded, we can then try adding the polyfill back in and ensure that it doesn't break the demo. It is entirely possible that native and polyfill implementations do not peacefully co-exist. This was working at one point, but may have broken. Next, you can try with the polyfill loaded and native support for scroll-driven animations explicitly turned off (can do this with experimental flags). Once all of this is working, you can then try in Safari to test for cross-browser support.

The old syntax, won't work with the recent polyfill changes as they bring the polyfill in line with the spec and should behave similar to native Chrome.

bramus commented 11 months ago

I think the problem you are running into is that the image can never reach the "cover 50%" point because the image is at the end of the scrollport's content.

As you mentioned, the other images work fine, so the code works.

Please refer to the already linked to documentation, which has explanations (and a visualization tool) for each range.

adaptifyDesigns commented 11 months ago

@kevers-google Yes, I tried it in Chrome, without loading the polyfill, and encountered the same frozen issue. What I don't understand is how/why it seems to work just fine on the Chrome demo page, but not for me, with the same code, same browser, etc.

@bramus Sorry, I did not explain clearly. The other images are not animating at all. And the final image is frozen, even though it enters into the range.

Here is my demo page.

bramus commented 11 months ago

Taking just your markup and your Scroll-Driven Animations code – which is exactly the same as this one here – works fine. It is most likely one of your other JavaScript files that is messing things up.

As @kevers-google mentioned: try to limit the number of moving parts, removing code from your site until it works fine, and then working your way back up.