Bug 1416106 - Part 7: Implement color graph. r=gl
authorDaisuke Akatsuka <dakatsuka@mozilla.com>
Wed, 14 Feb 2018 23:18:12 +0900
changeset 404001 b1365cf5d62ba82520e20be02b10d4152c6c04e1
parent 404000 729b7060c1e7f111e2282c41dc7a89d3ca4b59d2
child 404002 0070a351a4a4512adfe1dc1d68daad620ce63dc1
push id99924
push userebalazs@mozilla.com
push dateThu, 15 Feb 2018 20:43:51 +0000
treeherdermozilla-inbound@a7d2a49f46fb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgl
bugs1416106
milestone60.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 1416106 - Part 7: Implement color graph. r=gl MozReview-Commit-ID: 4ek6LXtsmKc
devtools/client/inspector/animation/components/AnimatedPropertyItem.js
devtools/client/inspector/animation/components/AnimatedPropertyList.js
devtools/client/inspector/animation/components/keyframes-graph/ColorPath.js
devtools/client/inspector/animation/components/keyframes-graph/ComputedStylePath.js
devtools/client/inspector/animation/components/keyframes-graph/KeyframesGraph.js
devtools/client/inspector/animation/components/keyframes-graph/KeyframesGraphPath.js
devtools/client/inspector/animation/components/keyframes-graph/moz.build
--- a/devtools/client/inspector/animation/components/AnimatedPropertyItem.js
+++ b/devtools/client/inspector/animation/components/AnimatedPropertyItem.js
@@ -12,41 +12,44 @@ const AnimatedPropertyName = createFacto
 const KeyframesGraph = createFactory(require("./keyframes-graph/KeyframesGraph"));
 
 class AnimatedPropertyItem extends PureComponent {
   static get propTypes() {
     return {
       property: PropTypes.string.isRequired,
       simulateAnimation: PropTypes.func.isRequired,
       state: PropTypes.object.isRequired,
+      type: PropTypes.string.isRequired,
       values: PropTypes.array.isRequired,
     };
   }
 
   render() {
     const {
       property,
       simulateAnimation,
       state,
+      type,
       values,
     } = this.props;
 
     return dom.li(
       {
         className: "animated-property-item"
       },
       AnimatedPropertyName(
         {
           property,
           state,
         }
       ),
       KeyframesGraph(
         {
           simulateAnimation,
+          type,
           values,
         }
       )
     );
   }
 }
 
 module.exports = AnimatedPropertyItem;
--- a/devtools/client/inspector/animation/components/AnimatedPropertyList.js
+++ b/devtools/client/inspector/animation/components/AnimatedPropertyList.js
@@ -49,45 +49,49 @@ class AnimatedPropertyList extends PureC
   }
 
   async updateKeyframesList(animation) {
     const {
       getAnimatedPropertyMap,
       emitEventForTest,
     } = this.props;
     const animatedPropertyMap = await getAnimatedPropertyMap(animation);
+    const animationTypes = await animation.getAnimationTypes(animatedPropertyMap.keys());
 
-    this.setState({ animatedPropertyMap });
+    this.setState({ animatedPropertyMap, animationTypes });
 
     emitEventForTest("animation-keyframes-rendered");
   }
 
   render() {
     const {
       simulateAnimation,
     } = this.props;
     const {
       animatedPropertyMap,
+      animationTypes,
     } = this.state;
 
     if (!animatedPropertyMap) {
       return null;
     }
 
     return dom.ul(
       {
         className: "animated-property-list"
       },
       [...animatedPropertyMap.entries()].map(([property, values]) => {
         const state = this.getPropertyState(property);
+        const type = animationTypes[property];
         return AnimatedPropertyItem(
           {
             property,
             simulateAnimation,
             state,
+            type,
             values,
           }
         );
       })
     );
   }
 }
 
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/components/keyframes-graph/ColorPath.js
@@ -0,0 +1,142 @@
+/* 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 dom = require("devtools/client/shared/vendor/react-dom-factories");
+
+const {colorUtils} = require("devtools/shared/css/color.js");
+
+const ComputedStylePath = require("./ComputedStylePath");
+
+/* Count for linearGradient ID */
+let LINEAR_GRADIENT_ID_COUNT = 0;
+
+class ColorPath extends ComputedStylePath {
+  constructor(props) {
+    super(props);
+
+    this.state = this.propToState(props);
+  }
+
+  componentWillReceiveProps(nextProps) {
+    this.setState(this.propToState(nextProps));
+  }
+
+  getPropertyName() {
+    return "color";
+  }
+
+  getPropertyValue(keyframe) {
+    return keyframe.value;
+  }
+
+  propToState({ values }) {
+    const maxObject = { distance: 0 };
+
+    for (let i = 0; i < values.length - 1; i++) {
+      const value1 = getRGBA(values[i].value);
+      for (let j = i + 1; j < values.length; j++) {
+        const value2 = getRGBA(values[j].value);
+        const distance = getRGBADistance(value1, value2);
+
+        if (maxObject.distance >= distance) {
+          continue;
+        }
+
+        maxObject.distance = distance;
+        maxObject.value1 = value1;
+        maxObject.value2 = value2;
+      }
+    }
+
+    const maxDistance = maxObject.distance;
+    const baseValue =
+      maxObject.value1 < maxObject.value2 ? maxObject.value1 : maxObject.value2;
+
+    return { baseValue, maxDistance };
+  }
+
+  toSegmentValue(computedStyle) {
+    const { baseValue, maxDistance } = this.state;
+    const value = getRGBA(computedStyle);
+    return getRGBADistance(baseValue, value) / maxDistance;
+  }
+
+  /**
+   * Overide parent's method.
+   */
+  renderPathSegments(segments) {
+    for (const segment of segments) {
+      segment.y = 1;
+    }
+
+    const lastSegment = segments[segments.length - 1];
+    const id = `color-property-${ LINEAR_GRADIENT_ID_COUNT++ }`;
+    const path = super.renderPathSegments(segments, { fill: `url(#${ id })` });
+    const linearGradient = dom.linearGradient(
+      { id },
+      segments.map(segment => {
+        return dom.stop(
+          {
+            "stopColor": segment.computedStyle,
+            "offset": segment.x / lastSegment.x,
+          }
+        );
+      })
+    );
+
+    return [path, linearGradient];
+  }
+
+  render() {
+    return dom.g(
+      {
+        className: "color-path",
+      },
+      super.renderGraph()
+    );
+  }
+}
+
+/**
+ * Parse given RGBA string.
+ *
+ * @param {String} colorString
+ *        e.g. rgb(0, 0, 0) or rgba(0, 0, 0, 0.5) and so on.
+ * @return {Object}
+ *         RGBA {r: r, g: g, b: b, a: a}.
+ */
+function getRGBA(colorString) {
+  const color = new colorUtils.CssColor(colorString);
+  return color.getRGBATuple();
+}
+
+/**
+ * Return the distance from give two RGBA.
+ *
+ * @param {Object} rgba1
+ *        RGBA (format is same to getRGBA)
+ * @param {Object} rgba2
+ *        RGBA (format is same to getRGBA)
+ * @return {Number}
+ *         The range is 0 - 1.0.
+ */
+function getRGBADistance(rgba1, rgba2) {
+  const startA = rgba1.a;
+  const startR = rgba1.r * startA;
+  const startG = rgba1.g * startA;
+  const startB = rgba1.b * startA;
+  const endA = rgba2.a;
+  const endR = rgba2.r * endA;
+  const endG = rgba2.g * endA;
+  const endB = rgba2.b * endA;
+  const diffA = startA - endA;
+  const diffR = startR - endR;
+  const diffG = startG - endG;
+  const diffB = startB - endB;
+  return Math.sqrt(diffA * diffA + diffR * diffR + diffG * diffG + diffB * diffB);
+}
+
+module.exports = ColorPath;
--- a/devtools/client/inspector/animation/components/keyframes-graph/ComputedStylePath.js
+++ b/devtools/client/inspector/animation/components/keyframes-graph/ComputedStylePath.js
@@ -128,26 +128,27 @@ class ComputedStylePath extends PureComp
 
     return this.renderPathSegments(segments);
   }
 
   /**
    * Return react dom fron given path segments.
    *
    * @param {Array} segments
+   * @param {Object} style
    * @return {Element}
    */
-  renderPathSegments(segments) {
+  renderPathSegments(segments, style) {
     const { graphHeight } = this.props;
 
     for (const segment of segments) {
       segment.y *= graphHeight;
     }
 
     let d = `M${ segments[0].x },0 `;
     d += toPathString(segments);
     d += `L${ segments[segments.length - 1].x },0 Z`;
 
-    return dom.path({ d });
+    return dom.path({ d, style });
   }
 }
 
 module.exports = ComputedStylePath;
--- a/devtools/client/inspector/animation/components/keyframes-graph/KeyframesGraph.js
+++ b/devtools/client/inspector/animation/components/keyframes-graph/KeyframesGraph.js
@@ -9,33 +9,36 @@ const dom = require("devtools/client/sha
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 const KeyframesGraphPath = createFactory(require("./KeyframesGraphPath"));
 
 class KeyframesGraph extends PureComponent {
   static get propTypes() {
     return {
       simulateAnimation: PropTypes.func.isRequired,
+      type: PropTypes.string.isRequired,
       values: PropTypes.array.isRequired,
     };
   }
 
   render() {
     const {
       simulateAnimation,
+      type,
       values,
     } = this.props;
 
     return dom.div(
       {
         className: "keyframes-graph",
       },
       KeyframesGraphPath(
         {
           simulateAnimation,
+          type,
           values,
         }
       )
     );
   }
 }
 
 module.exports = KeyframesGraph;
--- a/devtools/client/inspector/animation/components/keyframes-graph/KeyframesGraphPath.js
+++ b/devtools/client/inspector/animation/components/keyframes-graph/KeyframesGraphPath.js
@@ -4,67 +4,81 @@
 
 "use strict";
 
 const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 
+const ColorPath = createFactory(require("./ColorPath"));
 const DistancePath = createFactory(require("./DistancePath"));
 
 const {
   DEFAULT_GRAPH_HEIGHT,
   DEFAULT_KEYFRAMES_GRAPH_DURATION,
 } = require("../../utils/graph-helper");
 
 class KeyframesGraphPath extends PureComponent {
   static get propTypes() {
     return {
       simulateAnimation: PropTypes.func.isRequired,
+      type: PropTypes.string.isRequired,
       values: PropTypes.array.isRequired,
     };
   }
 
   constructor(props) {
     super(props);
 
     this.state = {
       componentWidth: 0,
     };
   }
 
   componentDidMount() {
     this.updateState();
   }
 
+  getPathComponent(type) {
+    switch (type) {
+      case "color" :
+        return ColorPath;
+      default :
+        return DistancePath;
+    }
+  }
+
   updateState() {
     const thisEl = ReactDOM.findDOMNode(this);
     this.setState({ componentWidth: thisEl.parentNode.clientWidth });
   }
 
   render() {
     const {
       simulateAnimation,
+      type,
       values,
     } = this.props;
     const { componentWidth } = this.state;
 
     if (!componentWidth) {
       return dom.svg();
     }
 
+    const pathComponent = this.getPathComponent(type);
+
     return dom.svg(
       {
         className: "keyframes-graph-path",
         preserveAspectRatio: "none",
         viewBox: `0 -${ DEFAULT_GRAPH_HEIGHT } `
                  + `${ DEFAULT_KEYFRAMES_GRAPH_DURATION } ${ DEFAULT_GRAPH_HEIGHT }`,
       },
-      DistancePath(
+      pathComponent(
         {
           componentWidth,
           graphHeight: DEFAULT_GRAPH_HEIGHT,
           simulateAnimation,
           totalDuration: DEFAULT_KEYFRAMES_GRAPH_DURATION,
           values,
         }
       )
--- a/devtools/client/inspector/animation/components/keyframes-graph/moz.build
+++ b/devtools/client/inspector/animation/components/keyframes-graph/moz.build
@@ -1,10 +1,11 @@
 # 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/.
 
 DevToolsModules(
+    'ColorPath.js',
     'ComputedStylePath.js',
     'DistancePath.js',
     'KeyframesGraph.js',
     'KeyframesGraphPath.js',
 )