ampproject / amphtml

The AMP web component framework.
https://amp.dev
Apache License 2.0
14.89k stars 3.89k forks source link

I2I: amp-fx=float-in-(top|bottom) #20881

Closed alanorozco closed 5 years ago

alanorozco commented 5 years ago

Background

20125 requests a feature to track scrolling in order to hide/show headers:

A way to dispatch events based on the user's scroll direction. This would be great for animations, such as a header that's removed when the user scrolls down and reappears when the user scroll up again. Might be nice to be able to put a range of scrolling: on:scrollUp.100vh.event for full screen scrolling and on:scrollUp.1vh.event for any user scroll interaction.

This approach more or less works, but it has some drawbacks and open questions that the alternative described here would eliminate.

Goal

To provide an independent component for element toggling on scroll for performance, UX and DX.

Issues with event-trigger approach

amp-animation usage and definition

The main use-case for this feature is presumably toggled headers on scroll up/down, similar to behavior on the Google SERP viewer, Safari/Chrome for iOS or Chrome for Android. In order to build such a feature using the method described in #20125, the author would need to define three separate things: action handlers for scrollUp/scrollDown, an amp-animation for scrollDown and an amp-animation for scrollUp.

Not only is this tedious but it's also fragile. In order to create an e.g. a slide-to-toggle-header animation, the absolute value of the translate has to match the the header's height and be included twice; once for each animation. This can obviously lead to mismatching and would be better off being calculated dynamically, similar to how amp-story handles animation presets.

Responsive designs are also an issue in this case, since headers could vary by height, so multiple elements and animations would have to be included for each media query configuration, aided by a combination of transparency and pointer-events: none CSS nightmare.

The amp-animation requirement is also a performance issue, since the extension is rather large (currently at 33.627kB gzipped) and we could just simply evade it and perform simple CSS animations instead.

Leaky abstraction

Triggering simple events on scroll may lead authors to hand-roll behavior where better performing alternatives are available, e.g. amp-position-observer, even if they're low-trust. Examples are scroll-bound or scroll-triggered animations, where listening to an element's position is a better pattern than listening to scroll events directly.

UX

In a significant number of cases, AMPHTML pages are rendered in the Google SERP viewer, which has the same header-toggle-on-scroll behavior. The logic for deciding when to toggle is not strictly simple (internal: go/amp-viewer-scroll), and is bound to hard-coded thresholds (internal: go/amp-viewer-scroll-thresholds). If the AMP-side logic or thresholds do not match the viewer's, independent headers would be toggled at different times, which would be extremely awkward. If we get rid of the threshold configuration from the event-based approach this is not strictly an issue, but we would be exposing a misnamed/misleading API instead.

Semantics & Encapsulation

Similar to misnamed APIs, we have to consider where to put this.

amp-fx-collection

Including a new fx preset allows us to encapsulate the desired behavior with clear boundaries and without bloat. It also enables some invariants that are good for UX and DX:

High-level usage

The amp-fx attribute is set with either float-in-top or float-in-bottom.

<!-- Header will slide in and out as the user scrolls up/down -->
<header amp-fx="float-in-top">
  <h1>⚡ Ampersand News</h1>
</header>

On build time, amp-fx-collection async'ly ensures that target:

Otherwise, it will do nothing.

Triggering

The component attaches scroll handlers that match the Google SERP viewer's header-toggling logic, and thresholds for consistency. On toggle, it calculates animation offsets corresponding to the target element's height and triggers a time-bound animation.

Performance

Multiple amp-fx="float-in-*" elements can live in one page for different targets. A single Observable should be instantiated from a single scroll handle that all the elements will listen to.

Opportunities

In Google SERP cases, we already receive a postMessage that tells us to toggle, including duration and curve. No additional scroll handlers would be needed and toggling logic would always match, regardless of viewer changes.

Milestones

  1. Self-contained scroll logic is the common case, so build that first.
  2. Receiving toggle message from Google SERP viewer is an optimization and should come later.

Drawbacks

It's up to the authors not to do this:

image

We can consider restricting the amount of toggle elements that are alive at once. Alas we don't currently do anything more sophisticated to disregard excessive floating elements, so relying on good faith that including several floating bars is bad UX would simply preserve the status quo.

/cc @ampproject/wg-ui-and-a11y @aghassemi @nainar @CrystalOnScript

cathyxz commented 5 years ago

I have a use case for https://github.com/ampproject/amphtml/issues/20125, which has been closed in favor of this issue. For context, it's what I outlined here: https://github.com/ampproject/amphtml/issues/21016#issuecomment-466546960

img_0276

I can partially implement this with position observer, amp-animation, translateY and a position https://codepen.io/cathyxz/pen/YBmKBq. So if we remove the animation exit, and instead hide this sticky footer on scrollDown, I think that should get us all of the way there.

Is scrollUp and scrollDown still something we're looking at adding? Could they be used to trigger animations?

alanorozco commented 5 years ago

I feel strongly that we shouldn't expose scrollUp and scrollDown directly. However, I think your use case is valid. Let's discuss offline or setup a meeting on monday?

alanorozco commented 5 years ago

(@cathyxz Sorry for leaving this hanging before, I filed #21044 with an alternative. We can still chat on Monday.)

aghassemi commented 5 years ago

Implemented!