Bug 1426194 - Part 1: Correspond to the keyframes which have same offset. r=pbro
authorDaisuke Akatsuka <dakatsuka@mozilla.com>
Fri, 22 Dec 2017 00:49:10 +0900
changeset 449505 1230c90d4d9945f2d9edf304ad876424f8ed1cbb
parent 449504 b5679bcdadf8f9538b4e431df0031d601378d1a7
child 449506 209dc71353056afed81100cfb84047bef2f3b535
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspbro
bugs1426194
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1426194 - Part 1: Correspond to the keyframes which have same offset. r=pbro The problem with this bug was that it did not correspond to animations that have multiple keyframes with the same offset. In summary graph, although we were changing the resolution for the graph creation by the distance of offset between keyframes, as the calculation of resolution between keyframes with equivalent offset was infinite, generation was not completed and it was in a state of freezing. In here, in case of zero distance of offsets, we make avoiding to affect to changing the resolution. In addition, the detail graph did not display correctly. For example, there is an animation below. div.animate( [ { offset: 0, opacity: 1, }, { offset: 0.5, opacity: 0.5, }, { offset: 0.5, opacity: 0.1, }, { offset: 1, opacity: 1, }, ], 1000 ); In this animation, opacity changes from 1 to 0.5 during 0 - 499ms, from 0.1 to 1 after 500ms. So, opacity at offset 0.5 behaves 0.5 during 0 - 499ms, 0.1 after 500ms. Since current animation inspector creates the graph with computed value of the time of offset, the opacity of offset 0.5 always is 0.1 in the example, was not correct. As a solution, same to the actual animation work, create the graph between each keyframes with range that from start keyframe offset time to just before the time of end keyframe offset time. Also, in same offsets, connects between just before the time of the offset time and the offset time. So, in the example, we separate as below, then calculate the each coordinates, then combine as graph shape. 1: 0 ~ 499.999ms 2: 499.999 ~ 500ms (same offsets) 3: 500 ~ 999.999ms 4: 1000ms MozReview-Commit-ID: p4Cn2N9iFt
devtools/client/animationinspector/components/keyframes.js
devtools/client/animationinspector/graph-helper.js
devtools/client/animationinspector/test/head.js
--- a/devtools/client/animationinspector/components/keyframes.js
+++ b/devtools/client/animationinspector/components/keyframes.js
@@ -161,23 +161,26 @@ function renderEasingHint(parentEl, segm
   const keyframes = helper.getKeyframes();
   const duration = helper.getDuration();
 
   // Split segments for each keyframe.
   for (let i = 0, indexOfSegments = 0; i < keyframes.length - 1; i++) {
     const startKeyframe = keyframes[i];
     const endKeyframe = keyframes[i + 1];
     const endTime = endKeyframe.offset * duration;
+    const keyframeSegments = [];
 
-    const keyframeSegments = [];
     for (; indexOfSegments < segments.length; indexOfSegments++) {
       const segment = segments[indexOfSegments];
       keyframeSegments.push(segment);
 
-      if (segment.x === endTime) {
+      if (startKeyframe.offset === endKeyframe.offset) {
+        keyframeSegments.push(segments[++indexOfSegments]);
+        break;
+      } else if (segment.x === endTime) {
         break;
       }
     }
 
     // Append easing hint as text and emphasis path.
     const gEl = createSVGNode({
       parent: parentEl,
       nodeType: "g"
--- a/devtools/client/animationinspector/graph-helper.js
+++ b/devtools/client/animationinspector/graph-helper.js
@@ -260,30 +260,40 @@ ProgressGraphHelper.prototype = {
       return createKeyframesPathSegments(duration, this.devtoolsKeyframes);
     }
 
     const segments = [];
 
     for (let i = 0; i < this.devtoolsKeyframes.length - 1; i++) {
       const startKeyframe = this.devtoolsKeyframes[i];
       const endKeyframe = this.devtoolsKeyframes[i + 1];
+      const startTime = startKeyframe.offset * duration;
+      const endTime = endKeyframe.offset * duration;
 
-      let threshold = getPreferredProgressThreshold(startKeyframe.easing);
-      if (threshold !== DEFAULT_MIN_PROGRESS_THRESHOLD) {
-        // We should consider the keyframe's duration.
-        threshold *= (endKeyframe.offset - startKeyframe.offset);
+      if (startKeyframe.offset === endKeyframe.offset) {
+        const startSegment = this.getSegment(startTime - BOUND_EXCLUDING_TIME);
+        startSegment.x = startTime;
+        const endSegment = this.getSegment(endTime);
+        segments.push(startSegment, endSegment);
+      } else {
+        let threshold = getPreferredProgressThreshold(startKeyframe.easing);
+
+        if (threshold !== DEFAULT_MIN_PROGRESS_THRESHOLD) {
+          // We should consider the keyframe's duration.
+          threshold *= (endKeyframe.offset - startKeyframe.offset);
+        }
+
+        segments.push(...createPathSegments(startTime, endTime - BOUND_EXCLUDING_TIME,
+                                            minSegmentDuration, threshold, this));
       }
-
-      const startTime = parseFloat((startKeyframe.offset * duration).toFixed(3));
-      const endTime = parseFloat((endKeyframe.offset * duration).toFixed(3));
-
-      segments.push(...createPathSegments(startTime, endTime,
-                                          minSegmentDuration, threshold, this));
     }
 
+    const lastKeyframe = this.devtoolsKeyframes[this.devtoolsKeyframes.length - 1];
+    const lastTime = lastKeyframe.offset * duration;
+    segments.push(this.getSegment(lastTime));
     return segments;
   },
 
   /**
    * Append path element as shape. Also, this method appends two segment
    * that are {start x, 0} and {end x, 0} to make shape.
    * @param {Element} parentEl - Parent element of this appended path element.
    * @param {Array} pathSegments - Path segments. Please see createPathSegments.
@@ -380,17 +390,17 @@ SummaryGraphHelper.prototype = {
     if (keyframes) {
       let previousOffset = 0;
 
       // Create new keyframes for opacity as computed style.
       // The reason why we use computed value instead of computed timing progress is to
       // include the easing in keyframes as well. Although the computed timing progress
       // is not affected by the easing in keyframes at all, computed value reflects that.
       frames = keyframes.map(keyframe => {
-        if (previousOffset) {
+        if (previousOffset && previousOffset != keyframe.offset) {
           const interval = keyframe.offset - previousOffset;
           durationResolution = Math.max(durationResolution, Math.ceil(1 / interval));
         }
         previousOffset = keyframe.offset;
 
         return {
           opacity: keyframe.offset,
           offset: keyframe.offset,
--- a/devtools/client/animationinspector/test/head.js
+++ b/devtools/client/animationinspector/test/head.js
@@ -591,19 +591,23 @@ function isPassingThrough(pathSegList, x
 
 /**
  * Find <stop> element which has given offset from given <svg> element.
  * @param svgEl - <svg> element which has <stop> element.
  * @param offset - float which represents the "offset" attribute of <stop>.
  * @return <stop> element.
  */
 function findStopElement(svgEl, offset) {
-  return [...svgEl.querySelectorAll("stop")].find(stopEl => {
-    return stopEl.getAttribute("offset") == offset;
-  });
+  for (const stopEl of svgEl.querySelectorAll("stop")) {
+    if (offset <= parseFloat(stopEl.getAttribute("offset"))) {
+      return stopEl;
+    }
+  }
+
+  return null;
 }
 
 /*
  * Returns all AnimationTargetNode instances.
  * This method should be called after emit "animation-timeline-rendering-completed".
  * @param {AnimationsPanel} panel The panel instance.
  * @return {Array} all AnimationTargetNode instances.
  */