Skip to content

[web-animations-1] Replace AnimationEffectTiming(ReadOnly) with setTiming() ? #2065

@birtles

Description

@birtles

From @birtles on September 14, 2016 7:47

Just a thought about how we could simplify the API and make it more consistent.

Currently, we manage timing properties using the following interfaces:

  • AnimationEffectTimingProperties — property bag for passing to constructors etc.
  • ComputedTimingProperties — extends AnimationEffectTimingProperties with some extra properties we compute
  • AnimationEffectTimingReadOnly — Live object with getters for reading the specified timing properties. Fortunately, this happens to have the same members as AnimationEffectTimingProperties so you can pass it to constructors/methods that expect a AnimationEffectTimingProperties and it should work.
  • AnimationEffectTiming — Subclass of AnimationEffectTimingReadOnly that adds getters for the different parameters (used by mutable effects, i.e. those not tied to CSS)

In practice it looks like this:

// Set up a new animation
const anim = elem.animate({...}, { duration: 2000, fill: 'auto' });

// Read back the specified values
anim.effect.timing.duration; // 2000
anim.effect.timing.fill; // 'auto'

// Read back computed values
anim.effect.getComputedTiming().duration; // 2000
anim.effect.getComputedTiming().fill; // 'none'
// NOTE: |fill| and |duration| are a little bit special in that they allow 'auto'
// values that get computed into something else. It's not particularly useful, though,
// until we have groups (where, e.g., a group's auto duration computes to the length
// of its children).

// Read other computed values
anim.effect.getComputedTiming().activeDuration // 2000
anim.effect.getComputedTiming().progress // 0.0

// Update specified values
anim.effect.timing.duration *= 2;
anim.effect.timing.iterations++;

The parallel here is with style:

  • anim.effect.timing =~ elem.style (sort of, kind of)
  • anim.effect.getComputedTiming =~ window.getComputedStyle(elem)

However, for keyframes we do something completely different. We don't have live keyframe objects since that would be hard and instead we just use getKeyframes() and setKeyframes().

In addition, keyframe offsets can be computed. The author can omit them and we'll fill them in. We don't just fill in the offset member with the computed value when we return keyframes using getKeyframes(), however, since if we did that you couldn't take the result of getKeyframes() and pass it to setKeyframes() on another effect without freezing the offsets at that point (and losing the ability to auto-distribute keyframes from then on). Instead, we return the computed offset as a separate member: computedOffset.

So my thought here was:

  • Drop AnimationEffectTimingReadOnly.
  • Drop AnimationEffectTiming.
  • Drop effect.timing.
  • Rename effect.getComputedTiming() to effect.getTiming().
  • Extend ComputedTimingProperties to include computedDuration and computedFill.
  • Add effect.setTiming() that takes an AnimationEffectTimingProperties (which ComputedTimingProperties extends so you could pass the result of getTiming() to setTiming()).

Advantages:

  • 2 fewer interfaces
  • Fewer live objects
  • Consistency within the API — keyframes and timing properties follow the same idiom
  • Bulk setting of timing properties is easier

Disadvantages:

  • More awkward to update an individual timing property, e.g.
// Current API
anim.effect.timing.duration *= 2;
// Proposed API
const updatedTiming = anim.effect.getTiming();
updatedTiming.duration *= 2;
anim.effect.setTiming(updatedTiming);
  • More inefficient to update individual timing properties since you need to read and write these property bags. From what I understand, at least for Gecko, that can be quite slow.

I do wonder how common those last two items are, though. (And for the first one, if you're just blindingly overwriting the value, you can use Object.assign() or, one day, the object spread operator to write it more compactly.)

The other consideration is this proposal may be less forwards compatible. For example, suppose we extend iterations to take a keyword value. Presumably it will still compute to a float. Code that currently calls anim.effect.getComputedTiming().iterations would continue to work when this new property is used. However, under this proposal, code that calls anim.effect.getTiming().iterations would need to be updated to call anim.effect.getTiming.computedIterations instead.

That said, I think we already have this problem in that anyone currently calling anim.effect.timing.iterations and assuming it will be a float (which I expect is not uncommon since it's less to type than anim.effect.getComputedTiming().iterations) will have the same problem.

@shans, @dstockwell Any thoughts about this?

@heycam, @bzbarsky Is returning snapshot objects generally the preferred approach over maintaining live objects?

Copied from original issue: w3c/web-animations#168

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions