author | Daisuke Akatsuka <dakatsuka@mozilla.com> |
Wed, 14 Feb 2018 23:18:13 +0900 | |
changeset 404004 | f20d1625e15bd501a5e36a85f9b30544ae35114a |
parent 404003 | 5df3265bb6097c870156423561b9d85598e3e83b |
child 404005 | d717511990c418ad5bb5df663ffe12295163cfc6 |
push id | 99924 |
push user | ebalazs@mozilla.com |
push date | Thu, 15 Feb 2018 20:43:51 +0000 |
treeherder | mozilla-inbound@a7d2a49f46fb [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | gl |
bugs | 1416106 |
milestone | 60.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
|
--- a/devtools/client/inspector/animation/components/keyframes-graph/ColorPath.js +++ b/devtools/client/inspector/animation/components/keyframes-graph/ColorPath.js @@ -62,16 +62,58 @@ class ColorPath extends ComputedStylePat const { baseValue, maxDistance } = this.state; const value = getRGBA(computedStyle); return getRGBADistance(baseValue, value) / maxDistance; } /** * Overide parent's method. */ + renderEasingHint() { + const { + easingHintStrokeWidth, + graphHeight, + totalDuration, + values, + } = this.props; + + const hints = []; + + for (let i = 0; i < values.length - 1; i++) { + const startKeyframe = values[i]; + const endKeyframe = values[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, + 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 })` });
--- a/devtools/client/inspector/animation/components/keyframes-graph/ComputedStylePath.js +++ b/devtools/client/inspector/animation/components/keyframes-graph/ComputedStylePath.js @@ -36,16 +36,17 @@ const { * e.g. 0 * @return {Number} * e.g. 0 (should be 0 - 1.0) */ class ComputedStylePath extends PureComponent { static get propTypes() { return { componentWidth: PropTypes.number.isRequired, + easingHintStrokeWidth: PropTypes.number.isRequired, graphHeight: PropTypes.number.isRequired, simulateAnimation: PropTypes.func.isRequired, totalDuration: PropTypes.number.isRequired, values: PropTypes.array.isRequired, }; } /** @@ -106,32 +107,92 @@ class ComputedStylePath extends PureComp for (const segment of segments) { segment.x += offset; } return segments; } /** + * Render easing hint from given path segments. + * + * @param {Array} segments + * Path segments. + * @return {Element} + * Element which represents easing hint. + */ + renderEasingHint(segments) { + const { + easingHintStrokeWidth, + totalDuration, + values, + } = this.props; + + const hints = []; + + for (let i = 0, indexOfSegments = 0; i < values.length - 1; i++) { + const startKeyframe = values[i]; + const endKeyframe = values[i + 1]; + const endTime = endKeyframe.offset * totalDuration; + const hintSegments = []; + + for (; indexOfSegments < segments.length; indexOfSegments++) { + const segment = segments[indexOfSegments]; + hintSegments.push(segment); + + if (startKeyframe.offset === endKeyframe.offset) { + hintSegments.push(segments[++indexOfSegments]); + break; + } else if (segment.x === endTime) { + break; + } + } + + const g = dom.g( + { + className: "hint" + }, + dom.title({}, startKeyframe.easing), + dom.path( + { + d: `M${ hintSegments[0].x },${ hintSegments[0].y } ` + + toPathString(hintSegments), + style: { + "stroke-width": easingHintStrokeWidth, + } + } + ) + ); + + hints.push(g); + } + + return hints; + } + + /** * Render graph. This method returns React dom. * * @return {Element} */ renderGraph() { const { values } = this.props; const segments = []; for (let i = 0; i < values.length - 1; i++) { const startValue = values[i]; const endValue = values[i + 1]; segments.push(...this.getPathSegments(startValue, endValue)); } - return this.renderPathSegments(segments); + return [ + this.renderPathSegments(segments), + this.renderEasingHint(segments) + ]; } /** * Return react dom fron given path segments. * * @param {Array} segments * @param {Object} style * @return {Element}
--- a/devtools/client/inspector/animation/components/keyframes-graph/KeyframesGraphPath.js +++ b/devtools/client/inspector/animation/components/keyframes-graph/KeyframesGraphPath.js @@ -9,16 +9,17 @@ const dom = require("devtools/client/sha 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 DiscretePath = createFactory(require("./DiscretePath")); const DistancePath = createFactory(require("./DistancePath")); const { + DEFAULT_EASING_HINT_STROKE_WIDTH, DEFAULT_GRAPH_HEIGHT, DEFAULT_KEYFRAMES_GRAPH_DURATION, } = require("../../utils/graph-helper"); class KeyframesGraphPath extends PureComponent { static get propTypes() { return { getComputedStyle: PropTypes.func.isRequired, @@ -28,16 +29,17 @@ class KeyframesGraphPath extends PureCom values: PropTypes.array.isRequired, }; } constructor(props) { super(props); this.state = { + componentHeight: 0, componentWidth: 0, }; } componentDidMount() { this.updateState(); } @@ -49,45 +51,55 @@ class KeyframesGraphPath extends PureCom return DiscretePath; default : return DistancePath; } } updateState() { const thisEl = ReactDOM.findDOMNode(this); - this.setState({ componentWidth: thisEl.parentNode.clientWidth }); + this.setState({ + componentHeight: thisEl.parentNode.clientHeight, + componentWidth: thisEl.parentNode.clientWidth, + }); } render() { const { getComputedStyle, property, simulateAnimation, type, values, } = this.props; - const { componentWidth } = this.state; + const { + componentHeight, + componentWidth, + } = this.state; if (!componentWidth) { return dom.svg(); } const pathComponent = this.getPathComponent(type); + const strokeWidthInViewBox = + DEFAULT_EASING_HINT_STROKE_WIDTH / 2 / componentHeight * DEFAULT_GRAPH_HEIGHT; return dom.svg( { className: "keyframes-graph-path", preserveAspectRatio: "none", - viewBox: `0 -${ DEFAULT_GRAPH_HEIGHT } ` - + `${ DEFAULT_KEYFRAMES_GRAPH_DURATION } ${ DEFAULT_GRAPH_HEIGHT }`, + viewBox: `0 -${ DEFAULT_GRAPH_HEIGHT + strokeWidthInViewBox } ` + + `${ DEFAULT_KEYFRAMES_GRAPH_DURATION } ` + + `${ DEFAULT_GRAPH_HEIGHT + strokeWidthInViewBox * 2 }`, }, pathComponent( { componentWidth, + easingHintStrokeWidth: DEFAULT_EASING_HINT_STROKE_WIDTH, getComputedStyle, graphHeight: DEFAULT_GRAPH_HEIGHT, property, simulateAnimation, totalDuration: DEFAULT_KEYFRAMES_GRAPH_DURATION, values, } )
--- a/devtools/client/inspector/animation/utils/graph-helper.js +++ b/devtools/client/inspector/animation/utils/graph-helper.js @@ -18,16 +18,18 @@ const DEFAULT_MIN_PROGRESS_THRESHOLD = 0 // DEFAULT_DURATION_RESOLUTION in order to draw the way the animation progresses. // But depending on the timing-function, we may be not able to make the graph // smoothly progress if this resolution is not high enough. // So, if the difference of animation progress between 2 divisions is more than // DEFAULT_MIN_PROGRESS_THRESHOLD * DEFAULT_GRAPH_HEIGHT, then createPathSegments // re-divides by DEFAULT_DURATION_RESOLUTION. // DEFAULT_DURATION_RESOLUTION shoud be integer and more than 2. const DEFAULT_DURATION_RESOLUTION = 4; +// Stroke width for easing hint. +const DEFAULT_EASING_HINT_STROKE_WIDTH = 5; /** * The helper class for creating summary graph. */ class SummaryGraphHelper { /** * Constructor. * @@ -255,14 +257,15 @@ function toPathString(segments) { segments.forEach(segment => { pathString += `L${ segment.x },${ segment.y } `; }); return pathString; } exports.createPathSegments = createPathSegments; exports.DEFAULT_DURATION_RESOLUTION = DEFAULT_DURATION_RESOLUTION; +exports.DEFAULT_EASING_HINT_STROKE_WIDTH = DEFAULT_EASING_HINT_STROKE_WIDTH; exports.DEFAULT_GRAPH_HEIGHT = DEFAULT_GRAPH_HEIGHT; exports.DEFAULT_KEYFRAMES_GRAPH_DURATION = DEFAULT_KEYFRAMES_GRAPH_DURATION; exports.getPreferredProgressThresholdByKeyframes = getPreferredProgressThresholdByKeyframes; exports.SummaryGraphHelper = SummaryGraphHelper; exports.toPathString = toPathString;
--- a/devtools/client/themes/animation.css +++ b/devtools/client/themes/animation.css @@ -355,17 +355,17 @@ .animated-property-name.warning span { text-decoration: underline dotted; } /* Keyframes Graph */ .keyframes-graph { height: 100%; - padding-top: 5px; + padding-top: 3px; width: calc(100% - var(--sidebar-width) - var(--graph-right-offset)); } .keyframes-graph-path { height: 100%; width: 100%; } @@ -385,16 +385,37 @@ fill: #ea800088; stroke: #ea8000; } .keyframes-graph-path .color-path path { stroke: none; } +.keyframes-graph .keyframes-graph-path .hint path { + fill: none; + stroke-linecap: round; + stroke-opacity: 0; +} + +.keyframes-graph-path .hint path:hover { + stroke-opacity: 1; +} + +.keyframes-graph-path .hint rect { + fill-opacity: 0.1; + stroke: #00b0bd; + stroke-opacity: 0; + vector-effect: non-scaling-stroke; +} + +.keyframes-graph-path .hint rect:hover { + stroke-opacity: 1; +} + /* No Animation Panel */ .animation-error-message { overflow: auto; } .animation-error-message > p { white-space: pre; }