This patch focus on the timing computation of animation effects. We have
to compute the correct progress based on the scroll offsets. Now we
simplify the implementation only from 0% to 100%. The test cases will be
added in the last patch once we hook the scroll timeline to the CSS
animation.
Details
- Reviewers
hiro - Commits
- Restricted Diffusion Commit
rMOZILLACENTRAL237f69b0017b: Bug 1676791 - Part 3: Implement the computation of timing. r=hiro - Bugzilla Bug ID
- 1676791
Diff Detail
- Repository
- rMOZILLACENTRAL mozilla-central
- Lint
Lint Not Applicable - Unit
Tests Not Applicable - Build Status
Buildable 379382 Build 474219: arc lint + arc unit
Event Timeline
Thanks for updating. Now this looks much better. What I was in fact suggesting is, something normalization of scroll timeline's time duration.
So for example, given that there are two different scrollers, one is 100px scrollable height and 10px scroll port, the other is 100px scrollable height and 50px height. With these scrollers, scrollTop=10px in the first one is at 1/10 position, scrollTop=5px in the second one is also at 1/10, can we represent both as a single same value?I believe we can.
Say, if we have a fake TimingParams::mDuration for all scroll timelines, say if the duration is 100, Then GetCurrentTimeAsDuration for scroll timelines for both cases would return same 10? Then GetCurrentTimeAsDouble would return 0.1.
So a question is what the reasonable fake TimingParam::mDuration is. Maybe INT64_MAX - 1, I am less sure though? (Given that INT64_MAX is a special value).
The formula of layout scroll range is: scrollable height - scroll port height, so let me update the first case to use 110px scrollable height.
Let's calculate this again:
Case 1:
scrollable rect height: 110px;
scroll port height: 10px;
so it's the scroll range height is: 110 - 10 = 100px;
With position: 10px, the progress is 10/100 = 0.1
Case 2:
scrollable rect height: 100px;
scroll port height: 50px;
so it's the scroll range height is: 100 - 50 = 50px;
With position: 5px, the progress is 5/50 = 0.1
Say, if we have a fake TimingParams::mDuration for all scroll timelines, say if the duration is 100, Then GetCurrentTimeAsDuration for scroll timelines for both cases would return same 10? Then GetCurrentTimeAsDouble would return 0.1.
So a question is what the reasonable fake TimingParam::mDuration is. Maybe INT64_MAX - 1, I am less sure though? (Given that INT64_MAX is a special value).
So, we keep TimingParam::mDuration be always a fixed value for scroll-timeline (e.g. let's use INT64_MAX - 1 now),
and let GetCurrentTimeAsDuration() return 0.1 * (INT64_MAX - 1), for Case 1 and Case 2. Therefore, we don't have to update AnimationEffect::GetLocalTime() because GetCurrentTimeAsDuration() work properly for scroll-timeline.
INT64_MAX represents the forever timestamp, so using INT64_MAX - 1 is ok to me now because we don't have the progress which is more than 100% for scroll-timeline. Therefore, we don't let GetCurrentTimeAsDuration() overflow.
Let's try this normalization. Thanks.
Well, GetCurrentTimeAsDouble() just converts the returned value of GetCurrentTimeAsDuration() into a double value. Why is it 0.1 in this case?
Oops. Need to make sure the animation playback rate shouldn't be more than 1.0. Otherwise, we may overflow. It seems only web animation APIs can update the playback rate, so it's fine for now.
I am sorry for the confusion. I meant 0.1 portion of the entire duration. Anyway my point is it's a normalized duration.
It seems GetCurrentTimeAsDouble() converts the millisecond to microsecond, so it may be 100000 in this example. I will double-check this. If it's possible to get a very large value, we may use a different fixed duration (e.g. 1000).
Code analysis found 1 defect in the diff 508051:
- 1 defect found by clang-tidy
You can run this analysis locally with:
- ./mach static-analysis check --outgoing (C/C++)
For your convenience, here is a patch that fixes all the clang-format defects (use it in your repository with hg import or git apply -p0).
If you see a problem in this automated review, please report it here.
You can view these defects on the code-review frontend and on Treeherder.
I've been assuming if we tweak AnimationEffect::SpecifiedTiming for scroll linked animations, we don't need to tweak in each functions in KeyframeEffect?
dom/animation/ScrollTimeline.cpp | ||
---|---|---|
12 | Let's use 100 sec as Chrome does. | |
70–71 | Setting vertical case values unconditionally looks odd. If you'd want to optimize the case, MOZ_LIKELY is would optimize the case (I hope). |
So something like:
AnimationEffect::SpecifiedTiming() { if (mAnimation->UsingScrollTimeline()) { return mAnimation->GetTimeline()->GenerateTiming(); } return mTiming; }
It's worth to try. :)
I am assuming the GenerateTiming() could be a constexpr and the name could be simpler something like ComputedTiming or some such.
dom/animation/ScrollTimeline.h | ||
---|---|---|
109–111 | SpecifiedTiming() returns a const reference, so we need a place to store |sTiming|. Besides, constexpr function cannot use a static variable inside the function, and TimingParams cannot be a const variable with non-zero mDuration. (We use StickyTimeDuration::FromMillisecond() in the constructor, and StickyTimingDuration doesn't support a constructor to accept the non-zero value.) I tried to move the static variable outside this function but got a link error. So I didn't do that in this patch. |
dom/animation/ScrollTimeline.h | ||
---|---|---|
109–111 | I probably know what happens on my wip. I will upload a separate patch to make this constexpr. |
Basically this looks good. I'd like to review again with the change that GetCurrentTimeAsDuration returns 1 in unscrollable scroller cases. Thanks!
dom/animation/ScrollTimeline.cpp | ||
---|---|---|
61–66 | Can we use nsLayoutUtils::FindScrollableFrameFor? | |
69–71 | As I commented in D129100, this check is not necessary. | |
dom/animation/ScrollTimeline.h | ||
102 | I do want to drop Get prefix since in our coding style, Get prefix basically means the function might return null (or fail), and in this case the function should be infallible. That said, I don't have any great idea about the name. so GetTiming is fine for now. |
dom/animation/ScrollTimeline.cpp | ||
---|---|---|
61–66 | We can, but we don't have to because FindScrollableFrameFor() calls GetScrollFrameFromContent() again. nsIScrollableFrame* nsLayoutUtils::FindScrollableFrameFor( nsIContent* aContent) { nsIFrame* scrollFrame = GetScrollFrameFromContent(aContent); return scrollFrame ? scrollFrame->GetScrollTargetFrame() : nullptr; } | |
69–71 | We can drop this from D129100, but not here. We have to filter out zero range (so avoid divided by zero) See double progress = position / range; in the following lines. Maybe return a special value, e.g. TimeDuration::FromMilliseconds(1) or TimeDuration(0)? |
dom/animation/ScrollTimeline.cpp | ||
---|---|---|
61–66 | What I suggested is just using FindScrollableFrameFor instead of the pair of GetScrollFrameFromContent and GetScrollTargetFrame. It also makes the thing worse? |
dom/animation/ScrollTimeline.cpp | ||
---|---|---|
69–71 | right. What I supposed is progress = 1 in such cases. then duration should be SCROLL_TIMELINE_DURATION_MILLISEC. |
dom/animation/ScrollTimeline.cpp | ||
---|---|---|
69–71 | Just FYI, our nsIScrollableFrame is generated if the target element is a scroll container. So it should ensure that if there's a valid nsIScrollableFrame, scroll linked animations tied to the scroll container should be active. So the null check for the nsIScrollableFrame above should work as expected. An overflow:hidden element should be a scroll container, overflow: visible should NOT be, FWIW. overflow: auto should be, FWIW, IIUC. |
dom/animation/ScrollTimeline.cpp | ||
---|---|---|
61–66 | The reason is: we need to use
So if we use FindScrollableFrameFor(), we can get nsIScrollFrame*. However, we still have to do_QueryFrame(scrollFrame) to get its nsIFrame. I would like to avoid using do_QueryFrame() because it does more things. |
dom/animation/ScrollTimeline.cpp | ||
---|---|---|
61–66 | Now I see what you mean. That's fair, fine for now. (Though I'd prefer do_QueryFrame way, the old fashion way should eventually go way). |
dom/animation/ScrollTimeline.cpp | ||
---|---|---|
69–71 | Returning TimeDuration::FromMilliseconds(SCROLL_TIMELINE_DURATION_MILLISEC); causes some wpt failures:
I'm checking what happened. |
dom/animation/ScrollTimeline.cpp | ||
---|---|---|
69–71 | The former case should fail since we don't yet implement start and end offsets, no? (I haven't looked into the latter cases) |
dom/animation/ScrollTimeline.cpp | ||
---|---|---|
69–71 | It expects we don't apply scroll-animations (the expected width is 0px). But we make the progress 100%, so get 200px width. |
dom/animation/ScrollTimeline.cpp | ||
---|---|---|
69–71 | But these tests use a specific scroller, so perhaps we don't have to care about them. |
dom/animation/ScrollTimeline.cpp | ||
---|---|---|
69–71 |
Right. We don't need to care about them because the root is not scrollable, so it should be 100%. Thanks for checking this behavior in Chrome. :) |
dom/animation/ScrollTimeline.cpp | ||
---|---|---|
69–71 |
Yes. we are using 0% to 100% in all cases, so it's impossible to go into before phase. |
dom/animation/ScrollTimeline.cpp | ||
---|---|---|
69–73 | GetAvailableScrollingDirections() compares the range with one device pixel and it looks more reasonable, so I use it now. An alternative way: just check range below double progress = range > 0.0 ? position / range : 1.0 Either way is ok to me. |
dom/animation/ScrollTimeline.cpp | ||
---|---|---|
68 | Why did you drop the null check for nsIScrollableFrame*? It is necessary, a valid nsIFrame doesn't mean it has a valid nsIScrollableFrame. (that's the reason why I prefer just using FindScrollableFrameFor). | |
79–80 | It wasn't immediately clear to me why we reduce scrollRange.x (or .y). Can you please elaborate the reason? And it ended up reminding me that scroll position in RTL would be negative. It also reminds me that just using the writing mode for the nsIFrame might be wrong. Did you ever test in RTL cases? At least for me this scrollOffset.x - scrollRange.x looks incorrect in RTL. |
dom/animation/ScrollTimeline.cpp | ||
---|---|---|
68 | Oops. Missed this update. Sorry about this. | |
79–80 | Thanks for pointing this out. This is definitely wrong.
I subtract scrollRange.x because the formula in the spec is offset - start offset. However, I was wrong because scrollFrame->GetScrollPosition() aligns the returned value to the scrollframe position already, i.e. nsPoint GetScrollPosition() const { return mScrollPort.TopLeft() - mScrolledFrame->GetPosition(); } So we don't have to subtract scrollRange.x/y.
I just tested it, and I was wrong. Basically, using abs(scrollOffset.{x|y}) is enough for both RTL and LTR. |
This revision requires a Testing Policy Project Tag to be set before landing. Please apply one of testing-approved, testing-exception-unchanged, testing-exception-ui, testing-exception-elsewhere, testing-exception-other. Tip: this Firefox add-on makes it easy!