Skip to content

Adding margin support to attemptChangeSize#6824

Merged
dvoytenko merged 1 commit intoampproject:masterfrom
tlong2:attempt-size-change-margins
Jan 13, 2017
Merged

Adding margin support to attemptChangeSize#6824
dvoytenko merged 1 commit intoampproject:masterfrom
tlong2:attempt-size-change-margins

Conversation

@tlong2
Copy link
Copy Markdown
Contributor

@tlong2 tlong2 commented Dec 28, 2016

Allows the margins on an element to be changed via the attemptChangeSize function in resources-impl.js.

#6626

@jridgewell
Copy link
Copy Markdown
Contributor

/to @dvoytenko

@jridgewell jridgewell requested a review from dvoytenko January 3, 2017 19:04
setStyle(this, 'width', newWidth, 'px');
}
if (opt_newMargins) {
setStyle(this, 'marginTop', opt_newMargins.top, 'px');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we should allow each component to be undefined and avoid overriding here. This is inline with similar use cases.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

* The structure that represents the margins of an Element.
*
* @typedef {{
* top: number,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As described above, let's do (number|undefined)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added a new type for specify the changes that allows undefined.

changeSize(element, newHeight, newWidth, opt_callback) {
this.scheduleChangeSize_(Resource.forElement(element), newHeight,
newWidth, /* force */ true, opt_callback);
newWidth, undefined, /* force */ true, opt_callback);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

/* opt_margins */ undefined

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

this.layoutBox_ = box;

const win = this.resources_.win;
const computedStyle = win./*OK*/getComputedStyle(this.element);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm wondering if we allow undefined values, would it be enough for us to just read the values from element.style? Calling getComputedStyle here for all elements (most of which do not care about resizing) is pretty expensive. If not - I think updating margin is a rare enough case and we should move it closer to the size updating code and only do this when margin changes have been requested.

Copy link
Copy Markdown
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 think element.style is sufficient; it would ignore anything that comes from the CSS style rules.

I've changed it so that the getLayoutMargins method calls getComputedStyle and reads the margins only when it's called and have also changed mutate() in resources-impl.js to only call getLayoutMargins() when there is actually a newMargins defined in the request. Does that seem OK?

Copy link
Copy Markdown
Contributor Author

@tlong2 tlong2 Jan 4, 2017

Choose a reason for hiding this comment

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

Although it occurs to me that calling getComputedStyle would probably have to be done somewhere inside a vsync_.measure call I guess?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yes, it has to be in the vsync.measure. I think we need to change this. Basically, we only need to read margins in a relatively rare case when margin change has been requested: they are not needed before or after otherwise. So, what we can do, is remove layoutMargins_ var and code from Resource file and modify this code here: https://github.com/ampproject/amphtml/blob/master/src/service/resources-impl.js#L1334

It'd be something like this:

if (resource.hasBeenMeasured() && !opt_newMargings) {
  // unchanged
} else {
  vsync.measure(() => {
    if (!resource.hasBeenMeasured()) {
     resource.measure();
    }
    let marginsDiff = null;
    if (opt_newMargin) {
      marginsDiff = // calculate existing margins and diff with opt_newMargin
    }
    this.completeScheduleChangeSize_(..., marginsDiff);
  });
}

And then we'd use marginsDiff in the resize code itself. E.g. marginsDiff && marginsDiff.top would mean that top margin has been modified, etc.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've done basically what you suggest. I've passed the existing and the new margins along, since we need the existing margins in mutateWork_() to know where the bottom displaced boundary is. So doesn't make much sense to pass a diff around just to turn it back into the original value later on.

// changed then it is the bottom edge of the margin box, otherwise it
// is the bottom edge of the layout box.
const bottomDisplacedBoundary = bottomMarginDiff ?
box.bottom + margins.bottom : box.bottom;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Shouldn't this be box.bottom + bottomDisplacedBoundary?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure what your suggestion is here?
The point in this is to record the boundary that will have a visible change as far as the user is concerned. If there is no margin change then it will only appear as though the bottom of the border-box has moved (which is box.bottom). If the bottom margin is also being resized then the spacing between this element and the next will also change.

So for example, if there is an element where box.bottom is <10% inside the top of the viewport and it has a bottom margin of 100px, then a pure height resize is fine (and would be allowed under the existing logic, as well as this new logic). If the bottom margin, however, is being resized then it's not fine, since this will make things within the main 80% of the viewport move around.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ok, got it. Let's do margin changes and I'll review this one more time - it should be good to go.

const box = resource.getLayoutBox();
const iniBox = resource.getInitialLayoutBox();
const diff = request.newHeight - box.height;
const margins = resource.getLayoutMargins();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

For simplicity, shouldn't we just do diff = (newHeight + newMargin) - (oldHeight + oldMargin) and operate with that as before? Why I'm asking is that naively, margin changes are visually very similar to padding changes and paddings would be captured inside the height. So it'd seem that the diff math should be the same?

Copy link
Copy Markdown
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 think just doing what you suggest is correct for either the case of changing padding or margins.

It seems that the existing logic uses the fact that the top edge of an element will not change as a result of a change in height (none of the rules for setting resize=true/false ever check what box.top is) and for changes to the height where margins and padding are held constant this is valid.

As far as a user is concerned the top edge of an element is the edge of whatever they can see. For the case of an image without a border, then this will be the top of the content box, for the case of something with a border or a background color this will be the border box (which is equivalent to layout box in AMP).

So in all cases, changing the top margin is more akin to re-positioning the element than it is to resizing it. And for elements with no border or background changing the top padding would also be more akin to re-positioning than resizing.

So changes in margins need to be considered somewhat separately from changes in height if we want to avoid user visible re-flows in the middle of the viewpoint.

setStyle(this, 'width', newWidth, 'px');
}
if (opt_newMargins) {
if (opt_newMargins.top) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should this be opt_newMargins.top != null? I assume 0 would be a valid value here, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

@@ -0,0 +1,56 @@
/**
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry for the furniture moving, but could we move these to layout-rect.js?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

marginChange, force, opt_callback);
});
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

"Returns the previously measured value..." is not longer true.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

@dvoytenko
Copy link
Copy Markdown
Contributor

@tlong2 Great! Just one small comment and I should be ready to merge. Thanks a lot!

* marginChange: (!MarginChangeDef|undefined),
* force: boolean,
* callback: (function(boolean)|undefined)
* }}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why aren't we giving changeSize a margins, too?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

}
if (bottomMarginDiff) {
// The lowest boundary of the element that would appear to be
// resized as a result of this size change. If the bottom margin is
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Shouldn't this include the topMarginDiff?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No. It's supposed to be the current position of the bottom boundary that will be displaced. This is used in the check that the bottom of the element is above the viewport (or within the 10% offset allowed). As long as the bottom displaced boundary meets this criteria then it doesn't matter what happens in terms of height changes of top margin changes, since the scroll adjusting will act such that nothing appears to change in the viewport except possibly in that allowed 10% region.

@dvoytenko dvoytenko merged commit 93b67b0 into ampproject:master Jan 13, 2017
@dvoytenko
Copy link
Copy Markdown
Contributor

Merged! Thanks a lot, @tlong2 !

rpominov pushed a commit to yandex-pcode/amphtml that referenced this pull request Jan 20, 2017
* master: (310 commits)
  Update csa.md to remove non-required parameters (ampproject#6902)
  Add notes about requesting ads ATF and link to demo (ampproject#7037)
  Remove whitelist for lightbox scrollable validator (ampproject#7034)
  Delegate submit events until amp-form is loaded  (ampproject#6929)
  Moves closure sha384 into a new extension amp-crypto-polyfill for lazy load (ampproject#7006)
  Refactor observables in viewer-impl into a map object (ampproject#7004)
  resizing of margins (ampproject#6824)
  Use URL replacer from embed for pixel (ampproject#7029)
  adds support for Gemius analytics (ampproject#6558)
  Avoid duplicating server-layout (ampproject#7021)
  Laterpay validator config (ampproject#6974)
  Validator rollup (ampproject#7023)
  skeleton for amp-tabs (ampproject#7003)
  Upgrade post-css and related packages to latest (ampproject#7020)
  handle unload (ampproject#7001)
  viewer-integr.js -> amp-viewer-integration (ampproject#6989)
  dev().info()->dev().fine() (ampproject#7017)
  Turned on experiment flag (ampproject#6774)
  Unlaunch ios-embed-wrapper for iOS8 to avoid scroll freezing issues (ampproject#7018)
  Add some A4A ad request parameters (ampproject#6643)
  ...
jridgewell pushed a commit to jridgewell/amphtml that referenced this pull request Jan 31, 2017
mrjoro pushed a commit to mrjoro/amphtml that referenced this pull request Apr 28, 2017
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.

3 participants