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-View-Transitions-2] Relative Additive Animation #9578

Open
KevinDoughty opened this issue Nov 8, 2023 · 12 comments
Open

[CSS-View-Transitions-2] Relative Additive Animation #9578

KevinDoughty opened this issue Nov 8, 2023 · 12 comments
Labels
css-view-transitions-2 View Transitions; New feature requests

Comments

@KevinDoughty
Copy link

I’m not too familiar with what you’re doing, in particular MPA vs. SPA. I only know that I solved something very similar and it might fit into your API. I call it relative additive animation.

I propose you implement CompositeOperation: blend and animation-composition: blend from:
https://phabricator.services.mozilla.com/D156634#5139009

You might as well implement transition-composition: blend as it is better than CSS Transitions’ Faster Reversal but is not needed for View Transitions. The fourth from the above list is unnecessary. The fifth is unfinished and experimental, and could possibly provide a moderate performance increase for compositor animations but is also not needed for View Transitions.

I propose you implement a new animation primitive, a discrete form of relative additive animation for collections. If you want to expose it to javascript arrays or NodeList that would be great too but might not be necessary for View Transitions.

The relative in relative additive animation refers to conversion of values by subtracting the new value from the old, old minus new, and using that as the start value, and animating to zero. This was popularized on the web as FLIP, but the W3C has so far ignored its benefits when additive.

Old minus new of a discrete collection when animating from [ A, B, C, D ] to [ A, B, D ] is simply [ C ], the item being removed. The animations are additive which results in [ A, B, D, C ]. They are not in the correct order, which requires sorting to return to [ A, B, C, D ] for the duration of the exit animation.

I propose View Transitions accept an optional second parameter sort function for startViewTransition. This sort function must sort all possible combinations of elements, where returning 0 would not establish equality, but instead identity.

Considering how transitioning elements are moved to an overlay pseudo element, the above discrete animation would resemble something more like [ A, B, D ] + { C } where the curly braces are the pseudo element. If [ B ] were removed before the previous transition finished, that would result in something like [ A, D ] + { B, C }.

The CSS Values spec might also need to define subtraction but I’m not sure. This is more than just addition * -1.

Related:
#8678
#9512

@KevinDoughty
Copy link
Author

It works just fine even when changing element order. There may be some additional time complexity in the union and difference operations, and there may be some stacking order surprises when one element appears in front of another suddenly, but it works just fine.

@KevinDoughty
Copy link
Author

There is no time complexity issue. Converting from an array to a set, testing membership, then converting back to an array is O(3n) which is still linear. There are no leaks, that’s the point of animating to zero. When finished memory can be freed without using a fill mode.

What remains to be solved appear to be trivialities. What gets passed to the sort function? Are they elements, IDs, data attributes, or something else? I guess there should be a different sort function per pseudo element for SPAs, but I haven’t looked at that in detail.

I’m sorry that I was the one to solve this as the bias seems to be against me and not the proposed algorithm, but there is no other solution except not allowing View Transitions to handle interruption at all, which is inferior and embarassingly backwards looking.

@KevinDoughty
Copy link
Author

Animations will need to be automatically copied from those pseudo elements back to their originals for re-entry, which I failed to mention. A web developer can acheive subgroup animation by manually copying those animations to sub-elements, which might be tricky for anything other than translation and opacity. If the sort function is absent, automatic copying animations from the pseudo back to the original element should not happen.

Square brackets denote groups and subgroups.
Capital letters denote outer elements.
Numbers denote sub-elements.
Pipe characters are used to divide outer elements.
Commas are used to divide sub-elements.
Curly braces (handlebars) denote rasterized GPU textures.
Round parenthesis denote animation.
Pound/hashtag character denote comments

[ A | B[1,2,3] | C ] # original
[ A | C ], {B[1,2,3]} + (exit) # Element B is removed with an exit animation
[ A | B[1,2,3] + (exit) + (entry) | C ] # Element B would have to be re-inserted entirely then sub-elements re-animated out
[ A | B[2]+(exit)+(entry), { 1+(exit)+(entry)+(re-exit), 3+(exit)+(entry)+(re-exit) } | C ] # Sub-elements 1 and 3 are removed individually

I wrote to the public-fx mailing list about animation copying. With the help of one W3C member I was able to reproduce an example I wrote in Obj-C. This was needed for sub-dividing glyph runs in NSTextView, which were drawn on GPU textures and shuffled around with Core Animation. Sadly all those javascript animation examples broke when the entire web-animations js shim repository was deleted and additive animation was removed from its replacement. This was the animation copying example in Obj-C:
https://www.youtube.com/watch?v=M-8_9vjJbKs&t=72s

It’s been over a decade now.

@KevinDoughty
Copy link
Author

There would be two sort functions in the above example, using "A, B, C" and "1, 2, 3" each for their own separate group. Sort functions must be able to sort all possible combinations of subviews. If the web developer changes the function output midway somehow, behavior is undefined, or perhaps better stated, “hilarity will ensue”.

@KevinDoughty
Copy link
Author

I could not have come up with the ergonomics of the View Transitions API for lack of experience. I do have experience with shuffling around GPU textures. It takes:

Relative additive animation and a sort function as single source of truth.

The absence of the optional sort function second parameter to startViewTransition allows for existing behavior, its inclusion allows for so much more.

@KevinDoughty
Copy link
Author

KevinDoughty commented Nov 18, 2023

One more thing, from the list of steps to achieve subgroup animation, steps three and four. The re-entry and re-exit animations must happen simultaneously. Equal and opposite additive animations would cancel each other out. The result would be only the original exit animation having any visual effect on sub-elements 1 and 3, while sub-element 2 would animate back in.

@KevinDoughty
Copy link
Author

TL;DR: Instead of animating a sub element back in you must simultaneously animate the outer element back in and the other sub elements back out.

This may not be the exact same thing, but you can really build a lot with this pattern.

@KevinDoughty
Copy link
Author

I’ve got it. For the sort function, zero establishes identity, not equality. Unless it’s a sub-element that is also meant to be in one of these groups. So you scan down to the next group, however deeply nested it is. If an element does not belong to a group and is not handled by the sort function, or any beneath it, it returns null.

@KevinDoughty
Copy link
Author

Sort function returns zero for identity, or if it's a descendent element from another group. Sort functions return null if not handled.

@KevinDoughty
Copy link
Author

That means any sort function will have to be able to sort all possible elements in its group, but also all possible elements of any subgroups, no matter how deeply nested. Zero establishes identity or descendant membership. If there are any subgroups, they must return null for any non-members.

It's a little hairy but I think it would work.

@fantasai fantasai added the css-view-transitions-2 View Transitions; New feature requests label Jan 9, 2024
@KevinDoughty
Copy link
Author

Sort function. For sub-elements not handled at the current level, return null instead of 1, 0, or -1. That means a different invocation of startViewTransition, with a corresponding different pseudo-element, has provided the sort for the sub-element.

To animate back in a sub-element of an element being animated out, a rasterized representation of the group would have to be redrawn with the sub-element removed from the picture. That group would have to be kept in memory, just inaccessible to the page author, for the duration of the exit animation. Styles would have to be preserved too. I hope those are what are called "snapshots" in Firefox/Servo, and they're not rasterized images. Styles of these snapshots would not have to be recalculated but the sub-element’s snapshot would have to be removed. Yes it’s hairy.

Exit animations on the group would have to be copied from the group to the newly reinserted sub-element. A fade out animation continues to run, and a new fade in animation runs additively on top.

Also, elements animating in should be live and instantly accessible to events. If discrete-state were implemented, hit testing would always resolve to destination values regardless of animation state.

No blocking the UI please.

@KevinDoughty
Copy link
Author

Step one is CompositeOperation: blend as shown in this diff for Chromium. It could probably be refactored and needs to be assessed for memory management.
https://gitlab.com/kevindoughty/chromium-diff/-/commit/9238b5ae324d535390bbba35d165f45b101a5940

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
css-view-transitions-2 View Transitions; New feature requests
Projects
None yet
Development

No branches or pull requests

2 participants