Scroll driven lottie animations - #2473
Open
tf wants to merge 14 commits into
Open
Conversation
Content elements can now observe how far they have travelled through the viewport, not only whether they are visible. Registering a content element type with `viewTimeline: true` makes the `useContentElementViewTimelineProgress` hook available, which invokes a callback with a progress value between 0 and 1. Vocabulary follows CSS scroll driven animations: The content element acts as the subject of a view timeline and the `range` option selects which part of it to measure (`cover`, `contain`, `entry` or `exit`). Progress is passed to a callback instead of being returned by the hook to prevent rerendering content elements on every scroll frame. It is meant to drive imperative APIs like animation players. Since content element types can support view timelines without always observing scroll position, scroll and resize handlers are only registered while there are subscriptions. Specs can drive progress via the new `simulateScrollProgress` function returned by `renderInContentElement`.
The new `scroll` playback mode turns the animation into a scrubbing target: Instead of playing on its own, the current frame follows the element's progress along the `cover` range of its view timeline. The animation starts as soon as the element enters the viewport and reaches its last frame once the element has completely left it. Frames can only be set once the animation has loaded. Progress observed before that is applied as soon as the load event fires.
Lets editors pick the new playback mode that couples the animation to the scroll position of the element.
Content elements with standAlone position are pinned in the center of the viewport while their scroll space passes by. Measuring their own bounding rect would make progress stall for exactly the part of the scroll space that the extra scrolling was added for. Let components that add scroll space around a content element pass the element that keeps moving with the page as the subject of the view timeline.
Content elements with sticky position stay pinned next to the text while the rest of their group scrolls past. Measuring their own bounding rect would make progress stall for exactly that part of the section. The group is the containing block that constrains the sticky box, so its bounding rect covers the same range of the page: from the element scrolling in at the top of the group to it leaving with the group's bottom edge. On narrow viewports, sticky boxes are rendered inline. Progress is then measured along the element itself again. Extract a Box component for the box element on the way, since looking up the group requires a ref.
Content elements that are pinned in the viewport are measured along a taller subject: the scroll space for standAlone position, the group for sticky position. Ranges other than cover took the subject's height for the element's height, so contain meant "while the group is completely inside the viewport" instead of "while the element is" - a range that collapses into a jump from 0 to 1 as soon as the group is taller than the viewport. Express ranges as pairs of milestones along the page instead, and let pinning components pass the pinned element next to the subject via a provider component, so the element's height can be taken into account. Elements that overflow their subject never reach their pinned position and keep moving with the page. Fall back to measuring their own rect, which then covers the same range of the page.
Content elements count as active while they intersect the vertical center of the viewport. Autoplayed videos start playing at that point. Add a range covering the same part of the page, so scroll coupled animations can run exactly while the element holds the center of attention. Also correct the description of isActive in the docs, which claimed elements are active while completely inside the viewport.
Content elements with sticky or standAlone position are pinned in the viewport for part of their scroll space. Add a range measuring exactly that part of the page, so scroll coupled animations can run while the element holds its featured spot. The position an element is pinned at is only known while it actually is pinned. Measure how far the element has slid within its subject instead: That distance grows from zero to the subject's extra scroll space while the element is pinned and stays constant before and after, which clamps progress on both ends. Elements that are not pinned at all have no such phase. Progress along the range stays 1 for them.
Which part of the page a content element holds the reader's attention for depends on its position: Elements that are pinned in the viewport do so while they stay in place, all others while they pass the center of the viewport. Add a range that resolves to the pinned or the center range accordingly, so content elements do not have to know whether the layout pins them - which can change with the viewport width.
Scroll coupled animations so far always ran along the whole part of the page during which the element was visible. Add a select that lets editors pick which part of the element's scroll motion drives the animation, including the phase during which sticky and standAlone elements stay in place. The inFocus range means different things depending on position, so name it after the pinned phase for positions that keep the element in place and after the viewport center for all others. Since the texts of a select cannot depend on other attributes, there are two inputs for the same property toggled via visible bindings. Which of them applies follows the resolved position, since layouts that do not support sticky position render such elements inline. Extend simulateScrollProgress with a range option, so specs can tell which range a content element observes. Let the SelectInput domino read the texts a select offers and the input dominos filter for inputs that are currently visible.
Some input views render miniatures of sections or content elements to illustrate what an option does. Those components are not input views themselves and are shared between input views, so keep them in a directory of their own. SectionPaddingVisualizationView stays where it is despite its name: It is an input view.
The illustration of how a content element behaves while the page scrolls is useful beyond choosing a position: Scroll ranges can be demonstrated with the same miniature section. Extract a ContentElementVisualization component that renders the markup and takes position and layout as props, and a useScrollAnimation hook that scrolls a referenced element back and forth. Children of the visualization are rendered inside the rectangle representing the content element, so callers can add overlays. Name it after the element it visualizes rather than after the section it renders, to keep it apart from SectionVisualization, which illustrates layout and appearance of sections. Let callers compute the scroll position from the rendered visualization instead of passing a fixed distance, since demonstrating a whole view timeline requires knowing where the element sits.
Editors cannot tell from range names alone which part of the page drives the animation. Illustrate each range with the same miniature section the position select uses, scrolled along the whole view timeline of the element: from before it enters the viewport until it has left again. Two tweaks to the illustration make ranges easier to tell apart: The rectangle representing the element is narrowed for positions that do not place it next to text, and the section gets room to scroll above and below its content. Without that room, the scroll position the end of the timeline requires is beyond what the visualization can scroll to, which makes progress appear to stop short of the end. Ranges that are measured against the center of the viewport mark it with a dotted line. Play progress is displayed inside the rectangle representing the content element, as a percentage over a bar that fills the rectangle. Progress comes from the same getViewTimelineProgress function the frontend uses, so the demo cannot drift from the real behavior. Since the visualization pins elements like the entry does, feeding it the rects measured inside the visualization is enough. All options illustrate the position the element currently has, since the range is what differs between them. Position and layout can be passed as functions, since options are only rendered once the dropdown opens.
Replace the plain selects for the scroll range with the new scroll range select input view, so editors see the range they pick played back on a miniature of their section instead of having to imagine it. Both wordings of the inFocus range keep their own input, and both illustrate the element as it appears in its section: The position is resolved through the layout of the section, and the layout decides whether text sits next to the element. Options only exist while the dropdown is open, so specs read the range names from the descriptions of the open dropdown. Export the input domino from pageflow so looking up an input that is not a select does not have to go through SelectInput.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
REDMINE-21349