Bug 1431573 - Part 6: Make summary graph reflect to playback rate. r?gl draft
authorDaisuke Akatsuka <dakatsuka@mozilla.com>
Tue, 13 Mar 2018 14:31:11 +0900
changeset 766609 455705afed3f832b3cab7efc6604690c71852404
parent 766608 04315850686318620452bff10d4a83f2900cc274
child 766610 2796fad5a38b0dd27f218a6d0964ac5ff9660fd7
push id102370
push userbmo:dakatsuka@mozilla.com
push dateTue, 13 Mar 2018 06:01:02 +0000
reviewersgl
bugs1431573
milestone61.0a1
Bug 1431573 - Part 6: Make summary graph reflect to playback rate. r?gl MozReview-Commit-ID: HFffrPAyJIh
devtools/client/inspector/animation/animation.js
devtools/client/inspector/animation/components/AnimationTimelineTickList.js
devtools/client/inspector/animation/components/graph/ComputedTimingPath.js
devtools/client/inspector/animation/components/graph/DelaySign.js
devtools/client/inspector/animation/components/graph/EffectTimingPath.js
devtools/client/inspector/animation/components/graph/EndDelaySign.js
devtools/client/inspector/animation/components/graph/NegativeDelayPath.js
devtools/client/inspector/animation/components/graph/NegativeEndDelayPath.js
devtools/client/inspector/animation/components/graph/NegativePath.js
devtools/client/inspector/animation/components/graph/SummaryGraphPath.js
devtools/client/inspector/animation/utils/timescale.js
--- a/devtools/client/inspector/animation/animation.js
+++ b/devtools/client/inspector/animation/animation.js
@@ -378,26 +378,27 @@ class AnimationInspector {
     await Promise.all(promises);
 
     this.updateState([...animations]);
   }
 
   updateState(animations) {
     this.stopAnimationsCurrentTimeTimer();
 
-    this.inspector.store.dispatch(updateAnimations(animations));
     // If number of displayed animations is one, we select the animation automatically.
     // But if selected animation is in given animations, ignores.
     const selectedAnimation = this.state.selectedAnimation;
 
     if (!selectedAnimation ||
         !animations.find(animation => animation.actorID === selectedAnimation.actorID)) {
       this.selectAnimation(animations.length === 1 ? animations[0] : null);
     }
 
+    this.inspector.store.dispatch(updateAnimations(animations));
+
     if (hasPlayingAnimation(animations)) {
       this.startAnimationsCurrentTimeTimer();
     }
   }
 }
 
 class CurrentTimeTimer {
   constructor(animationInspector) {
--- a/devtools/client/inspector/animation/components/AnimationTimelineTickList.js
+++ b/devtools/client/inspector/animation/components/AnimationTimelineTickList.js
@@ -30,42 +30,42 @@ class AnimationTimelineTickList extends 
     super(props);
 
     this.state = {
       tickList: [],
     };
   }
 
   componentDidMount() {
-    this.updateTickList();
+    this.updateTickList(this.props);
   }
 
   componentWillReceiveProps(nextProps) {
-    this.updateTickList();
+    this.updateTickList(nextProps);
   }
 
   shouldComponentUpdate(nextProps, nextState) {
     if (this.state.tickList.length !== nextState.tickList.length) {
       return true;
     }
 
     for (let i = 0; i < this.state.tickList.length; i++) {
       const currentTickItem = this.state.tickList[i];
       const nextTickItem = nextState.tickList[i];
 
-      if (currentTickItem.text !== nextTickItem.text) {
+      if (currentTickItem.timeTickLabel !== nextTickItem.timeTickLabel) {
         return true;
       }
     }
 
     return false;
   }
 
-  updateTickList() {
-    const { timeScale } = this.props;
+  updateTickList(props) {
+    const { timeScale } = props;
     const tickListEl = ReactDOM.findDOMNode(this);
     const width = tickListEl.offsetWidth;
     const animationDuration = timeScale.getDuration();
     const minTimeInterval = TIME_GRADUATION_MIN_SPACING * animationDuration / width;
     const intervalLength = findOptimalTimeInterval(minTimeInterval);
     const intervalWidth = intervalLength * width / animationDuration;
     const tickCount = width / intervalWidth;
     const intervalPositionPercentage = 100 * intervalWidth / width;
--- a/devtools/client/inspector/animation/components/graph/ComputedTimingPath.js
+++ b/devtools/client/inspector/animation/components/graph/ComputedTimingPath.js
@@ -11,27 +11,29 @@ const { SummaryGraphHelper, toPathString
 const TimingPath = require("./TimingPath");
 
 class ComputedTimingPath extends TimingPath {
   static get propTypes() {
     return {
       animation: PropTypes.object.isRequired,
       durationPerPixel: PropTypes.number.isRequired,
       keyframes: PropTypes.object.isRequired,
+      offset: PropTypes.number.isRequired,
       opacity: PropTypes.number.isRequired,
       simulateAnimation: PropTypes.func.isRequired,
       totalDuration: PropTypes.number.isRequired,
     };
   }
 
   render() {
     const {
       animation,
       durationPerPixel,
       keyframes,
+      offset,
       opacity,
       simulateAnimation,
       totalDuration,
     } = this.props;
 
     const { state } = animation;
     const effectTiming = Object.assign({}, state, {
       iterations: state.iterationCount ? state.iterationCount : Infinity
@@ -73,17 +75,16 @@ class ComputedTimingPath extends TimingP
       const lastSegment = segments[segments.length - 1];
       pathString += `L${ lastSegment.x },0 Z`;
       return pathString;
     };
 
     const helper = new SummaryGraphHelper(state, keyframes,
                                           totalDuration, durationPerPixel,
                                           getValueFunc, toPathStringFunc);
-    const offset = state.previousStartTime ? state.previousStartTime : 0;
 
     return dom.g(
       {
         className: "animation-computed-timing-path",
         style: { opacity },
         transform: `translate(${ offset })`
       },
       super.renderGraph(state, helper)
--- a/devtools/client/inspector/animation/components/graph/DelaySign.js
+++ b/devtools/client/inspector/animation/components/graph/DelaySign.js
@@ -16,29 +16,33 @@ class DelaySign extends PureComponent {
     };
   }
 
   render() {
     const {
       animation,
       timeScale,
     } = this.props;
+    const {
+      fill,
+      playbackRate,
+      previousStartTime = 0,
+    } = animation.state;
 
-    const { state } = animation;
-    const startTime = (state.previousStartTime || 0) - timeScale.minStartTime
-                      + (state.delay < 0 ? state.delay : 0);
+    const delay = animation.state.delay / playbackRate;
+    const startTime =
+      previousStartTime - timeScale.minStartTime + (delay < 0 ? delay : 0);
     const offset = startTime / timeScale.getDuration() * 100;
-    const width = Math.abs(state.delay) / timeScale.getDuration() * 100;
-
-    const delayClass = state.delay < 0 ? "negative" : "";
-    const fillClass = state.fill === "both" || state.fill === "backwards" ? "fill" : "";
+    const width = Math.abs(delay) / timeScale.getDuration() * 100;
 
     return dom.div(
       {
-        className: `animation-delay-sign ${ delayClass } ${ fillClass }`,
+        className: "animation-delay-sign" +
+                   (delay < 0 ? " negative" : "") +
+                   (fill === "both" || fill === "backwards" ? " fill" : ""),
         style: {
           width: `${ width }%`,
           left: `${ offset }%`,
         },
       }
     );
   }
 }
--- a/devtools/client/inspector/animation/components/graph/EffectTimingPath.js
+++ b/devtools/client/inspector/animation/components/graph/EffectTimingPath.js
@@ -10,25 +10,27 @@ const dom = require("devtools/client/sha
 const { SummaryGraphHelper, toPathString } = require("../../utils/graph-helper");
 const TimingPath = require("./TimingPath");
 
 class EffectTimingPath extends TimingPath {
   static get propTypes() {
     return {
       animation: PropTypes.object.isRequired,
       durationPerPixel: PropTypes.number.isRequired,
+      offset: PropTypes.number.isRequired,
       simulateAnimation: PropTypes.func.isRequired,
       totalDuration: PropTypes.number.isRequired,
     };
   }
 
   render() {
     const {
       animation,
       durationPerPixel,
+      offset,
       simulateAnimation,
       totalDuration,
     } = this.props;
 
     const { state } = animation;
     const effectTiming = Object.assign({}, state, {
       iterations: state.iterationCount ? state.iterationCount : Infinity
     });
@@ -52,17 +54,16 @@ class EffectTimingPath extends TimingPat
       const lastSegment = segments[segments.length - 1];
       pathString += `L${ lastSegment.x },0`;
       return pathString;
     };
 
     const helper = new SummaryGraphHelper(state, null,
                                           totalDuration, durationPerPixel,
                                           getValueFunc, toPathStringFunc);
-    const offset = state.previousStartTime ? state.previousStartTime : 0;
 
     return dom.g(
       {
         className: "animation-effect-timing-path",
         transform: `translate(${ offset })`
       },
       super.renderGraph(state, helper)
     );
--- a/devtools/client/inspector/animation/components/graph/EndDelaySign.js
+++ b/devtools/client/inspector/animation/components/graph/EndDelaySign.js
@@ -16,30 +16,37 @@ class EndDelaySign extends PureComponent
     };
   }
 
   render() {
     const {
       animation,
       timeScale,
     } = this.props;
+    const {
+      delay,
+      duration,
+      fill,
+      iterationCount,
+      playbackRate,
+      previousStartTime = 0,
+    } = animation.state;
 
-    const { state } = animation;
-    const startTime = (state.previousStartTime || 0) - timeScale.minStartTime;
-    const endTime = state.duration * state.iterationCount + state.delay;
-    const endDelay = state.endDelay < 0 ? state.endDelay : 0;
-    const offset = (startTime + endTime + endDelay) / timeScale.getDuration() * 100;
-    const width = Math.abs(state.endDelay) / timeScale.getDuration() * 100;
-
-    const endDelayClass = state.endDelay < 0 ? "negative" : "";
-    const fillClass = state.fill === "both" || state.fill === "forwards" ? "fill" : "";
+    const endDelay = animation.state.endDelay / playbackRate;
+    const startTime = previousStartTime - timeScale.minStartTime;
+    const endTime =
+      (duration * iterationCount + delay) / playbackRate + (endDelay < 0 ? endDelay : 0);
+    const offset = (startTime + endTime) / timeScale.getDuration() * 100;
+    const width = Math.abs(endDelay) / timeScale.getDuration() * 100;
 
     return dom.div(
       {
-        className: `animation-end-delay-sign ${ endDelayClass } ${ fillClass }`,
+        className: "animation-end-delay-sign" +
+                   (endDelay < 0 ? " negative" : "") +
+                   (fill === "both" || fill === "forwards" ? " fill" : ""),
         style: {
           width: `${ width }%`,
           left: `${ offset }%`,
         },
       }
     );
   }
 }
--- a/devtools/client/inspector/animation/components/graph/NegativeDelayPath.js
+++ b/devtools/client/inspector/animation/components/graph/NegativeDelayPath.js
@@ -1,33 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 
 const NegativePath = require("./NegativePath");
 
 class NegativeDelayPath extends NegativePath {
-  static get propTypes() {
-    return {
-      animation: PropTypes.object.isRequired,
-      durationPerPixel: PropTypes.number.isRequired,
-      keyframes: PropTypes.object.isRequired,
-      simulateAnimation: PropTypes.func.isRequired,
-      totalDuration: PropTypes.number.isRequired,
-    };
-  }
-
-  constructor(props) {
-    props.className = "animation-negative-delay-path";
-    super(props);
+  getClassName() {
+    return "animation-negative-delay-path";
   }
 
   renderGraph(state, helper) {
     const startTime = state.delay;
     const endTime = 0;
     const segments = helper.createPathSegments(startTime, endTime);
 
     return dom.path(
--- a/devtools/client/inspector/animation/components/graph/NegativeEndDelayPath.js
+++ b/devtools/client/inspector/animation/components/graph/NegativeEndDelayPath.js
@@ -1,33 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 
 const NegativePath = require("./NegativePath");
 
 class NegativeEndDelayPath extends NegativePath {
-  static get propTypes() {
-    return {
-      animation: PropTypes.object.isRequired,
-      durationPerPixel: PropTypes.number.isRequired,
-      keyframes: PropTypes.object.isRequired,
-      simulateAnimation: PropTypes.func.isRequired,
-      totalDuration: PropTypes.number.isRequired,
-    };
-  }
-
-  constructor(props) {
-    props.className = "animation-negative-end-delay-path";
-    super(props);
+  getClassName() {
+    return "animation-negative-end-delay-path";
   }
 
   renderGraph(state, helper) {
     const endTime = state.delay + state.iterationCount * state.duration;
     const startTime = endTime + state.endDelay;
     const segments = helper.createPathSegments(startTime, endTime);
 
     return dom.path(
--- a/devtools/client/inspector/animation/components/graph/NegativePath.js
+++ b/devtools/client/inspector/animation/components/graph/NegativePath.js
@@ -12,27 +12,28 @@ const { SummaryGraphHelper, toPathString
 
 class NegativePath extends PureComponent {
   static get propTypes() {
     return {
       animation: PropTypes.object.isRequired,
       className: PropTypes.string.isRequired,
       durationPerPixel: PropTypes.number.isRequired,
       keyframes: PropTypes.object.isRequired,
+      offset: PropTypes.number.isRequired,
       simulateAnimation: PropTypes.func.isRequired,
       totalDuration: PropTypes.number.isRequired,
     };
   }
 
   render() {
     const {
       animation,
-      className,
       durationPerPixel,
       keyframes,
+      offset,
       simulateAnimation,
       totalDuration,
     } = this.props;
 
     const { state } = animation;
     const effectTiming = Object.assign({}, state, {
       fill: "both",
       iterations: state.iterationCount ? state.iterationCount : Infinity
@@ -70,21 +71,20 @@ class NegativePath extends PureComponent
       const lastSegment = segments[segments.length - 1];
       pathString += `L${ lastSegment.x },0 Z`;
       return pathString;
     };
 
     const helper = new SummaryGraphHelper(state, keyframes,
                                           totalDuration, durationPerPixel,
                                           getValueFunc, toPathStringFunc);
-    const offset = state.previousStartTime ? state.previousStartTime : 0;
 
     return dom.g(
       {
-        className,
+        className: this.getClassName(),
         transform: `translate(${ offset })`
       },
       this.renderGraph(state, helper)
     );
   }
 }
 
 module.exports = NegativePath;
--- a/devtools/client/inspector/animation/components/graph/SummaryGraphPath.js
+++ b/devtools/client/inspector/animation/components/graph/SummaryGraphPath.js
@@ -169,70 +169,76 @@ class SummaryGraphPath extends PureCompo
 
     const {
       animation,
       simulateAnimation,
       timeScale,
     } = this.props;
 
     const totalDuration = this.getTotalDuration(animation, timeScale);
-    const startTime = timeScale.minStartTime;
+    const { playbackRate, previousStartTime = 0 } = animation.state;
+    const startTime = timeScale.minStartTime * playbackRate;
+    const offset = previousStartTime * playbackRate;
     const opacity = Math.max(1 / keyframesList.length, MIN_KEYFRAMES_EASING_OPACITY);
 
     return dom.svg(
       {
         className: "animation-summary-graph-path",
         preserveAspectRatio: "none",
         viewBox: `${ startTime } -${ DEFAULT_GRAPH_HEIGHT } `
                  + `${ totalDuration } ${ DEFAULT_GRAPH_HEIGHT }`,
       },
       keyframesList.map(keyframes =>
         ComputedTimingPath(
           {
             animation,
             durationPerPixel,
             keyframes,
+            offset,
             opacity,
             simulateAnimation,
             totalDuration,
           }
         )
       ),
       animation.state.easing !== "linear" ?
         EffectTimingPath(
           {
             animation,
             durationPerPixel,
+            offset,
             simulateAnimation,
             totalDuration,
           }
         )
       :
       null,
       animation.state.delay < 0 ?
         keyframesList.map(keyframes => {
           return NegativeDelayPath(
             {
               animation,
               durationPerPixel,
               keyframes,
+              offset,
               simulateAnimation,
               totalDuration,
             }
           );
         })
       :
       null,
       animation.state.iterationCount && animation.state.endDelay < 0 ?
         keyframesList.map(keyframes => {
           return NegativeEndDelayPath(
             {
               animation,
               durationPerPixel,
               keyframes,
+              offset,
               simulateAnimation,
               totalDuration,
             }
           );
         })
       :
       null
     );
--- a/devtools/client/inspector/animation/utils/timescale.js
+++ b/devtools/client/inspector/animation/utils/timescale.js
@@ -40,29 +40,27 @@ class TimeScale {
   addAnimation(state) {
     let {
       delay,
       documentCurrentTime,
       duration,
       endDelay = 0,
       iterationCount,
       playbackRate,
-      previousStartTime,
+      previousStartTime = 0,
     } = state;
 
     const toRate = v => v / playbackRate;
     const minZero = v => Math.max(v, 0);
     const rateRelativeDuration =
       toRate(duration * (!iterationCount ? 1 : iterationCount));
     // Negative-delayed animations have their startTimes set such that we would
     // be displaying the delay outside the time window if we didn't take it into
     // account here.
     const relevantDelay = delay < 0 ? toRate(delay) : 0;
-    previousStartTime = previousStartTime || 0;
-
     const startTime = toRate(minZero(delay)) +
                       rateRelativeDuration +
                       endDelay;
     this.minStartTime = Math.min(
       this.minStartTime,
       previousStartTime +
       relevantDelay +
       Math.min(startTime, 0)