Bug 1253494 - Part1 Implement endDelay representation in the animation inspector r=pbro
[mq]: Represent_endDelay_in_the_animation_inspector
MozReview-Commit-ID: CxUvQRu6Yrn
--- a/devtools/client/animationinspector/components/animation-time-block.js
+++ b/devtools/client/animationinspector/components/animation-time-block.js
@@ -49,17 +49,17 @@ AnimationTimeBlock.prototype = {
this.unrender();
this.animation = animation;
let {state} = this.animation;
// Create a container element to hold the delay and iterations.
// It is positioned according to its delay (divided by the playbackrate),
// and its width is according to its duration (divided by the playbackrate).
- let {x, iterationW, delayX, delayW, negativeDelayW} =
+ let {x, iterationW, delayX, delayW, negativeDelayW, endDelayX, endDelayW} =
TimeScale.getAnimationDimensions(animation);
createNode({
parent: this.containerEl,
attributes: {
"class": "iterations" + (state.iterationCount ? "" : " infinite"),
// Individual iterations are represented by setting the size of the
// repeating linear-gradient.
@@ -94,38 +94,59 @@ AnimationTimeBlock.prototype = {
parent: this.containerEl,
attributes: {
"class": "delay" + (state.delay < 0 ? " negative" : ""),
"style": `left:${delayX}%;
width:${delayW}%;`
}
});
}
+
+ // endDelay
+ if (state.endDelay) {
+ createNode({
+ parent: this.containerEl,
+ attributes: {
+ "class": "end-delay" + (state.endDelay < 0 ? " negative" : ""),
+ "style": `left:${endDelayX}%;
+ width:${endDelayW}%;`
+ }
+ });
+ }
},
getTooltipText: function(state) {
let getTime = time => L10N.getFormatStr("player.timeLabel",
L10N.numberWithDecimals(time / 1000, 2));
let text = "";
// Adding the name.
text += getFormattedAnimationTitle({state});
text += "\n";
// Adding the delay.
- text += L10N.getStr("player.animationDelayLabel") + " ";
- text += getTime(state.delay);
- text += "\n";
+ if (state.delay) {
+ text += L10N.getStr("player.animationDelayLabel") + " ";
+ text += getTime(state.delay);
+ text += "\n";
+ }
// Adding the duration.
text += L10N.getStr("player.animationDurationLabel") + " ";
text += getTime(state.duration);
text += "\n";
+ // Adding the endDelay.
+ if (state.endDelay) {
+ text += L10N.getStr("player.animationEndDelayLabel") + " ";
+ text += getTime(state.endDelay);
+ text += "\n";
+ }
+
// Adding the iteration count (the infinite symbol, or an integer).
if (state.iterationCount !== 1) {
text += L10N.getStr("player.animationIterationCountLabel") + " ";
text += state.iterationCount ||
L10N.getStr("player.infiniteIterationCountText");
text += "\n";
}
--- a/devtools/client/animationinspector/utils.js
+++ b/devtools/client/animationinspector/utils.js
@@ -194,30 +194,42 @@ var TimeScale = {
minStartTime: Infinity,
maxEndTime: 0,
/**
* Add a new animation to time scale.
* @param {Object} state A PlayerFront.state object.
*/
addAnimation: function(state) {
- let {previousStartTime, delay, duration,
+ let {previousStartTime, delay, duration, endDelay,
iterationCount, playbackRate} = state;
+ endDelay = typeof endDelay === "undefined" ? 0 : endDelay;
+ let toRate = v => v / playbackRate;
+ let minZero = v => Math.max(v, 0);
+ let rateRelativeDuration =
+ toRate(duration * (!iterationCount ? 1 : iterationCount));
// Negative-delayed animations have their startTimes set such that we would
// be displaying the delay outside the time window if we didn't take it into
// account here.
- let relevantDelay = delay < 0 ? delay / playbackRate : 0;
+ let relevantDelay = delay < 0 ? toRate(delay) : 0;
previousStartTime = previousStartTime || 0;
- this.minStartTime = Math.min(this.minStartTime,
- previousStartTime + relevantDelay);
- let length = (delay / playbackRate) +
- ((duration / playbackRate) *
- (!iterationCount ? 1 : iterationCount));
+ let startTime = toRate(minZero(delay)) +
+ rateRelativeDuration +
+ endDelay;
+ this.minStartTime = Math.min(
+ this.minStartTime,
+ previousStartTime +
+ relevantDelay +
+ Math.min(startTime, 0)
+ );
+ let length = toRate(delay) +
+ rateRelativeDuration +
+ toRate(minZero(endDelay));
let endTime = previousStartTime + length;
this.maxEndTime = Math.max(this.maxEndTime, endTime);
},
/**
* Reset the current time scale.
*/
reset: function() {
@@ -289,27 +301,33 @@ var TimeScale = {
* animation in the timeline.
*/
getAnimationDimensions: function({state}) {
let start = state.previousStartTime || 0;
let duration = state.duration;
let rate = state.playbackRate;
let count = state.iterationCount;
let delay = state.delay || 0;
+ let endDelay = state.endDelay || 0;
// The start position.
let x = this.startTimeToDistance(start + (delay / rate));
// The width for a single iteration.
let w = this.durationToDistance(duration / rate);
// The width for all iterations.
let iterationW = w * (count || 1);
// The start position of the delay.
let delayX = delay < 0 ? x : this.startTimeToDistance(start);
// The width of the delay.
let delayW = this.durationToDistance(Math.abs(delay) / rate);
// The width of the delay if it is negative, 0 otherwise.
let negativeDelayW = delay < 0 ? delayW : 0;
+ // The width of the endDelay.
+ let endDelayW = this.durationToDistance(Math.abs(endDelay) / rate);
+ // The start position of the endDelay.
+ let endDelayX = endDelay < 0 ? x + w - endDelayW : x + w;
- return {x, w, iterationW, delayX, delayW, negativeDelayW};
+ return {x, w, iterationW, delayX, delayW, negativeDelayW,
+ endDelayX, endDelayW};
}
};
exports.TimeScale = TimeScale;
--- a/devtools/client/locales/en-US/animationinspector.properties
+++ b/devtools/client/locales/en-US/animationinspector.properties
@@ -38,16 +38,21 @@ player.transitionNameLabel=Transition
# displayed before the animation duration.
player.animationDurationLabel=Duration:
# LOCALIZATION NOTE (player.animationDelayLabel):
# This string is displayed in each animation player widget. It is the label
# displayed before the animation delay.
player.animationDelayLabel=Delay:
+# LOCALIZATION NOTE (player.animationEndDelayLabel):
+# This string is displayed in each animation player widget. It is the label
+# displayed before the animation endDelay.
+player.animationEndDelayLabel=End delay:
+
# LOCALIZATION NOTE (player.animationRateLabel):
# This string is displayed in each animation player widget. It is the label
# displayed before the animation playback rate.
player.animationRateLabel=Playback rate:
# LOCALIZATION NOTE (player.animationIterationCountLabel):
# This string is displayed in each animation player widget. It is the label
# displayed before the number of times the animation is set to repeat.
--- a/devtools/client/themes/animationinspector.css
+++ b/devtools/client/themes/animationinspector.css
@@ -222,17 +222,17 @@ body {
left: var(--timeline-sidebar-width);
/* Leave the width of a marker right of a track so the 100% markers can be
selected easily */
right: var(--keyframes-marker-size);
height: var(--timeline-animation-height);
}
.animation-timeline .scrubber-wrapper {
- z-index: 1;
+ z-index: 2;
pointer-events: none;
height: 100%;
}
.animation-timeline .scrubber {
position: absolute;
height: 100%;
width: 0;
@@ -371,16 +371,17 @@ body {
position: absolute;
color: var(--theme-selection-color);
height: 100%;
display: flex;
align-items: center;
padding: 0 2px;
box-sizing: border-box;
--fast-track-icon-width: 12px;
+ z-index: 1;
}
.animation-timeline .animation .name div {
/* Flex items don't support text-overflow, so a child div is used */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
@@ -393,39 +394,53 @@ body {
/* Animations running on the compositor have the fast-track background image*/
content: "";
display: block;
position: absolute;
top: 0;
right: 0;
height: 100%;
width: var(--fast-track-icon-width);
+ z-index: 1;
background-image: url("images/animation-fast-track.svg");
background-repeat: no-repeat;
background-position: center;
}
-.animation-timeline .animation .delay {
+.animation-timeline .animation .delay,
+.animation-timeline .animation .end-delay {
position: absolute;
height: 100%;
-
border: 1px solid var(--timeline-border-color);
box-sizing: border-box;
+}
+
+.animation-timeline .animation .delay {
border-width: 1px 0 1px 1px;
-
background-image: repeating-linear-gradient(45deg,
transparent,
transparent 1px,
var(--theme-selection-color) 1px,
var(--theme-selection-color) 4px);
background-color: var(--timeline-border-color);
}
-.animation-timeline .animation .delay.negative {
+.animation-timeline .animation .end-delay {
+ border-width: 1px 1px 1px 0;
+ background-image: repeating-linear-gradient(
+ -45deg,
+ transparent,
+ transparent 3px,
+ var(--timeline-border-color) 3px,
+ var(--timeline-border-color) 4px);
+}
+
+.animation-timeline .animation .delay.negative,
+.animation-timeline .animation .end-delay.negative {
/* Negative delays are displayed on top of the animation, so they need a
right border. Whereas normal delays are displayed just before the
animation, so there's already the animation's left border that serves as
a separation. */
border-width: 1px;
}
/* Animation target node gutter, contains a preview of the dom node */
--- a/devtools/server/actors/animation.js
+++ b/devtools/server/actors/animation.js
@@ -177,16 +177,24 @@ var AnimationPlayerActor = ActorClass({
* Get the animation delay from this player, in milliseconds.
* @return {Number}
*/
getDelay: function() {
return this.player.effect.getComputedTiming().delay;
},
/**
+ * Get the animation endDelay from this player, in milliseconds.
+ * @return {Number}
+ */
+ getEndDelay: function() {
+ return this.player.effect.getComputedTiming().endDelay;
+ },
+
+ /**
* Get the animation iteration count for this player. That is, how many times
* is the animation scheduled to run.
* @return {Number} The number of iterations, or null if the animation repeats
* infinitely.
*/
getIterationCount: function() {
let iterations = this.player.effect.getComputedTiming().iterations;
return iterations === "Infinity" ? null : iterations;
@@ -215,16 +223,17 @@ var AnimationPlayerActor = ActorClass({
startTime: this.player.startTime,
previousStartTime: this.previousStartTime,
currentTime: this.player.currentTime,
playState: this.player.playState,
playbackRate: this.player.playbackRate,
name: this.getName(),
duration: this.getDuration(),
delay: this.getDelay(),
+ endDelay: this.getEndDelay(),
iterationCount: this.getIterationCount(),
// animation is hitting the fast path or not. Returns false whenever the
// animation is paused as it is taken off the compositor then.
isRunningOnCompositor: this.player.isRunningOnCompositor,
// The document timeline's currentTime is being sent along too. This is
// not strictly related to the node's animationPlayer, but is useful to
// know the current time of the animation with respect to the document's.
documentCurrentTime: this.node.ownerDocument.timeline.currentTime
@@ -287,17 +296,18 @@ var AnimationPlayerActor = ActorClass({
if (hasCurrentAnimation(changedAnimations)) {
// Only consider the state has having changed if any of delay, duration
// or iterationcount has changed (for now at least).
let newState = this.getState();
let oldState = this.currentState;
hasChanged = newState.delay !== oldState.delay ||
newState.iterationCount !== oldState.iterationCount ||
- newState.duration !== oldState.duration;
+ newState.duration !== oldState.duration ||
+ newState.endDelay !== oldState.endDelay;
break;
}
}
if (hasChanged) {
events.emit(this, "changed", this.getCurrentState());
}
},
@@ -426,16 +436,17 @@ var AnimationPlayerFront = FrontClass(An
startTime: this._form.startTime,
previousStartTime: this._form.previousStartTime,
currentTime: this._form.currentTime,
playState: this._form.playState,
playbackRate: this._form.playbackRate,
name: this._form.name,
duration: this._form.duration,
delay: this._form.delay,
+ endDelay: this._form.endDelay,
iterationCount: this._form.iterationCount,
isRunningOnCompositor: this._form.isRunningOnCompositor,
documentCurrentTime: this._form.documentCurrentTime
};
},
/**
* Executed when the AnimationPlayerActor emits a "changed" event. Used to