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

Animation builder and runner. #6092

Merged
merged 7 commits into from Nov 14, 2016
Merged

Animation builder and runner. #6092

merged 7 commits into from Nov 14, 2016

Conversation

dvoytenko
Copy link
Contributor

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

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.

Still going through it, sending out some early comments.


/** @override */
activate() {
if (!this.configJson_) {
Copy link
Contributor

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.

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



/**
* @typedef {!WebMultiAnimationDef|!WebKeyframeAnimationDef}
Copy link
Contributor

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.

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 always understood circular dependencies to be fine. Can we confirm it early on?

Copy link
Contributor

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)

Copy link
Contributor Author

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,
Copy link
Contributor

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?

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 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>,
Copy link
Contributor

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.

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 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.

Copy link
Contributor Author

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, *>>}
Copy link
Contributor

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
}}

Copy link
Contributor Author

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.

Copy link
Contributor Author

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?

Copy link
Contributor

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.

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

// Property -> keyframes form.
const object = /** {!Object<string, *>} */ (spec.keyframes);
keyframes = {};
for (const prop in object) {
Copy link
Contributor

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?

Copy link
Contributor Author

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.

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


/** @param {!WebMultiAnimationDef} unusedSpec */
onMultiAnimation(unusedSpec) {
dev().assert(null, 'not implemented');
Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor

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.

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. Added to the class and methods as well.

keyframes.push(clone(frame));
}
} else {
// No a known form of keyframes spec.
Copy link
Contributor

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.

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. Will add.

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

*/
measure_(target, prop) {
const index = this.targets_.indexOf(target);
if (!this.computedStyleCache_[index]) {
Copy link
Contributor

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

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 meant to be fire-and-forget class for this reason. I think it should be natural for a scanner - it scans only once.

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

* @template T
*/
function clone(x) {
if (!x) {
Copy link
Contributor

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)

Copy link
Contributor Author

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?

Copy link
Contributor

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));
}

Copy link
Contributor

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. 😀

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

Copy link
Contributor

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?

Copy link
Contributor Author

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) {
Copy link
Contributor

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 ?

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 had it there :) Everyone hated it and it was eventually removed. In this case, however, we definitely need it... /cc @jridgewell

Copy link
Contributor

Choose a reason for hiding this comment

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

🤷

Copy link
Contributor Author

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)) {
Copy link
Contributor

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 ?
Copy link
Contributor

Choose a reason for hiding this comment

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

_.defaults?

Copy link
Contributor Author

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,
Copy link
Contributor

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

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'm calling Number(duration) above, so it can't be a '' anymore.

@dvoytenko dvoytenko merged commit 9058780 into ampproject:master Nov 14, 2016
@dvoytenko dvoytenko deleted the anim2 branch November 14, 2016 21:54
dreamofabear pushed a commit to dreamofabear/amphtml that referenced this pull request Nov 15, 2016
* Animation scanner and runner.

* presubmit fixes

* tests

* more tests

* types fixed

* docs

* minor docs fixes
Lith pushed a commit to Lith/amphtml that referenced this pull request Dec 22, 2016
* Animation scanner and runner.

* presubmit fixes

* tests

* more tests

* types fixed

* docs

* minor docs fixes
Lith pushed a commit to Lith/amphtml that referenced this pull request Dec 22, 2016
* Animation scanner and runner.

* presubmit fixes

* tests

* more tests

* types fixed

* docs

* minor docs fixes
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.

None yet

6 participants