/* 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 { getFormatStr, } = require("resource://devtools/client/inspector/animation/utils/l10n.js"); // If total duration for all animations is eqaul to or less than // TIME_FORMAT_MAX_DURATION_IN_MS, the text which expresses time is in milliseconds, // and seconds otherwise. Use in formatTime function. const TIME_FORMAT_MAX_DURATION_IN_MS = 4000; /** * TimeScale object holds the total duration, start time and end time and zero position * time information for all animations which should be displayed, and is used to calculate * the displayed area for each animation. */ class TimeScale { constructor(animations) { let resultCurrentTime = -Number.MAX_VALUE; let resultMinStartTime = Infinity; let resultMaxEndTime = 0; let resultZeroPositionTime = 0; for (const animation of animations) { const { currentTime, currentTimeAtCreated, delay, endTime, startTimeAtCreated, } = animation.state.absoluteValues; let { startTime } = animation.state.absoluteValues; const negativeDelay = Math.min(delay, 0); let zeroPositionTime = 0; // To shift the zero position time is the following two patterns. // * Animation has negative current time which is smaller than negative delay. // * Animation has negative delay. // Furthermore, we should override the zero position time if we will need to // expand the duration due to this negative current time or negative delay of // this target animation. if (currentTimeAtCreated < negativeDelay) { startTime = startTimeAtCreated; zeroPositionTime = Math.abs(currentTimeAtCreated); } else if (negativeDelay < 0) { zeroPositionTime = Math.abs(negativeDelay); } if (startTime < resultMinStartTime) { resultMinStartTime = startTime; // Override the previous calculated zero position only if the duration will be // expanded. resultZeroPositionTime = zeroPositionTime; } else { resultZeroPositionTime = Math.max( resultZeroPositionTime, zeroPositionTime ); } resultMaxEndTime = Math.max(resultMaxEndTime, endTime); resultCurrentTime = Math.max(resultCurrentTime, currentTime); } this.minStartTime = resultMinStartTime; this.maxEndTime = resultMaxEndTime; this.currentTime = resultCurrentTime; this.zeroPositionTime = resultZeroPositionTime; } /** * Convert a distance in % to a time, in the current time scale. The time * will be relative to the zero position time. * i.e., If zeroPositionTime will be negative and specified time is shorter * than the absolute value of zero position time, relative time will be * negative time. * * @param {Number} distance * @return {Number} */ distanceToRelativeTime(distance) { return (this.getDuration() * distance) / 100 - this.zeroPositionTime; } /** * Depending on the time scale, format the given time as milliseconds or * seconds. * * @param {Number} time * @return {String} The formatted time string. */ formatTime(time) { // Ignore negative zero if (Math.abs(time) < 1 / 1000) { time = 0.0; } // Format in milliseconds if the total duration is short enough. if (this.getDuration() <= TIME_FORMAT_MAX_DURATION_IN_MS) { return getFormatStr("timeline.timeGraduationLabel", time.toFixed(0)); } // Otherwise format in seconds. return getFormatStr("player.timeLabel", (time / 1000).toFixed(1)); } /** * Return entire animations duration. * * @return {Number} duration */ getDuration() { return this.maxEndTime - this.minStartTime; } /** * Return current time of this time scale represents. * * @return {Number} */ getCurrentTime() { return this.currentTime - this.minStartTime; } /** * Return end time of given animation. * This time does not include playbackRate and cratedTime. * Also, if the animation has infinite iterations, this returns Infinity. * * @param {Object} animation * @return {Numbber} end time */ getEndTime({ state }) { return state.iterationCount ? state.delay + state.duration * state.iterationCount + state.endDelay : Infinity; } } module.exports = TimeScale;