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
Scroll AMP doc to center highlighted elements in the viewport #15791
Conversation
#15337 merged. |
e4b6024
to
d110ce2
Compare
Thanks. Rebased the change to amphtml/master. |
} else { | ||
if (scrollTop > SCROLL_ANIMATION_HIGHT_LIMIT) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: HEIGHT
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, good catch!
if (!nodes) { | ||
return 0; | ||
} | ||
const viewport = Services.viewportForDoc(this.ampdoc_); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is Viewport.animateScrollIntoView(element, duration, curve, 'center');
not good enough for your use case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are two reasons I implemented a new logic:
- I want to center multiple elements because discontinuous sentences can be highlighted.
- Want to center elements when the header is shown (height = win.height - viewport.getPaddingTop).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case, I propose you do the following:
- Use the same math to calculate a center rectangle.
- Said rectangle is used to create an invisible element with
pointer-events: none
so that it won't block interaction. - Use
animateScrollIntoView(invisibleElement, duration, curve, 'center')
. - Remove invisible element afterwards.
This has two purposes:
- keep the viewport API stable.
- Aid in the AMP build-size reduction effort. We have a bundle size restriction in place, which would be triggered by adding a method to the viewport service. We can relax this limit, but measures like this help keep it smaller in the first place! :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand your suggestion correctly.
To implement your suggestion, I need to add new code to implement
- Said rectangle is used to create an invisible element with pointer-events: none so that it won't block interaction.
- Remove invisible element afterwards.
This is not very trivial and it will increase amp-viewer-extension
size a little bit. However, I can not remove any code from the current implementation except for the interface of animateScrollToTop
in viewport-impl.js
.
Which part of code do you think we can remove by your suggestion?
Thanks,
yunabe
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Increasing the size of amp-viewer-integeration
is fine. However, by modifying Viewport
, the size of the core AMP binary increases.
I'm suggesting that you revert the changes to Viewport
and use the method described above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's true. But the additional size of dist/v0.js
is just 7 bytes after gzip (78597 --> 78604, 107 bytes before gzip 247319 --> 247426).
Do 7 bytes (+0.01%) really matter in the core AMP binary size?
If 7 bytes really matter, I will remove default parameters from animateScrollToTop. Then, the size diff is only 1 byte after gzip (78597 --> 78598).
Do we really want to implement non-trivial logic in amp-viewer-integration
just to save 7 bytes (1 byte w/o default parameters) in the core runtime?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry if I'm missing something, but how is it non-trivial? It seems like you could calculate easily and left/right aren't an issue since we're only scrolling vertically. So it would only be a matter of doing something like:
const sentinel = document.createElement('i-amphtml-sentinel');
setStyles(sentinel, {
'position': absolute,
'left: '0',
'right: '0',
'top': topAsUsuallyCalculated,
'bottom': newLogicToCalculateBottom(),
});
this.getAmpDoc().getBody().appendChild(sentinel);
return viewport.animateScrollIntoView(// .. etc
Unfortunately we're only counting size before gzip.
But you're right, the difference is negligible. It's fine to bump size. Just one nit: rename to
animateScrollTo
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant I think it's less trivial to inject the sentinal node than to export animateScrollTo
in viewerport, especially considering setScrollTop
is already exported in viewport and the additional size to v0.js
is limited.
But I do not have a strong preference. I reimplemented this based on your suggestion.
let minTop = Number.MAX_VALUE; | ||
let maxBottom = 0; | ||
for (let i = 0; i < nodes.length; i++) { | ||
const rect = viewport.getLayoutRect(nodes[i]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Use destructuring:
const {top, bottom} = viewport.getLayoutRect(nodes[i]);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
.animateScrollIntoView(sentinel).then(() => { | ||
this.sendHighlightState_('shown'); | ||
}); | ||
body.removeChild(sentinel); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should occur once animateScrollIntoView
is resolved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the review. Could you take another look?
let minTop = Number.MAX_VALUE; | ||
let maxBottom = 0; | ||
for (let i = 0; i < nodes.length; i++) { | ||
const rect = viewport.getLayoutRect(nodes[i]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
if (!nodes) { | ||
return 0; | ||
} | ||
const viewport = Services.viewportForDoc(this.ampdoc_); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant I think it's less trivial to inject the sentinal node than to export animateScrollTo
in viewerport, especially considering setScrollTop
is already exported in viewport and the additional size to v0.js
is limited.
But I do not have a strong preference. I reimplemented this based on your suggestion.
This pull request should be merged after #15337. There is only one new commit in this PR (2674709)