Bug 1406285 - Part 7: Implement effect timing graph. r=gl

MozReview-Commit-ID: DIrt9PdY2Nd

--HG--
extra : rebase_source : d3c2829abefda76a8099be746cfd1bb38b236995
This commit is contained in:
Daisuke Akatsuka 2018-01-18 12:46:38 +09:00
Родитель 32e5aaaef8
Коммит 9f2d2b30d6
5 изменённых файлов: 113 добавлений и 5 удалений

Просмотреть файл

@ -0,0 +1,72 @@
/* 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 PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const { SummaryGraphHelper, toPathString } = require("../../utils/graph-helper");
const TimingPath = require("./TimingPath");
class EffectTimingPath extends TimingPath {
static get propTypes() {
return {
animation: PropTypes.object.isRequired,
durationPerPixel: PropTypes.number.isRequired,
simulateAnimation: PropTypes.func.isRequired,
totalDuration: PropTypes.number.isRequired,
};
}
render() {
const {
animation,
durationPerPixel,
simulateAnimation,
totalDuration,
} = this.props;
const { state } = animation;
const effectTiming = Object.assign({}, state, {
iterations: state.iterationCount ? state.iterationCount : Infinity
});
const simulatedAnimation = simulateAnimation(null, effectTiming, false);
const endTime = simulatedAnimation.effect.getComputedTiming().endTime;
const getValueFunc = time => {
if (time < 0) {
return { x: time, y: 0 };
}
simulatedAnimation.currentTime = time < endTime ? time : endTime;
return Math.max(simulatedAnimation.effect.getComputedTiming().progress, 0);
};
const toPathStringFunc = segments => {
const firstSegment = segments[0];
let pathString = `M${ firstSegment.x },0 `;
pathString += toPathString(segments);
const lastSegment = segments[segments.length - 1];
pathString += `L${ lastSegment.x },0`;
return pathString;
};
const helper = new SummaryGraphHelper(state, null,
totalDuration, durationPerPixel,
getValueFunc, toPathStringFunc);
const offset = state.previousStartTime ? state.previousStartTime : 0;
return dom.g(
{
className: "animation-effect-timing-path",
transform: `translate(${ offset })`
},
super.renderGraph(state, helper)
);
}
}
module.exports = EffectTimingPath;

Просмотреть файл

@ -10,6 +10,9 @@ const dom = require("devtools/client/shared/vendor/react-dom-factories");
const ReactDOM = require("devtools/client/shared/vendor/react-dom"); const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const ComputedTimingPath = createFactory(require("./ComputedTimingPath")); const ComputedTimingPath = createFactory(require("./ComputedTimingPath"));
const EffectTimingPath = createFactory(require("./EffectTimingPath"));
const { DEFAULT_GRAPH_HEIGHT } = require("../../utils/graph-helper");
// Minimum opacity for semitransparent fill color for keyframes's easing graph. // Minimum opacity for semitransparent fill color for keyframes's easing graph.
const MIN_KEYFRAMES_EASING_OPACITY = 0.5; const MIN_KEYFRAMES_EASING_OPACITY = 0.5;
@ -163,7 +166,8 @@ class SummaryGraphPath extends PureComponent {
{ {
className: "animation-summary-graph-path", className: "animation-summary-graph-path",
preserveAspectRatio: "none", preserveAspectRatio: "none",
viewBox: `${ startTime } -1 ${ totalDuration } 1` viewBox: `${ startTime } -${ DEFAULT_GRAPH_HEIGHT } `
+ `${ totalDuration } ${ DEFAULT_GRAPH_HEIGHT }`,
}, },
keyframesList.map(keyframes => keyframesList.map(keyframes =>
ComputedTimingPath( ComputedTimingPath(
@ -176,7 +180,18 @@ class SummaryGraphPath extends PureComponent {
totalDuration, totalDuration,
} }
) )
),
animation.state.easing !== "linear" ?
EffectTimingPath(
{
animation,
durationPerPixel,
simulateAnimation,
totalDuration,
}
) )
:
null
); );
} }
} }

Просмотреть файл

@ -4,6 +4,7 @@
DevToolsModules( DevToolsModules(
'ComputedTimingPath.js', 'ComputedTimingPath.js',
'EffectTimingPath.js',
'SummaryGraph.js', 'SummaryGraph.js',
'SummaryGraphPath.js', 'SummaryGraphPath.js',
'TimingPath.js' 'TimingPath.js'

Просмотреть файл

@ -7,6 +7,9 @@
// BOUND_EXCLUDING_TIME should be less than 1ms and is used to exclude start // BOUND_EXCLUDING_TIME should be less than 1ms and is used to exclude start
// and end bounds when dividing duration in createPathSegments. // and end bounds when dividing duration in createPathSegments.
const BOUND_EXCLUDING_TIME = 0.001; const BOUND_EXCLUDING_TIME = 0.001;
// We define default graph height since if the height of viewport in SVG is
// too small (e.g. 1), vector-effect may not be able to calculate correctly.
const DEFAULT_GRAPH_HEIGHT = 100;
// DEFAULT_MIN_PROGRESS_THRESHOLD shoud be between more than 0 to 1. // DEFAULT_MIN_PROGRESS_THRESHOLD shoud be between more than 0 to 1.
const DEFAULT_MIN_PROGRESS_THRESHOLD = 0.1; const DEFAULT_MIN_PROGRESS_THRESHOLD = 0.1;
// In the createPathSegments function, an animation duration is divided by // In the createPathSegments function, an animation duration is divided by
@ -14,8 +17,8 @@ const DEFAULT_MIN_PROGRESS_THRESHOLD = 0.1;
// But depending on the timing-function, we may be not able to make the graph // But depending on the timing-function, we may be not able to make the graph
// smoothly progress if this resolution is not high enough. // smoothly progress if this resolution is not high enough.
// So, if the difference of animation progress between 2 divisions is more than // So, if the difference of animation progress between 2 divisions is more than
// DEFAULT_MIN_PROGRESS_THRESHOLD, then createPathSegments re-divides // DEFAULT_MIN_PROGRESS_THRESHOLD * DEFAULT_GRAPH_HEIGHT, then createPathSegments
// by DURATION_RESOLUTION. // re-divides by DURATION_RESOLUTION.
// DURATION_RESOLUTION shoud be integer and more than 2. // DURATION_RESOLUTION shoud be integer and more than 2.
const DURATION_RESOLUTION = 4; const DURATION_RESOLUTION = 4;
@ -45,7 +48,8 @@ class SummaryGraphHelper {
getValueFunc, toPathStringFunc) { getValueFunc, toPathStringFunc) {
this.totalDuration = totalDuration; this.totalDuration = totalDuration;
this.minSegmentDuration = minSegmentDuration; this.minSegmentDuration = minSegmentDuration;
this.minProgressThreshold = getPreferredProgressThreshold(state, keyframes); this.minProgressThreshold =
getPreferredProgressThreshold(state, keyframes) * DEFAULT_GRAPH_HEIGHT;
this.durationResolution = getPreferredDurationResolution(keyframes); this.durationResolution = getPreferredDurationResolution(keyframes);
this.getValue = getValueFunc; this.getValue = getValueFunc;
this.toPathString = toPathStringFunc; this.toPathString = toPathStringFunc;
@ -79,7 +83,7 @@ class SummaryGraphHelper {
*/ */
getSegment(time) { getSegment(time) {
const value = this.getValue(time); const value = this.getValue(time);
return { x: time, y: value }; return { x: time, y: value * DEFAULT_GRAPH_HEIGHT };
} }
} }
@ -235,5 +239,6 @@ function toPathString(segments) {
return pathString; return pathString;
} }
module.exports.DEFAULT_GRAPH_HEIGHT = DEFAULT_GRAPH_HEIGHT;
exports.SummaryGraphHelper = SummaryGraphHelper; exports.SummaryGraphHelper = SummaryGraphHelper;
exports.toPathString = toPathString; exports.toPathString = toPathString;

Просмотреть файл

@ -58,14 +58,17 @@
.animation-item.cssanimation { .animation-item.cssanimation {
--computed-timing-graph-color: var(--theme-contrast-background); --computed-timing-graph-color: var(--theme-contrast-background);
--effect-timing-graph-color: var(--theme-highlight-lightorange);
} }
.animation-item.csstransition { .animation-item.csstransition {
--computed-timing-graph-color: var(--theme-highlight-blue); --computed-timing-graph-color: var(--theme-highlight-blue);
--effect-timing-graph-color: var(--theme-highlight-bluegrey);
} }
.animation-item.scriptanimation { .animation-item.scriptanimation {
--computed-timing-graph-color: var(--theme-graphs-green); --computed-timing-graph-color: var(--theme-graphs-green);
--effect-timing-graph-color: var(--theme-highlight-green);
} }
/* Animation Target */ /* Animation Target */
@ -103,6 +106,18 @@
opacity: 0.3; opacity: 0.3;
} }
.animation-effect-timing-path path {
fill: none;
stroke: var(--effect-timing-graph-color);
stroke-dasharray: 2px 2px;
transform: scale(1, -1);
vector-effect: non-scaling-stroke;
}
.animation-effect-timing-path path.infinity:nth-child(n+2) {
opacity: 0.3;
}
/* No Animation Panel */ /* No Animation Panel */
.animation-error-message { .animation-error-message {
overflow: auto; overflow: auto;