-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
amp-fx-collection: parallax #13073
Conversation
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.
Validator changes look fine.
@@ -0,0 +1,38 @@ | |||
PASS | |||
| <!-- | |||
| Copyright 2016 The AMP HTML Authors. All Rights Reserved. |
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.
2018
@@ -0,0 +1,37 @@ | |||
<!-- | |||
Copyright 2016 The AMP HTML Authors. All Rights Reserved. |
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.
2018
--> | ||
<!-- | ||
Test Description: | ||
Tests for the amp-fx-collection tag. See the inline comments. |
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.
Remove "See the inline comments." since there aren't any.
| --> | ||
| <!-- | ||
| Test Description: | ||
| Tests for the amp-fx-collection tag. See the inline comments. |
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.
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(); |
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.
Can we use computed properties?
const fxProviders = map({
[FxType.PARALLAX]: ParallaxProvider,
});
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.
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
.
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.
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' });
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.
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
?
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.
Map
s take an iterable (so arrays), not an object.
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.
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'
}
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.
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>} */ |
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: this /** @type */
is superfluous since Closure can infer the type from the return type of the function below
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
scan_() { | ||
const fxElements = this.root_.querySelectorAll('[amp-fx]'); | ||
iterateCursor(fxElements, fxElement => { | ||
if (this.seen_.includes(fxElement)) { |
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.
Where are items added to this.seen_
? should that happen in register_
? Or in a subclass?
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 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) { |
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 a more descriptive variable name than n
to help readers understand faster
} | ||
const aboveTheFold = (offsetTop < viewportHeight); | ||
|
||
if (aboveTheFold) { |
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.
style nit, super optional: convert if/else
to ternary for brevity
this.adjustedViewportHeight_ = this.getAdjustedViewportHeight_(); | ||
|
||
// start observing position of the element. | ||
this.observePositionChanges_(); |
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 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()
.
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
*/ | ||
installOn(element) { | ||
Services.vsyncFor(this.ampdoc_.win).measure(() => { | ||
new ParallaxElement(element, this.viewport_, this.positionObserver_); |
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.
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.
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
/** @private @const {!Object<FxType, FxProviderInterface>} */ | ||
this.fxProviderInstances_ = map(); | ||
|
||
ampdoc.whenReady().then(() => this.viewer_.whenFirstVisible()).then(() => { |
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 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(/*...*/)
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.
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
@cvializ Thanks, PTAL. |
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.
LGTM!
// Translate the element offset pixels. | ||
// No need for vsync mutate, position observer only calls back at most | ||
// every animation frame. | ||
setStyles(this.element_, |
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 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 😅
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.
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.
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.
kk thanks for explaining
* 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
* 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
* 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
* 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
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.-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
/cc @ericlindley-g