Skip to content

Conversation

zhouyx
Copy link
Contributor

@zhouyx zhouyx commented Aug 5, 2017

Instead of extending from an abstract position observer class, the difference between inabox or ampdoc are handled in Viewport class. Thus the PositionObserver should be identical in both cases.

@zhouyx zhouyx requested review from aghassemi and lannka August 5, 2017 01:06
@zhouyx zhouyx force-pushed the non-AMP-host-posob branch 2 times, most recently from adae0fa to f636fd1 Compare August 7, 2017 22:56
@zhouyx zhouyx requested a review from jridgewell August 8, 2017 18:57
@zhouyx zhouyx force-pushed the non-AMP-host-posob branch from c81b1bd to bea519d Compare August 9, 2017 20:37
@zhouyx
Copy link
Contributor Author

zhouyx commented Aug 9, 2017

PTAL

Copy link
Contributor

@aghassemi aghassemi left a comment

Choose a reason for hiding this comment

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

Awesome refactoring! few small requests.

}

connect() {
//console.log('connect but do nothing! should fail');
Copy link
Contributor

Choose a reason for hiding this comment

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

remove


import {Services} from '../../src/services';
import {registerServiceBuilderForDoc} from '../../src/service';
import {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: one line for single imports

* @param {function(?)} unusedCallback
* @return {function()}
*/
onHostMessage(unusedCallback) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit {}



/**
* @implements {PosObViewportInfoDef}
Copy link
Contributor

Choose a reason for hiding this comment

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

naming suggestions:
PositionObserverHostInterface
PositionObserverNonAmpHost
etc...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Haha I am really bad at naming.
changed to PosObHostInterfaceDef, then PosObAmpdocHostInterface PosObInaboxHostInterface PosObNonAmpHostInterface

// If not receive iframe position from host, or if iframe is outside vp
return null;
}
if (!element.inIframePositionRect) {
Copy link
Contributor

Choose a reason for hiding this comment

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

can this really happen? like getLayoutRect be called before inIframePositionRect is set for InABoxCase?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, but not because getLayoutRect is called before.
It happens when the old inIframePositionRect is outdated, and we clear the stored value. For example, if the inabox iframe gets resized, we would want to refresh the inIframePositionRect value.

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. So to make sure I understand this right: I was expecting another AmpDocPositionObserver in here monitoring the element but I guess in-a-box is not scrollable (which will likely stay true for forseeable future) so the only real event that can invalidate this is a resize change and the host handles that. Correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct!

if (prePos
&& layoutRectEquals(prePos.positionRect, position.positionRect)
&& layoutRectEquals(prePos.viewportRect, position.viewportRect)) {
// position doesn't change, do nothing.
Copy link
Contributor

Choose a reason for hiding this comment

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

didn't


/**
* This should always be called in vsync.
* @param {boolean=} force
Copy link
Contributor

Choose a reason for hiding this comment

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

this is opt too 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.

Yes. force can be used to support fidelity. Viewport resize event for example would require we force update all entries, while viewport scroll doesn't

Copy link
Contributor

Choose a reason for hiding this comment

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

ok opt_force then?

* @param {!Element} element
*/
unobserve(element) {
for (let i = 0; i < this.entries_.length; i++) {
Copy link
Contributor

Choose a reason for hiding this comment

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

filterSplice in array.js can do this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not exactly. filterSplice splice every array item that filter false. Here we can use some optimization to only splice the first item that filter false.
Do you think it is worth to create a new function for this?

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. Neither the short-circuitingnot not using filterSplice here are a big deal here. Up to you.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yup, #filterSplice is meant only for multi-removals.

/** @private {function()} */
this.boundStopScroll_ = debounce(this.win_, () => {
this.inScroll_ = false;
}, 500);
Copy link
Contributor

Choose a reason for hiding this comment

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

const for 500

// Need to notify that element gets outside viewport
// NOTE: This is required for inabox position observer.
this.position = null;
position.positionRect = null;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we mutating an object we don't own?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because we don't want to expose the element position when it gets out of viewport. So set its positionRect to null.

/** @private {!Window} */
this.win_ = win;

/** @private {!Array<!Object>} */
Copy link
Contributor

Choose a reason for hiding this comment

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

Update the typedef.

unobserve(element) {
for (let i = 0; i < this.entries_.length; i++) {
if (this.entries_[i].element == element) {
this.entries_[i].handler = null;
Copy link
Contributor

Choose a reason for hiding this comment

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

This shouldn't be necessary.

for (let i = 0; i < this.entries_.length; i++) {
const entry = this.entries_[i];
if (opt_remeasure) {
entry.element.inIframePositionRect = null;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why does an Element have a #inIframePositionRect ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

😢 I don't like this either. But in the case of inabox, I know an element's relative pos to the inabox window is rarely changed. So I store the value in elemenet['inIframePositionRect'] (need avoid using .) to avoid unnecessary getBoundingClientRect() call. However in the case of inabox window resize I'll need to clear the stored value. That's why I set it back to null here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I realized it's really not good to store the info to element. I moved it to entry.inInframePositionRect.

continue;
}
// Reset entry.turn value.
if (!force) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: join this with the previous if statement.

* @param {!Event} event
* @private
*/
onHostMessageHandler_(event) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why does a standard position observer care about this?

Copy link
Contributor Author

@zhouyx zhouyx Aug 10, 2017

Choose a reason for hiding this comment

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

Two types of PositionObserver

  • Top doc level PosOb where the doc viewport is the top level viewport. (not needed)
  • Iframe PosOb where it requires position message from host doc (need this handler)

I am trying to let the PositionObserver to work in all scenarios, and that's why I have this #onHostMessageHandler_.

this.viewport_.storeIframePosition(iframePosition);

// do this in vsyn.measure
this.vsync_.measure(() => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Call #schedulePass_.

*/
onResizeHandler_() {
this.needRefreshOnMessage_ = true;
this.vsync_.measure(() => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Call #schedulePass_.

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 in my current code, #schedulePass_ actively remeasure element position in every animation frame until scroll stops. I can modify the #schedulePass_ function, let me know if you like that better, would become #schedulePass_(isOneTime)

@zhouyx zhouyx force-pushed the non-AMP-host-posob branch from ebb29ab to 9272904 Compare August 11, 2017 17:45
@zhouyx
Copy link
Contributor Author

zhouyx commented Aug 11, 2017

See #10995. We will need the inabox position api anyway.

Sync with @aghassemi and @lannka offline. Somehow decided to switch from Solution B to solution A.

Solution A
image

Parent window inform iframe window to start observe position through postMessage (‘start’, ‘end’)
Position Observer in CORS iframe request positions via a series of postMessages (‘send-position’)

  • Pros:

    Can embed different logic to viewport. Ampdoc and Inabox will both have the same logic.
    Easy to handle fidelity at the iframe side.
    Works well for inabox in non AMP host case. With very straightforward logic in non AMP host.

  • Cons:

    AMP runtime needs to listen to send position request all the time.
    Though we send ‘start’ ‘end’ signal to CORS, 3P iframe can request position any time.

Solution B
image

CORS iframe register the iframe.
Parent window send positions to iframe window at certain fidelity while iframe position is being updated.

  • Pros:

    AMP runtime handles sending positions.
    Less abuse of the API.

  • Cons:

    Optimization logic lives in non AMP host. We want to keep it as simple as possible.
    Ampdoc and inabox position observer will have different logic.

@zhouyx zhouyx force-pushed the non-AMP-host-posob branch from 9272904 to 7314b7e Compare August 25, 2017 16:02
} from '../../../src/layout';
import {
installPositionObserverServiceForDoc,
} from '../../../src/service/position-observer/position-observer-impl';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since the position-observer-impl.js position-observer-entry.js are much cleaner after the cleanup, I am OK if we want to move them to one single file. So that we don't need to import from different files or whitelist multiple files.

Ideas?

// This code is here for demo purposes and enables the required experiments
// automatically for this demo so that visitors do not need to enable them manually.
var experiments = ['amp-animation', 'amp-position-observer'];
var experiments = ['amp-animation', 'amp-position-observer', 'inabox-position-api'];
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@aghassemi Is the scrollbound animation already supported. Basically with the new refractor we need to toggle on inabox-position-api to enable position observer in inabox. Let me know if I should remove the experiment flag in this PR

Copy link
Contributor

Choose a reason for hiding this comment

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

scrollbound is still under an experiment flag but will be removed soon. if you are okay with removing inabox-position-api experiment, go for it. otherwise mention it in #10875 so they get removed at the same time

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll mention there : )

@zhouyx
Copy link
Contributor Author

zhouyx commented Aug 25, 2017

I rewrite the PR with #10987 and #11017 in. Ready for review, PTAL : )

* @typedef {{
* positionRect: ?../../layout-rect.LayoutRectDef,
* viewportRect: !../../layout-rect.LayoutRectDef,
* relativePos: string,
Copy link
Contributor

Choose a reason for hiding this comment

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

let's make this of type RelativePositions

Copy link
Contributor Author

@zhouyx zhouyx Aug 26, 2017

Choose a reason for hiding this comment

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

I added a TODO here. Since relativePos is being used in several files. I would rather make this change a following PR.

*/
trigger(position) {

const prePos = this.prevPosition_ ;
Copy link
Contributor

Choose a reason for hiding this comment

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

prevPos

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done


// Add the relative position of the element to its viewport
position.relativePos = layoutRectsRelativePos(
/** @type {!../../layout-rect.LayoutRectDef} */ (position.positionRect),
Copy link
Contributor

Choose a reason for hiding this comment

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

if position.positionRect can't be null, do an assert and assign to a constant so type casting happens only once.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

try {
this.handler_(position);
} catch (err) {
// TODO(@zhouyx, #9208) Throw error.
Copy link
Contributor

Choose a reason for hiding this comment

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

why catch if rethrowing anyway?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hmmm you're right! remove try catch block

PositionObserverFidelity,
LOW_FIDELITY_FRAME_COUNT,
} from './position-observer-fidelity';
/** @const @private */
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: new line above

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed


this.entries_.push(entry);

if (this.entries_.length == 1) {
Copy link
Contributor

Choose a reason for hiding this comment

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

this.entries_.length == 1 was confusing to me initially had to think to see why. Can we track through another variable? like callbackStarted_?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added

const bPromise = this.binding_.getLayoutRectAsync(el, 0, 0);
const cPromise = this.binding_.getLayoutRectAsync(
frameElement, scrollLeft, scrollTop);
const cPromise = this.binding_.getLayoutRectAsync(frameElement);
Copy link
Contributor

Choose a reason for hiding this comment

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

/cc @dvoytenko for the not passing scroll values anymore as I am not sure what the effects of it would be.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I found if scrollTop/scrollLeft are not passed in, binding will calculate latest scrollTop/scrollLeft. That's what I want.

Copy link
Contributor

Choose a reason for hiding this comment

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

ok, please ping @dvoytenko for the implications of this before merging

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 sounds odd. A big change from the original change. Please clarify.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added two new methods to Viewport class:
#getLayoutRectAsync() and #getBoundingRectAsync(). The idea is that in most cases where an element wants to know its layout rect, #getLayoutRectAsync() should be used instead of #getLayoutRect(). And #getLayoutRectAsync() always return the latest rect in certain animation frame.

To implement this, I added a #getLayoutRectAsync() function to ViewportBindingDef class. Here I am trying to implement Viewport #getLayoutRectAsync() with ViewportBindingDef #getLayoutRectAsync() method. I am not passing scroll values here. Because I want to get the latest layout rect, and don't want to use any cached value here. (I just realized this may not work in ViewportBindingNaturalIosEmbed_ case, I'll fix that)

WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

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

This should definitely be using the cached values, they should always be correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

From what I found they are not always correct when we try to calculate rect at a higher rate than browser 'scroll' event. See https://github.com/ampproject/amphtml/blob/master/src/service/viewport-impl.js#L258 that Viewport class use the cached scrollTop_ value when calling #getScrollTop(). But viewport.scrollTop_ is only updated on browser scroll event (throttled already). https://github.com/ampproject/amphtml/blob/master/src/service/viewport-impl.js#L889.

To get the latest layout rect in each animationFrame, I can't use the cached values.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jridgewell @dvoytenko Maybe we can talk about this offline? My plan to to have Viewport provide functions to get latest rect info at every animationFrame.

// This code is here for demo purposes and enables the required experiments
// automatically for this demo so that visitors do not need to enable them manually.
var experiments = ['amp-animation', 'amp-position-observer'];
var experiments = ['amp-animation', 'amp-position-observer', 'inabox-position-api'];
Copy link
Contributor

Choose a reason for hiding this comment

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

scrollbound is still under an experiment flag but will be removed soon. if you are okay with removing inabox-position-api experiment, go for it. otherwise mention it in #10875 so they get removed at the same time

@zhouyx
Copy link
Contributor Author

zhouyx commented Aug 26, 2017

comment addressed

@zhouyx
Copy link
Contributor Author

zhouyx commented Aug 28, 2017

@jridgewell PTAL : )

for (let i = 0; i < this.entries_.length; i++) {
const entry = this.entries_[i];
if (entry.element == element) {
entry.fidelity = fidelity;
Copy link
Contributor

Choose a reason for hiding this comment

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

This if statement should be a method on Entry.

this.startCallback_();
}

this.callbackStarted_ = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: move into if statement.

* @param {!Element} element
*/
unobserve(element) {
for (let i = 0; i < this.entries_.length; i++) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Yup, #filterSplice is meant only for multi-removals.

*/
updateAllEntries(opt_force) {
for (let i = 0; i < this.entries_.length; i++) {
const entry = this.entries_[i];
Copy link
Contributor

@jridgewell jridgewell Aug 28, 2017

Choose a reason for hiding this comment

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

This body statement should be a method on Entry.

Copy link
Contributor

Choose a reason for hiding this comment

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

Still belongs as a method on Entry.

// P2: do passes on onDomMutation (if available using MutationObserver
// mostly for in-a-box host, since most DOM mutations are constraint to the
// AMP elements).
this.updateAllEntries();
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this wait for a measure?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

#updateAllEntries() call #updateSingleEntries() then call #Viewport.getBoundingRectAsync(). which returns rect in next vsync measure.

if (!this.inScroll_) {
// Stop measure if viewport is no longer scrolling
this.measure_ = false;
return;
Copy link
Contributor

Choose a reason for hiding this comment

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

The positions could still change by this time (without firing a new scroll event). We should probably remeasure again, but not issue another vsync.

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 I call updateAllEntries() before checking this.inScroll_. I think that address your comment here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Lol, ignore me.

}

getRectAsync() {
return this.vsync_.measurePromise(() => {
Copy link
Contributor

Choose a reason for hiding this comment

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

This should return a memoized promise until the vsync executes (it'll save a lot of memory).

const bPromise = this.binding_.getLayoutRectAsync(el, 0, 0);
const cPromise = this.binding_.getLayoutRectAsync(
frameElement, scrollLeft, scrollTop);
const cPromise = this.binding_.getLayoutRectAsync(frameElement);
Copy link
Contributor

Choose a reason for hiding this comment

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

This should definitely be using the cached values, they should always be correct.

@zhouyx zhouyx force-pushed the non-AMP-host-posob branch from 1134c19 to 8899bc9 Compare September 5, 2017 21:32
@zhouyx
Copy link
Contributor Author

zhouyx commented Sep 5, 2017

I think #11148 unblock the work here. Changed the PR. PTAL : )

* @param {!PositionInViewportEntryDef} position
*/
trigger(position) {

Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: no newline.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

deleted

return;
}

dev().assert(position.positionRect);
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: add a message.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added


dev().assert(position.positionRect);
const positionRect =
/** @type {!../../layout-rect.LayoutRectDef} */ (position.positionRect);
Copy link
Contributor

Choose a reason for hiding this comment

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

This would be cleaner if the position we passed to trigger weren't nullable, and the one we pass to handler was. Or, we just pass two objects to the handler.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

position is always NOT nullable. position.postionRect is be nullable.
The problem is I want to pass position.positionRect = null to handler for the first time. After that I don't want to invoke handler with another positionRect null.

Copy link
Contributor

Choose a reason for hiding this comment

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

position.postionRect is be nullable.

That's what I meant.

The problem is I want to pass position.positionRect = null to handler for the first time.

Don't you mean when there's no intersection? It can still happen that way by using a second type, or passing two arguments to the handler function.

Copy link
Contributor Author

@zhouyx zhouyx Sep 7, 2017

Choose a reason for hiding this comment

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

emmm. I sorry I kind of get lost here.
So what you want is have position.positionRect NOT nullable when passed to #trigger(), but have it nullable when passed to #handler()? This is what I have now.

Yes I can definitely change the arguments to the handler function. But now handler function expects single object, and I think our code is already depending on that return type. Dima proposed some more general object type that we might migrate to. But that's following work then.

this.entries_.splice(i, 1);
if (this.entries_.length == 0) {
this.stopCallback_();
this.callbackStarted_ = false;
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: this can be moved into start and stop callbacks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good idea! done


if (!this.callbackStarted_) {
this.startCallback_();
this.callbackStarted_ = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: this can be moved into start and stop callbacks.

*/
updateAllEntries(opt_force) {
for (let i = 0; i < this.entries_.length; i++) {
const entry = this.entries_[i];
Copy link
Contributor

Choose a reason for hiding this comment

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

Still belongs as a method on Entry.

const positionRect =
/** @type {!../../layout-rect.LayoutRectDef} */ (position.positionRect);
// Add the relative position of the element to its viewport
position.relativePos = layoutRectsRelativePos(positionRect,
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this being calculated here?

Copy link
Contributor Author

@zhouyx zhouyx Sep 7, 2017

Choose a reason for hiding this comment

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

Hmmm that's how we define (at least currently) the return value of PositionObserverCallback

{
  viewportRect,
  positionRect,
  relativePos,
}

Because we will be setting positionRect to null when element gets out of viewport, we need to calculate its relativePosition to viewport (above/below) here, handler won't have enough data to calculate it.

for (let i = 0; i < this.entries_.length; i++) {
const entry = this.entries_[i];

if (opt_force) {
Copy link
Contributor

Choose a reason for hiding this comment

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

These two if statements belong in Entry#update as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah! 👍


dev().assert(position.positionRect);
const positionRect =
/** @type {!../../layout-rect.LayoutRectDef} */ (position.positionRect);
Copy link
Contributor

Choose a reason for hiding this comment

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

position.postionRect is be nullable.

That's what I meant.

The problem is I want to pass position.positionRect = null to handler for the first time.

Don't you mean when there's no intersection? It can still happen that way by using a second type, or passing two arguments to the handler function.

LOW_FIDELITY_FRAME_COUNT,
} from './position-observer-fidelity';
PositionObserverEntry,
/* eslint no-unused-vars: 0 */ PositionObserverFidelity,
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Appending Def to the name will skip the lint error.

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 the value is an enum not a Def 😕 Should i do that.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, this is just being used as a typedef. Just use the ./position-observer-entry.PositionObserverFidelity types instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's the first thing I try.
Get

Bad type annotation. Unknown type module$src$service$position_observer$position_observer_entry.PositionObserverFidelity

Strange that ./position-observer-entry.PositionInViewportEntryDef works but ./position-observer-entry.PositionObserverFidelity doesn't.

Copy link
Contributor

Choose a reason for hiding this comment

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

That's likely during the extension compiling. What's the full output?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

src/service/position-observer/position-observer-impl.js:73: ERROR - Bad type annotation. Unknown type module$src$service$position_observer$position_observer_entry.PositionObserverFidelity
   * @param {!./position-observer-entry.PositionObserverFidelity} fidelity

get this running gulp check-types

this.turn--;
return;
}
this.turn = (this.fidelity == PositionObserverFidelity.LOW) ?
Copy link
Contributor

Choose a reason for hiding this comment

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

This is slightly different than the old code. Now, when a force update comes in, all low fidelity Entries will be synchronized to the same frame turn. Using two nested if statements cleans it up:

if (!opt_force) {
  if (this.turn > 0) {
    this.turn--;
    return;
  }
  
  // An if statement just seems nicer than the assignment conditional
  if (this.fidelity == LOW) {
    this.turn = 4;
  }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. Talked offline, will move back to old behavior. But we may need future discussion on whether we should randomize entry.turn or sync all low fidelity elements' measurement

*/
export let PositionInViewportEntryDef;

export class PositionObserverEntry {
Copy link
Contributor

Choose a reason for hiding this comment

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

-> xxxWorker

* @param {!Element} element
* @param {PositionObserverFidelity} fidelity
*/
changeFidelity(element, fidelity) {
Copy link
Contributor

Choose a reason for hiding this comment

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

can we remove?

@zhouyx zhouyx force-pushed the non-AMP-host-posob branch from bb18853 to 5a4370e Compare September 8, 2017 20:40
@zhouyx zhouyx merged commit a832f26 into ampproject:master Sep 8, 2017
@zhouyx zhouyx deleted the non-AMP-host-posob branch September 8, 2017 21:10
@zhouyx
Copy link
Contributor Author

zhouyx commented Sep 8, 2017

fix #8755

@zhouyx zhouyx mentioned this pull request Sep 16, 2017
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants