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
Animation builder and runner. #6092
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.
Still going through it, sending out some early comments.
|
||
/** @override */ | ||
activate() { | ||
if (!this.configJson_) { |
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.
Maybe move the config parsing to buildCallback for earlier error reporting and also should improve perceived performance as less JS would run between user-interaction and the animation start.
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
|
||
|
||
/** | ||
* @typedef {!WebMultiAnimationDef|!WebKeyframeAnimationDef} |
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.
Closure Compiler's New Type Inference (not enabled yet) will complain about this because of the circular dependencies between WebMultiAnimationDef and WebAnimationDef.
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 always understood circular dependencies to be fine. Can we confirm it early on?
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.
try gulp type-check --nti
(there will be lots of errors but you can search and see if this typedef is flagged)
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.
Just tried. Not sure if it prints all the errors, but nothing is reported against this case.
import {getVendorJsPropertyName} from '../../../src/style'; | ||
import {isArray, isObject} from '../../../src/types'; | ||
import { | ||
WebAnimationDef, |
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.
interesting way of doing this. Does it make sense to move the type defs to amp.extern?
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.
Not yet. But possible in the future. At the very least, I'm definitely anticipating we'll move the web-animation-types
to src
/** | ||
* @mixes WebAnimationTimingDef | ||
* @typedef {{ | ||
* animations: !Array<!WebAnimationDef>, |
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.
Wouldn't it be enough for this to be:
animations: !Array<!WebKeyframeAnimationDef>,
I'm not sure I see the point of the nesting otherwise and that should solve the circular dependency mentioned before.
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 though that it might be convenient to sometimes apply some animations parameters to all sub-animations. E.g. duration
, delay
, etc - to avoid copy-pasting them when they are exactly the same.
But if that seems like an over-complication - I'll reconsider.
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.
Also, another nuance, I wanted all of the following to be valid configs:
Shortest key-frame only config:
{
target: target1,
duration: 1000,
keyframes: {opacity: 0}
}
An array of animations:
[
{
target: target1,
duration: 1000,
keyframes: {opacity: 1}
},
{
target: target2,
duration: 500,
delay: 500,
keyframes: {opacity: 1}
}
]
And an object + array:
{
duration: 1000,
animations: [
{
target: target1,
keyframes: {opacity: 1}
},
{
target: target2,
duration: 500,
delay: 500,
keyframes: {opacity: 1}
}
]
}
WDYT?
|
||
|
||
/** | ||
* @typedef {!Object<string, *>|!Array<!Object<string, *>>} |
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.
Doesn't it make sense to make this use a typedef to clearly document that keyframes are only alowed to modify opacity and transform?
@typedef {{
offset: number,
opacity: number,
transform: string
}}
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.
Hmm. Good question. In other words, you'd like to enumerate all allowed right away? I was also going to include visibility
and background
to start with.
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.
A bit of a difficulty with typedef
based whitelist is that something like background-color
cannot be defined there. Perhaps we'll just go with a Object<string, *>
and clearly document whitelisted props?
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 clearly documented whitelist seems fine by me.
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
// Property -> keyframes form. | ||
const object = /** {!Object<string, *>} */ (spec.keyframes); | ||
keyframes = {}; | ||
for (const prop in 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.
This, and below, will allow any animatable property to be animated. Is validation that only opacity and transform animate happening somewhere else?
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.
Thanks for reminding! We'll add whitelist right away.
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
|
||
/** @param {!WebMultiAnimationDef} unusedSpec */ | ||
onMultiAnimation(unusedSpec) { | ||
dev().assert(null, 'not implemented'); |
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 there ever be an implementation here? This whole class looks like an @abstract
to me.
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 is. But I thought it was out pattern for @abstract
, since dev.assert
are removed from the binary. Is it not? Could you provide the right way?
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 was thinking to just annotate the class with @abstract
to make that clear.
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. Added to the class and methods as well.
keyframes.push(clone(frame)); | ||
} | ||
} else { | ||
// No a known form of keyframes spec. |
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 'a'
Took a bit of reading to get what onKeyframeAnimation
does, but it is fairly clear what it does after. Some summary comments on onKeyframeAnimation
may help future readers.
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.
Good idea. Will add.
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
*/ | ||
measure_(target, prop) { | ||
const index = this.targets_.indexOf(target); | ||
if (!this.computedStyleCache_[index]) { |
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 believe we need to invalidate this cache after animation run finishes otherwise a second animation run on specs that have fill-mode forward
or both
( or if style changes with a user interaction between the first and second run ) could yield the wrong 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.
This is meant to be fire-and-forget class for this reason. I think it should be natural for a scanner - it scans only once.
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 see
* @template T | ||
*/ | ||
function clone(x) { | ||
if (!x) { |
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.
JSON.parse(JSON.stringify(obj)); is simpler (and I would guess likely faster)
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.
Do you mean that would work better than clone
?
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 mean as an implementation of clone().
function clone(x) {
return JSON.parse(JSON.stringify(x));
}
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 agree. It'll also throw an error on cyclic dependencies, instead of the infinite loop we have now. 😀
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
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.
New code not pushed yet?
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.
Just pushed.
* @return T | ||
* @template T | ||
*/ | ||
function clone(x) { |
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.
move to /utils/clone.js
?
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 had it there :) Everyone hated it and it was eventually removed. In this case, however, we definitely need it... /cc @jridgewell
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.
🤷
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.
Ok. Reimplemented as JSON.parse(stringify)
/** @type {!WebKeyframesDef} */ | ||
let keyframes; | ||
|
||
if (isObject(spec.keyframes)) { |
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.
Holy parsing batman.
* @private | ||
*/ | ||
mergeTiming_(newTiming, prevTiming) { | ||
const duration = newTiming.duration != null ? |
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.
_.defaults
?
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 does a bit of a parsing here, in terms of Number()
and such.
@@ -306,15 +335,15 @@ export class MeasureScanner extends Scanner { | |||
|
|||
// Validate. | |||
if (this.validate_) { | |||
user().assert(!isNaN(duration), | |||
user().assert(duration >= 0, |
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.
you may want to call isFiniteNumber()
(from types.js
) for all of these numbers first ( since for instance '' >= 0 is true
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'm calling Number(duration)
above, so it can't be a ''
anymore.
* Animation scanner and runner. * presubmit fixes * tests * more tests * types fixed * docs * minor docs fixes
* Animation scanner and runner. * presubmit fixes * tests * more tests * types fixed * docs * minor docs fixes
* Animation scanner and runner. * presubmit fixes * tests * more tests * types fixed * docs * minor docs fixes
This is the first draft of animation builder and runner. Tests are on the way. Please do an early review.
Partial for #5910.
/cc @hellyhansen, @emarchiori