Bug 1416106 - Part 7: Implement color graph. r?gl
MozReview-Commit-ID: 4ek6LXtsmKc
--- 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',
)