summaryrefslogtreecommitdiffstats
path: root/devtools/client/inspector/animation/components/keyframes-graph/ColorPath.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--devtools/client/inspector/animation/components/keyframes-graph/ColorPath.js209
1 files changed, 209 insertions, 0 deletions
diff --git a/devtools/client/inspector/animation/components/keyframes-graph/ColorPath.js b/devtools/client/inspector/animation/components/keyframes-graph/ColorPath.js
new file mode 100644
index 0000000000..120b61c73b
--- /dev/null
+++ b/devtools/client/inspector/animation/components/keyframes-graph/ColorPath.js
@@ -0,0 +1,209 @@
+/* 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("resource://devtools/client/shared/vendor/react-dom-factories.js");
+
+const { colorUtils } = require("resource://devtools/shared/css/color.js");
+
+const ComputedStylePath = require("resource://devtools/client/inspector/animation/components/keyframes-graph/ComputedStylePath.js");
+
+const DEFAULT_COLOR = { r: 0, g: 0, b: 0, a: 1 };
+
+/* Count for linearGradient ID */
+let LINEAR_GRADIENT_ID_COUNT = 0;
+
+class ColorPath extends ComputedStylePath {
+ constructor(props) {
+ super(props);
+
+ this.state = this.propToState(props);
+ }
+
+ // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
+ UNSAFE_componentWillReceiveProps(nextProps) {
+ this.setState(this.propToState(nextProps));
+ }
+
+ getPropertyName() {
+ return "color";
+ }
+
+ getPropertyValue(keyframe) {
+ return keyframe.value;
+ }
+
+ propToState({ keyframes, name }) {
+ const maxObject = { distance: -Number.MAX_VALUE };
+
+ for (let i = 0; i < keyframes.length - 1; i++) {
+ const value1 = getRGBA(name, keyframes[i].value);
+ for (let j = i + 1; j < keyframes.length; j++) {
+ const value2 = getRGBA(name, keyframes[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, name };
+ }
+
+ toSegmentValue(computedStyle) {
+ const { baseValue, maxDistance, name } = this.state;
+ const value = getRGBA(name, computedStyle);
+ return getRGBADistance(baseValue, value) / maxDistance;
+ }
+
+ /**
+ * Overide parent's method.
+ */
+ renderEasingHint() {
+ const { easingHintStrokeWidth, graphHeight, keyframes, totalDuration } =
+ this.props;
+
+ const hints = [];
+
+ for (let i = 0; i < keyframes.length - 1; i++) {
+ const startKeyframe = keyframes[i];
+ const endKeyframe = keyframes[i + 1];
+ const startTime = startKeyframe.offset * totalDuration;
+ const endTime = endKeyframe.offset * totalDuration;
+
+ const g = dom.g(
+ {
+ className: "hint",
+ },
+ dom.title({}, startKeyframe.easing),
+ dom.rect({
+ x: startTime,
+ y: -graphHeight,
+ height: graphHeight,
+ width: endTime - startTime,
+ }),
+ dom.line({
+ x1: startTime,
+ y1: -graphHeight,
+ x2: endTime,
+ y2: -graphHeight,
+ style: {
+ "stroke-width": easingHintStrokeWidth,
+ },
+ })
+ );
+ hints.push(g);
+ }
+
+ return hints;
+ }
+
+ /**
+ * 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} propertyName
+ * @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(propertyName, colorString) {
+ // Special handling for CSS property which can specify the not normal CSS color value.
+ switch (propertyName) {
+ case "caret-color": {
+ // This property can specify "auto" keyword.
+ if (colorString === "auto") {
+ return DEFAULT_COLOR;
+ }
+ break;
+ }
+ case "scrollbar-color": {
+ // This property can specify "auto", "dark", "light" keywords and multiple colors.
+ if (
+ ["auto", "dark", "light"].includes(colorString) ||
+ colorString.indexOf(" ") > 0
+ ) {
+ return DEFAULT_COLOR;
+ }
+ break;
+ }
+ }
+
+ 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;