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

amp-fx-collection: parallax #13073

Merged
merged 11 commits into from
Jan 30, 2018
Merged

Conversation

aghassemi
Copy link
Contributor

@aghassemi aghassemi commented Jan 26, 2018

amp-fx-collection extension brings a collection of easy-to-use visual effects to AMP.
parallax is the first one to get implement. Others planned include fade and slide animations.

<h1 amp-fx="parallax" data-parallax-factor="1.4">
<amp-img amp-fx="parallax" data-parallax-factor="0.5">

-This will replace the existing experimental amp-fx-parallax component. (Separate PR to remove parallax)
-Documentation and tests to follow.
Closes #7928
Closes #1443
Closes #7925

parallax

/cc @ericlindley-g

Copy link
Contributor

@honeybadgerdontcare honeybadgerdontcare left a comment

Choose a reason for hiding this comment

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

Validator changes look fine.

@@ -0,0 +1,38 @@
PASS
| <!--
| Copyright 2016 The AMP HTML Authors. All Rights Reserved.
Copy link
Contributor

Choose a reason for hiding this comment

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

2018

@@ -0,0 +1,37 @@
<!--
Copyright 2016 The AMP HTML Authors. All Rights Reserved.
Copy link
Contributor

Choose a reason for hiding this comment

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

2018

-->
<!--
Test Description:
Tests for the amp-fx-collection tag. See the inline comments.
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove "See the inline comments." since there aren't any.

| -->
| <!--
| Test Description:
| Tests for the amp-fx-collection tag. See the inline comments.
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove "See the inline comments." since there aren't any.

* Map of fx type to fx provider class.
* @type {Object<FxType, function(new:FxProviderInterface, !../../../src/service/ampdoc-impl.AmpDoc)>}
*/
const fxProviders = map();
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 use computed properties?

const fxProviders = map({
  [FxType.PARALLAX]: ParallaxProvider,
});

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Whoa, TIL: using [] in the key will evaluate it. Yikes?
@jridgewell As our TC-39 resident, you get to see me complain about JS now :).
How is this syntax consolidated with native JS Map that can have object keys? What would var x = new Map({ [Key]: Val }) do? I would have expected key to be an array.

Copy link
Contributor

Choose a reason for hiding this comment

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

Huh? {[KEY]: VALUE} is called a computed key, so of course it evaluates the key...

const KEY = 'key';
const computed = {
  [KEY]: 'value',
};
assert.deepEquals(computed, { key: 'value' });

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah but wouldn't using [] to mean compute vs to mean array conflict with native Map that supports any object (including arrays) as keys? How does the new native Map know what I mean by x = new Map({ [Key]: Val }), what if I actually want the array [Key] to be the key for my Val?

Copy link
Contributor

Choose a reason for hiding this comment

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

Maps take an iterable (so arrays), not an object.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oh, I see. still pretty ugly that [] has a second meaning. Supporting template literals as keys so dev can start migrating over seems like a decent choice, not sure if that is something they are considering

const computed = {
   `${key}`: 'value'
}

Copy link
Contributor

Choose a reason for hiding this comment

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

The intention is to mirror bracket access:

let obj = {};
obj[some_expresion] = value;

// ES6
let obj = {
  [some_expression]: value,
};

dev().assert(!this.seen_.includes(fxElement));
dev().assert(this.viewer_.isVisible());

/** @type {!Array<!FxType>} */
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 /** @type */ is superfluous since Closure can infer the type from the return type of the function below

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

scan_() {
const fxElements = this.root_.querySelectorAll('[amp-fx]');
iterateCursor(fxElements, fxElement => {
if (this.seen_.includes(fxElement)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Where are items added to this.seen_? should that happen in register_? Or in a subclass?

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 is what happens when there are no unit tests.

const viewportHeight = this.viewport_.getHeight();

let offsetTop = 0;
for (let n = this.element_; n; n = n./*OK*/offsetParent) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: use a more descriptive variable name than n to help readers understand faster

}
const aboveTheFold = (offsetTop < viewportHeight);

if (aboveTheFold) {
Copy link
Contributor

Choose a reason for hiding this comment

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

style nit, super optional: convert if/else to ternary for brevity

this.adjustedViewportHeight_ = this.getAdjustedViewportHeight_();

// start observing position of the element.
this.observePositionChanges_();
Copy link
Contributor

Choose a reason for hiding this comment

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

I notice that some of your constructors in this PR mutate global state, e.g. adding event listeners, installing services. You can mutate global state, but then you have to stub those functions in tests which makes writing tests more difficult. I think that having pure constructors may make testing easier, and just have the consumers of the class call the state-mutating functions like observePositionChanges().

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

*/
installOn(element) {
Services.vsyncFor(this.ampdoc_.win).measure(() => {
new ParallaxElement(element, this.viewport_, this.positionObserver_);
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 not obvious why this constructor call is wrapped in a measure. Is it because the constructor calls getAdjustedViewportHeight? If so, I would suggest having getAdjustedViewportHeight return a promise from vsync.measurePromise 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.

done

/** @private @const {!Object<FxType, FxProviderInterface>} */
this.fxProviderInstances_ = map();

ampdoc.whenReady().then(() => this.viewer_.whenFirstVisible()).then(() => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the whenReady necessary? Does the viewer ever resolve whenFirstVisible before whenReady is resolved? If either one could happen first, maybe something like this would better communicate your intent.

Promise.all([
  ampdoc.whenReady(),
  this.viewer_.whenFirstVisible(),
]).then(/*...*/)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah whenFirstVisible is purely based on a message received from the viewer so I assumed it can happen at any moment. Switched to Promise.all

@aghassemi
Copy link
Contributor Author

@cvializ Thanks, PTAL.

Copy link
Contributor

@cvializ cvializ left a comment

Choose a reason for hiding this comment

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

LGTM!

// Translate the element offset pixels.
// No need for vsync mutate, position observer only calls back at most
// every animation frame.
setStyles(this.element_,
Copy link
Contributor

Choose a reason for hiding this comment

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

I thought the purpose of vsync mutate was not to batch individual components' calls, but to batch mutations across all components that might manipulate the DOM at any moment? Wouldn't you still need it here to coordinate with other AMP components manipulating the DOM? I'm still trying to learn when it is and isn't needed 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Your assessment is correct but we still don't need it here since we are only changing transform which is done at composition layer and won't cause relayouts. Updated the comments (it also reminded me to add 'will-change': 'transform' to the element as well) . Using vsyncMutate would make us be one frame behind which doesn't feel as nice.

Copy link
Contributor

Choose a reason for hiding this comment

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

kk thanks for explaining

@aghassemi aghassemi merged commit b685a07 into ampproject:master Jan 30, 2018
Gregable pushed a commit that referenced this pull request Feb 7, 2018
Gregable added a commit that referenced this pull request Feb 7, 2018
* Revision bump for #13036

* Revision bump for #13073

* Add DEDUPE_ON_MINIFY flags to license tags so that release process can
reduce the number of identical duplicate licenses in the minified
validator.

* Add requires_extension to AttrSpec.

* JSDoc updates.

* Generated validator javascript improvements.

* Add comment to ValidationError hinting at how to render.

* Revision bump for #12955

* Add new error types for future CSS validation.

* Revision bump for #12798

* Fix a typo.

* Allow animation-timing-function for keyframes

* Fix typo
gzgogo pushed a commit to gzgogo/amphtml that referenced this pull request Feb 14, 2018
gzgogo pushed a commit to gzgogo/amphtml that referenced this pull request Feb 14, 2018
gzgogo pushed a commit to gzgogo/amphtml that referenced this pull request Feb 14, 2018
protonate pushed a commit to protonate/amphtml that referenced this pull request Feb 26, 2018
* Revision bump for ampproject#13036

* Revision bump for ampproject#13073

* Add DEDUPE_ON_MINIFY flags to license tags so that release process can
reduce the number of identical duplicate licenses in the minified
validator.

* Add requires_extension to AttrSpec.

* JSDoc updates.

* Generated validator javascript improvements.

* Add comment to ValidationError hinting at how to render.

* Revision bump for ampproject#12955

* Add new error types for future CSS validation.

* Revision bump for ampproject#12798

* Fix a typo.

* Allow animation-timing-function for keyframes

* Fix typo
RanAbram pushed a commit to RanAbram/amphtml that referenced this pull request Mar 12, 2018
RanAbram pushed a commit to RanAbram/amphtml that referenced this pull request Mar 12, 2018
* Revision bump for ampproject#13036

* Revision bump for ampproject#13073

* Add DEDUPE_ON_MINIFY flags to license tags so that release process can
reduce the number of identical duplicate licenses in the minified
validator.

* Add requires_extension to AttrSpec.

* JSDoc updates.

* Generated validator javascript improvements.

* Add comment to ValidationError hinting at how to render.

* Revision bump for ampproject#12955

* Add new error types for future CSS validation.

* Revision bump for ampproject#12798

* Fix a typo.

* Allow animation-timing-function for keyframes

* Fix typo
protonate pushed a commit to protonate/amphtml that referenced this pull request Mar 15, 2018
protonate pushed a commit to protonate/amphtml that referenced this pull request Mar 15, 2018
* Revision bump for ampproject#13036

* Revision bump for ampproject#13073

* Add DEDUPE_ON_MINIFY flags to license tags so that release process can
reduce the number of identical duplicate licenses in the minified
validator.

* Add requires_extension to AttrSpec.

* JSDoc updates.

* Generated validator javascript improvements.

* Add comment to ValidationError hinting at how to render.

* Revision bump for ampproject#12955

* Add new error types for future CSS validation.

* Revision bump for ampproject#12798

* Fix a typo.

* Allow animation-timing-function for keyframes

* Fix typo
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.

5 participants