Bug 1468475 - Part 2. Display the zero graduation. r?daisuke draft
authorMantaroh Yoshinaga <mantaroh@gmail.com>
Thu, 12 Jul 2018 09:30:04 +0900
changeset 817098 af3f2430a563aaab14319ae5acba391aed145641
parent 817097 e5469d500f1c36dad4db2601a1878d93f88d86b0
child 817099 84e864d78595224baa54e5c511da926e149c88a3
push id115953
push userbmo:mantaroh@gmail.com
push dateThu, 12 Jul 2018 00:30:42 +0000
reviewersdaisuke
bugs1468475
milestone63.0a1
Bug 1468475 - Part 2. Display the zero graduation. r?daisuke This patch will ensure that displaying the zero graduation into the timeline by using shift the graduation with zeroBaseTime. If we shifted all of the graduations, the graph might not have the first graduation. So this patch will add the first graduation intentionally in this case. MozReview-Commit-ID: HnLouXBja6L
devtools/client/inspector/animation/components/AnimationListContainer.js
devtools/client/inspector/animation/components/AnimationToolbar.js
devtools/client/inspector/animation/components/App.js
devtools/client/inspector/animation/components/CurrentTimeLabel.js
devtools/client/inspector/animation/test/browser_animation_animation-timeline-tick.js
--- a/devtools/client/inspector/animation/components/AnimationListContainer.js
+++ b/devtools/client/inspector/animation/components/AnimationListContainer.js
@@ -64,25 +64,44 @@ class AnimationListContainer extends Pur
     const tickLinesEl = ReactDOM.findDOMNode(this).querySelector(".tick-lines");
     const width = tickLinesEl.offsetWidth;
     const animationDuration = timeScale.getDuration();
     const minTimeInterval = TIME_GRADUATION_MIN_SPACING * animationDuration / width;
     const intervalLength = findOptimalTimeInterval(minTimeInterval);
     const intervalWidth = intervalLength * width / animationDuration;
     const tickCount = parseInt(width / intervalWidth, 10);
     const isAllDurationInfinity =
-      animations.every(animation => animation.state.duration === Infinity);
+          animations.every(animation => animation.state.duration === Infinity);
+    const zeroBasePosition =
+          width * (timeScale.zeroPositionTime / animationDuration);
+    const shiftWidth = zeroBasePosition % intervalWidth;
+    const needToShift = zeroBasePosition !== 0 && shiftWidth !== 0;
 
     const ticks = [];
+    // Need to display first graduation since position will be shifted.
+    if (needToShift) {
+      const label = timeScale.formatTime(timeScale.distanceToRelativeTime(0));
+      ticks.push({ position: 0, label });
+    }
 
     for (let i = 0; i <= tickCount; i++) {
-      const position = i * intervalWidth * 100 / width;
-      const label = isAllDurationInfinity && i === tickCount
-                      ? getStr("player.infiniteTimeLabel")
-                      : timeScale.formatTime(timeScale.distanceToRelativeTime(position));
+      const position = ((i * intervalWidth) + shiftWidth) * 100 / width;
+      const distance = timeScale.distanceToRelativeTime(position);
+      let label = isAllDurationInfinity && i === tickCount
+                  ? getStr("player.infiniteTimeLabel")
+                  : timeScale.formatTime(distance);
+      // As result of shifting the label, first shifted label might overlap
+      // to the most left label. So display empyt label in this case.
+      // And prevent to skip displaying zero position label.
+      if (i === 0 &&
+          needToShift &&
+          shiftWidth < intervalWidth &&
+          Math.abs(distance) >= 0.001) {
+        label = "";
+      }
       ticks.push({ position, label });
     }
 
     this.setState({ ticks });
   }
 
   render() {
     const {
--- a/devtools/client/inspector/animation/components/AnimationToolbar.js
+++ b/devtools/client/inspector/animation/components/AnimationToolbar.js
@@ -17,27 +17,29 @@ class AnimationToolbar extends PureCompo
   static get propTypes() {
     return {
       addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
       animations: PropTypes.arrayOf(PropTypes.object).isRequired,
       removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
       rewindAnimationsCurrentTime: PropTypes.func.isRequired,
       setAnimationsPlaybackRate: PropTypes.func.isRequired,
       setAnimationsPlayState: PropTypes.func.isRequired,
+      timeScale: PropTypes.object.isRequired,
     };
   }
 
   render() {
     const {
       addAnimationsCurrentTimeListener,
       animations,
       removeAnimationsCurrentTimeListener,
       rewindAnimationsCurrentTime,
       setAnimationsPlaybackRate,
       setAnimationsPlayState,
+      timeScale,
     } = this.props;
 
     return dom.div(
       {
         className: "animation-toolbar devtools-toolbar",
       },
       RewindButton(
         {
@@ -55,15 +57,16 @@ class AnimationToolbar extends PureCompo
           animations,
           setAnimationsPlaybackRate,
         }
       ),
       CurrentTimeLabel(
         {
           addAnimationsCurrentTimeListener,
           removeAnimationsCurrentTimeListener,
+          timeScale,
         }
       )
     );
   }
 }
 
 module.exports = AnimationToolbar;
--- a/devtools/client/inspector/animation/components/App.js
+++ b/devtools/client/inspector/animation/components/App.js
@@ -89,16 +89,17 @@ class App extends Component {
         AnimationToolbar(
           {
             addAnimationsCurrentTimeListener,
             animations,
             removeAnimationsCurrentTimeListener,
             rewindAnimationsCurrentTime,
             setAnimationsPlaybackRate,
             setAnimationsPlayState,
+            timeScale,
           }
         ),
         SplitBox({
           className: "animation-container-splitter",
           endPanel: AnimationDetailContainer(
             {
               addAnimationsCurrentTimeListener,
               emitEventForTest,
--- a/devtools/client/inspector/animation/components/CurrentTimeLabel.js
+++ b/devtools/client/inspector/animation/components/CurrentTimeLabel.js
@@ -8,16 +8,17 @@ const { PureComponent } = require("devto
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 class CurrentTimeLabel extends PureComponent {
   static get propTypes() {
     return {
       addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
       removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
+      timeScale: PropTypes.object.isRequired,
     };
   }
 
   constructor(props) {
     super(props);
 
     const { addAnimationsCurrentTimeListener } = props;
     this.onCurrentTimeUpdated = this.onCurrentTimeUpdated.bind(this);
@@ -34,50 +35,44 @@ class CurrentTimeLabel extends PureCompo
     removeAnimationsCurrentTimeListener(this.onCurrentTimeUpdated);
   }
 
   onCurrentTimeUpdated(currentTime) {
     this.setState({ currentTime });
   }
 
   render() {
+    const { timeScale } = this.props;
     const { currentTime } = this.state;
 
     return dom.label(
       {
         className: "current-time-label",
       },
-      formatStopwatchTime(currentTime)
+      formatStopwatchTime(currentTime - timeScale.zeroPositionTime)
     );
   }
 }
 
 /**
  * Format a timestamp (in ms) as a mm:ss.mmm string.
  *
  * @param {Number} time
  * @return {String}
  */
 function formatStopwatchTime(time) {
   // Format falsy values as 0
   if (!time) {
     return "00:00.000";
   }
 
-  let milliseconds = parseInt(time % 1000, 10);
-  let seconds = parseInt((time / 1000) % 60, 10);
-  let minutes = parseInt((time / (1000 * 60)), 10);
+  const sign = time < 0 ? "-" : "";
+  let milliseconds = parseInt(Math.abs(time % 1000), 10);
+  let seconds = parseInt(Math.abs((time / 1000)) % 60, 10);
+  let minutes = parseInt(Math.abs((time / (1000 * 60))), 10);
 
-  const pad = (nb, max) => {
-    if (nb < max) {
-      return new Array((max + "").length - (nb + "").length + 1).join("0") + nb;
-    }
-    return nb;
-  };
-
-  minutes = pad(minutes, 10);
-  seconds = pad(seconds, 10);
-  milliseconds = pad(milliseconds, 100);
-
-  return `${minutes}:${seconds}.${milliseconds}`;
+  minutes = minutes.toString().padStart(2, "0");
+  seconds = seconds.toString().padStart(2, "0");
+  milliseconds = milliseconds.toString().padStart(3, "0");
+  return `${sign}${minutes}:${seconds}.${milliseconds}`;
 }
 
 module.exports = CurrentTimeLabel;
--- a/devtools/client/inspector/animation/test/browser_animation_animation-timeline-tick.js
+++ b/devtools/client/inspector/animation/test/browser_animation_animation-timeline-tick.js
@@ -49,26 +49,31 @@ function assertTickLabels(timeScale, lis
   const timelineTickListEl = listContainerEl.querySelector(".tick-labels");
   ok(timelineTickListEl,
     "The animation timeline tick list element should be in header");
 
   const width = timelineTickListEl.offsetWidth;
   const animationDuration = timeScale.getDuration();
   const minTimeInterval = TIME_GRADUATION_MIN_SPACING * animationDuration / width;
   const interval = findOptimalTimeInterval(minTimeInterval);
-  const expectedTickItem = Math.ceil(animationDuration / interval);
+  const shiftWidth = timeScale.zeroPositionTime % interval;
+  const expectedTickItem = Math.ceil(animationDuration / interval) +
+                             (shiftWidth !== 0 ? 1 : 0);
 
   const timelineTickItemEls = timelineTickListEl.querySelectorAll(".tick-label");
   is(timelineTickItemEls.length, expectedTickItem,
     "The expected number of timeline ticks were found");
 
   info("Make sure graduations are evenly distributed and show the right times");
   for (const [index, tickEl] of timelineTickItemEls.entries()) {
     const left = parseFloat(tickEl.style.marginInlineStart);
-    const expectedPos = index * interval * 100 / animationDuration;
+    let expectedPos = (((index - 1) * interval + shiftWidth) / animationDuration) * 100;
+    if (shiftWidth !== 0 && index === 0) {
+      expectedPos = 0;
+    }
     is(Math.round(left), Math.round(expectedPos),
       `Graduation ${ index } is positioned correctly`);
 
     // Note that the distancetoRelativeTime and formatTime functions are tested
     // separately in xpcshell test test_timeScale.js, so we assume that they
     // work here.
     const formattedTime =
       timeScale.formatTime(timeScale.distanceToRelativeTime(expectedPos, width));