зеркало из https://github.com/mozilla/gecko-dev.git
214 строки
6.7 KiB
JavaScript
214 строки
6.7 KiB
JavaScript
/* 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 {
|
|
FrontClassWithSpec,
|
|
registerFront,
|
|
} = require("devtools/shared/protocol");
|
|
const {
|
|
animationPlayerSpec,
|
|
animationsSpec,
|
|
} = require("devtools/shared/specs/animation");
|
|
|
|
class AnimationPlayerFront extends FrontClassWithSpec(animationPlayerSpec) {
|
|
constructor(conn, targetFront, parentFront) {
|
|
super(conn, targetFront, parentFront);
|
|
|
|
this.state = {};
|
|
this.before("changed", this.onChanged.bind(this));
|
|
}
|
|
|
|
form(form) {
|
|
this._form = form;
|
|
this.state = this.initialState;
|
|
}
|
|
|
|
/**
|
|
* If the AnimationsActor was given a reference to the WalkerActor previously
|
|
* then calling this getter will return the animation target NodeFront.
|
|
*/
|
|
get animationTargetNodeFront() {
|
|
if (!this._form.animationTargetNodeActorID) {
|
|
return null;
|
|
}
|
|
|
|
return this.conn.getFrontByID(this._form.animationTargetNodeActorID);
|
|
}
|
|
|
|
/**
|
|
* Getter for the initial state of the player. Up to date states can be
|
|
* retrieved by calling the getCurrentState method.
|
|
*/
|
|
get initialState() {
|
|
return {
|
|
type: this._form.type,
|
|
startTime: this._form.startTime,
|
|
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,
|
|
iterationStart: this._form.iterationStart,
|
|
easing: this._form.easing,
|
|
fill: this._form.fill,
|
|
direction: this._form.direction,
|
|
animationTimingFunction: this._form.animationTimingFunction,
|
|
isRunningOnCompositor: this._form.isRunningOnCompositor,
|
|
propertyState: this._form.propertyState,
|
|
documentCurrentTime: this._form.documentCurrentTime,
|
|
createdTime: this._form.createdTime,
|
|
currentTimeAtCreated: this._form.currentTimeAtCreated,
|
|
absoluteValues: this.calculateAbsoluteValues(this._form),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Executed when the AnimationPlayerActor emits a "changed" event. Used to
|
|
* update the local knowledge of the state.
|
|
*/
|
|
onChanged(partialState) {
|
|
const { state } = this.reconstructState(partialState);
|
|
this.state = state;
|
|
}
|
|
|
|
/**
|
|
* Refresh the current state of this animation on the client from information
|
|
* found on the server. Doesn't return anything, just stores the new state.
|
|
*/
|
|
async refreshState() {
|
|
const data = await this.getCurrentState();
|
|
if (this.currentStateHasChanged) {
|
|
this.state = data;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* getCurrentState interceptor re-constructs incomplete states since the actor
|
|
* only sends the values that have changed.
|
|
*/
|
|
getCurrentState() {
|
|
this.currentStateHasChanged = false;
|
|
return super.getCurrentState().then(partialData => {
|
|
const { state, hasChanged } = this.reconstructState(partialData);
|
|
this.currentStateHasChanged = hasChanged;
|
|
return state;
|
|
});
|
|
}
|
|
|
|
reconstructState(data) {
|
|
let hasChanged = false;
|
|
|
|
for (const key in this.state) {
|
|
if (typeof data[key] === "undefined") {
|
|
data[key] = this.state[key];
|
|
} else if (data[key] !== this.state[key]) {
|
|
hasChanged = true;
|
|
}
|
|
}
|
|
|
|
data.absoluteValues = this.calculateAbsoluteValues(data);
|
|
return { state: data, hasChanged };
|
|
}
|
|
|
|
calculateAbsoluteValues(data) {
|
|
const {
|
|
createdTime,
|
|
currentTime,
|
|
currentTimeAtCreated,
|
|
delay,
|
|
duration,
|
|
endDelay = 0,
|
|
fill,
|
|
iterationCount,
|
|
playbackRate,
|
|
} = data;
|
|
|
|
const toRate = v => v / Math.abs(playbackRate);
|
|
const isPositivePlaybackRate = playbackRate > 0;
|
|
let absoluteDelay = 0;
|
|
let absoluteEndDelay = 0;
|
|
let isDelayFilled = false;
|
|
let isEndDelayFilled = false;
|
|
|
|
if (isPositivePlaybackRate) {
|
|
absoluteDelay = toRate(delay);
|
|
absoluteEndDelay = toRate(endDelay);
|
|
isDelayFilled = fill === "both" || fill === "backwards";
|
|
isEndDelayFilled = fill === "both" || fill === "forwards";
|
|
} else {
|
|
absoluteDelay = toRate(endDelay);
|
|
absoluteEndDelay = toRate(delay);
|
|
isDelayFilled = fill === "both" || fill === "forwards";
|
|
isEndDelayFilled = fill === "both" || fill === "backwards";
|
|
}
|
|
|
|
let endTime = 0;
|
|
|
|
if (duration === Infinity) {
|
|
// Set endTime so as to enable the scrubber with keeping the consinstency of UI
|
|
// even the duration was Infinity. In case of delay is longer than zero, handle
|
|
// the graph duration as double of the delay amount. In case of no delay, handle
|
|
// the duration as 1ms which is short enough so as to make the scrubber movable
|
|
// and the limited duration is prioritized.
|
|
endTime = absoluteDelay > 0 ? absoluteDelay * 2 : 1;
|
|
} else {
|
|
endTime =
|
|
absoluteDelay +
|
|
toRate(duration * (iterationCount || 1)) +
|
|
absoluteEndDelay;
|
|
}
|
|
|
|
const absoluteCreatedTime = isPositivePlaybackRate
|
|
? createdTime
|
|
: createdTime - endTime;
|
|
const absoluteCurrentTimeAtCreated = isPositivePlaybackRate
|
|
? currentTimeAtCreated
|
|
: endTime - currentTimeAtCreated;
|
|
const animationCurrentTime = isPositivePlaybackRate
|
|
? currentTime
|
|
: endTime - currentTime;
|
|
const absoluteCurrentTime =
|
|
absoluteCreatedTime + toRate(animationCurrentTime);
|
|
const absoluteStartTime = absoluteCreatedTime + Math.min(absoluteDelay, 0);
|
|
const absoluteStartTimeAtCreated =
|
|
absoluteCreatedTime + absoluteCurrentTimeAtCreated;
|
|
// To show whole graph with endDelay, we add negative endDelay amount to endTime.
|
|
const endTimeWithNegativeEndDelay = endTime - Math.min(absoluteEndDelay, 0);
|
|
const absoluteEndTime = absoluteCreatedTime + endTimeWithNegativeEndDelay;
|
|
|
|
return {
|
|
createdTime: absoluteCreatedTime,
|
|
currentTime: absoluteCurrentTime,
|
|
currentTimeAtCreated: absoluteCurrentTimeAtCreated,
|
|
delay: absoluteDelay,
|
|
endDelay: absoluteEndDelay,
|
|
endTime: absoluteEndTime,
|
|
isDelayFilled,
|
|
isEndDelayFilled,
|
|
startTime: absoluteStartTime,
|
|
startTimeAtCreated: absoluteStartTimeAtCreated,
|
|
};
|
|
}
|
|
}
|
|
|
|
exports.AnimationPlayerFront = AnimationPlayerFront;
|
|
registerFront(AnimationPlayerFront);
|
|
|
|
class AnimationsFront extends FrontClassWithSpec(animationsSpec) {
|
|
constructor(client, targetFront, parentFront) {
|
|
super(client, targetFront, parentFront);
|
|
|
|
// Attribute name from which to retrieve the actorID out of the target actor's form
|
|
this.formAttributeName = "animationsActor";
|
|
}
|
|
}
|
|
|
|
exports.AnimationsFront = AnimationsFront;
|
|
registerFront(AnimationsFront);
|