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

Calculate current absolute position of fixed-position elements #5624

Conversation

jridgewell
Copy link
Contributor

@jridgewell jridgewell commented Oct 14, 2016

Currently, Resources caches the absolute position (layoutBox's top and bottom). This works perfectly for the majority of elements, since they never move absolutely.

But fixed-position elements are different. They are positioned relative to the viewport, and the viewport moves. So, fixed-positions "move" absolutely. This change makes it so that we cache the relative positions of fixed-positions (to the viewport). When we later ask for the element's layoutBox, we'll calculate the "absolute" position by adding in the current scrollTop and scrollLeft. This is exactly the same as what I'm doing for ad's layoutBox.

Part 2 (and hopefully the final) of #5149.
/cc @jasti.

@@ -172,8 +172,7 @@ export class BaseElement {
* @return {!./layout-rect.LayoutRectDef}
*/
getLayoutBox() {
return this.element.getResources().getResourceForElement(
this.element).getLayoutBox();
return this.element.getLayoutBox();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this a DOM api? i think this should be from resources-impl - like how it was before?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's defined in custom-element.js. It does exactly this.

@camelburrito
Copy link
Contributor

LGTM - @dvoytenko do you want to take a pass?

@dvoytenko
Copy link
Contributor

Yes. Looking.

@@ -444,9 +444,6 @@ function createBaseCustomElementClass(win) {
this.layout_ = Layout.NODISPLAY;

/** @private {number} */
this.layoutWidth_ = -1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's going to happen to this var? I still see it used throughout in this class.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're looking at an old diff.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I think I'm looking at the right one. This appears to be a9808200539a76eb965b6c6f7d0aba79cce8a4e1 commit...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops. Re-added.

@@ -707,6 +704,7 @@ function createBaseCustomElementClass(win) {
this.implementation_.layoutWidth_ = this.layoutWidth_;
}
// TODO(malteubl): Forward for stubbed elements.
// TODO(jridgewell): We should pass the layoutBox down.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's passed. Right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.implementation_.onLayoutMeasure();. We should pass it to #onLayoutMeasure, because a lot of them immediately call #getLayoutBox.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, that'd be fine, of course. But we'd keep getLayoutBox() method itself? I made more notes on this below as well. Ideally this would be only size. However, in the meantime, what this API promises is to return "the last measured/known layout box".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But we'd keep getLayoutBox() method itself?

Yup, we need that else where.

Ideally this would be only size.

Yah, just passing { width, height } would satisfy most all of them. Anyone else could call #getPosition or whatever we end up calling it.

return this.layoutBox_;
}
const viewport = this.resources_.getViewport();
return moveLayoutRect(this.layoutBox_, viewport.getScrollLeft(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. This is a bit troublesome. The definition of the getLayoutBox is that it doesn't change based on the scroll state. We are de-facto changing it here. I'm not sure what downstream issues we maybe expecting from this. The truth is, of course, that fixed element's layout box doesn't change but defined within another (fixed) layer. I think a better option for us would be:

  1. Modify overlaps method to incorporate the knowledge of isFixed. We can even rename it as overlapsViewport. IMO, this is better b/c this is a more direct API that's only used for the purposes of scheduling.
  2. At this time, propagating getLayoutBox is rather dangerous. I don't know who uses it and where. I think it's best to replace this call with getLayoutSize() or getLayoutWidth/Height pair. I believe most of clients only care about size and not absolute position.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The definition of the getLayoutBox is that it doesn't change based on the scroll state.

I've taken it to mean "give me the absolute position from the origin (0, 0)", which is how everything (until we introduced #isFixed) treated it. This just fixes the value fixed-position's return.

  1. Modify overlaps method to incorporate the knowledge of isFixed.

The overlaps stuff is totally superficial to the PR. I need to fix #getLayoutBox for Flying Carpet, which depends on it returning an absolute position.

I don't know who uses it and where. I think it's best to replace this call with getLayoutSize() or getLayoutWidth/Height pair.

I'd love to see this. Should we wait for the Layers work?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it should definitely wait for layers work. Or, alternatively we can do a refactoring pass to clarify this API and remove exposing left/top to clients that don't need it.

I've taken it to mean "give me the absolute position from the origin (0, 0)", which is how everything (until we introduced #isFixed) treated it. This just fixes the value fixed-position's return.

This might be a mis-understanding, but this is not how I read this code. I definitely understand that the current implementation is wrong, but not yet clear that this fixes it.

What does "origin (0, 0)" mean: document origin or viewport origin? From what I see, (assuming we measured the fixed element once at first) if I call this method twice at different scroll positions (e.g. scrollTop=100 and 200) this method will now return different results. I'm not sure if that'd improve things.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, alternatively we can do a refactoring pass to clarify this API and remove exposing left/top to clients that don't need it.

That works.

What does "origin (0, 0)" mean: document origin or viewport origin?

The top left corner of the document, not the viewport.

if I call this method twice at different scroll positions (e.g. scrollTop=100 and 200) this method will now return different results.

Exactly. Because the fixed-position has "moved" relative to the origin. Because fixed-position's are fixed relative to the viewport (usually), and the viewport moves down the document, so fixed-positions move.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Alright.

@@ -444,9 +444,6 @@ function createBaseCustomElementClass(win) {
this.layout_ = Layout.NODISPLAY;

/** @private {number} */
this.layoutWidth_ = -1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I think I'm looking at the right one. This appears to be a9808200539a76eb965b6c6f7d0aba79cce8a4e1 commit...

@@ -707,6 +704,7 @@ function createBaseCustomElementClass(win) {
this.implementation_.layoutWidth_ = this.layoutWidth_;
}
// TODO(malteubl): Forward for stubbed elements.
// TODO(jridgewell): We should pass the layoutBox down.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, that'd be fine, of course. But we'd keep getLayoutBox() method itself? I made more notes on this below as well. Ideally this would be only size. However, in the meantime, what this API promises is to return "the last measured/known layout box".

@jridgewell jridgewell force-pushed the fixed-position-intersection-observer-part-2 branch from 2a2132c to 09c2437 Compare October 18, 2016 18:43
This makes the Resource’s `layoutBox` aware of fixed-position behavior.
We now store the _relative_ position to the viewport for
fixed-positions. When the element requests it’s current `layoutBox`,
we’ll return the absolute position from (0, 0) based on the current
`scrollTop` and `scrollLeft`.
@jridgewell jridgewell force-pushed the fixed-position-intersection-observer-part-2 branch from 09c2437 to d8b2b48 Compare October 18, 2016 18:43
@jridgewell jridgewell force-pushed the fixed-position-intersection-observer-part-2 branch from a252937 to 3869f24 Compare October 18, 2016 21:02
@jridgewell
Copy link
Contributor Author

@dvoytenko: Can you take one last look, added two more commits for testing.

@dvoytenko
Copy link
Contributor

LGTM

@jridgewell jridgewell merged commit feb5bfc into ampproject:master Oct 19, 2016
@jridgewell jridgewell deleted the fixed-position-intersection-observer-part-2 branch October 19, 2016 19:33
Lith pushed a commit to Lith/amphtml that referenced this pull request Dec 22, 2016
…oject#5624)

* tmp

* Make layoutBox rects aware of fixed position movement

This makes the Resource’s `layoutBox` aware of fixed-position behavior.
We now store the _relative_ position to the viewport for
fixed-positions. When the element requests it’s current `layoutBox`,
we’ll return the absolute position from (0, 0) based on the current
`scrollTop` and `scrollLeft`.

* Fix out of order assignment

* Re-add #layoutWidth. Accidental deletion.

* Fix const assignments

* Fix calcTaskScore_ tests for fixeds

* Fix setting initialLayoutBox_
Lith pushed a commit to Lith/amphtml that referenced this pull request Dec 22, 2016
…oject#5624)

* tmp

* Make layoutBox rects aware of fixed position movement

This makes the Resource’s `layoutBox` aware of fixed-position behavior.
We now store the _relative_ position to the viewport for
fixed-positions. When the element requests it’s current `layoutBox`,
we’ll return the absolute position from (0, 0) based on the current
`scrollTop` and `scrollLeft`.

* Fix out of order assignment

* Re-add #layoutWidth. Accidental deletion.

* Fix const assignments

* Fix calcTaskScore_ tests for fixeds

* Fix setting initialLayoutBox_
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants