diff options
Diffstat (limited to '')
-rw-r--r-- | devtools/client/inspector/animation/components/keyframes-graph/ColorPath.js | 209 |
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; |