blob: 7afa652ff5740a2161e32dbcffb51b682b4d0589 [file] [log] [blame]
Blink Reformat4c46d092018-04-07 15:32:371// Copyright (c) 2015 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
Paul Lewis752031f2020-01-09 14:14:234
Tim van der Lippe91d32562020-02-13 15:08:475import * as Common from '../common/common.js';
6import * as Host from '../host/host.js';
Simon Zünd684ebae2020-03-09 11:44:167import * as Platform from '../platform/platform.js';
Tim van der Lippe91d32562020-02-13 15:08:478import * as SDK from '../sdk/sdk.js';
9import * as UI from '../ui/ui.js';
10
Paul Lewis752031f2020-01-09 14:14:2311import {AnimationGroupPreviewUI} from './AnimationGroupPreviewUI.js';
12import {AnimationEffect, AnimationGroup, AnimationImpl, AnimationModel, Events} from './AnimationModel.js'; // eslint-disable-line no-unused-vars
13import {AnimationScreenshotPopover} from './AnimationScreenshotPopover.js';
14import {AnimationUI} from './AnimationUI.js';
15
Paul Lewiseae437e2020-10-19 14:09:4516/** @type {!WeakMap<!SDK.DOMModel.DOMNode, !NodeUI>} */
17const nodeUIsByNode = new WeakMap();
18
19/** @type {!WeakMap<!HTMLElement, number>} */
20const playbackRates = new WeakMap();
21
Blink Reformat4c46d092018-04-07 15:32:3722/**
Tim van der Lippe91d32562020-02-13 15:08:4723 * @implements {SDK.SDKModel.SDKModelObserver<!AnimationModel>}
Blink Reformat4c46d092018-04-07 15:32:3724 * @unrestricted
25 */
Tim van der Lippe91d32562020-02-13 15:08:4726export class AnimationTimeline extends UI.Widget.VBox {
Blink Reformat4c46d092018-04-07 15:32:3727 constructor() {
28 super(true);
Jack Franklin71519f82020-11-03 12:08:5929 this.registerRequiredCSS('animation/animationTimeline.css', {enableLegacyPatching: true});
Blink Reformat4c46d092018-04-07 15:32:3730 this.element.classList.add('animations-timeline');
31
Paul Lewis44e37572020-10-28 15:57:2632 this._grid = UI.UIUtils.createSVGChild(this.contentElement, 'svg', 'animation-timeline-grid');
Blink Reformat4c46d092018-04-07 15:32:3733
34 this._playbackRate = 1;
35 this._allPaused = false;
36 this._createHeader();
37 this._animationsContainer = this.contentElement.createChild('div', 'animation-timeline-rows');
38 const timelineHint = this.contentElement.createChild('div', 'animation-timeline-rows-hint');
39 timelineHint.textContent = ls`Select an effect above to inspect and modify.`;
40
Paul Lewiseae437e2020-10-19 14:09:4541 /** @type {!Array<!HTMLElement>} */
42 this._playbackRateButtons;
43 /** @type {!HTMLElement} */
44 this._previewContainer;
45 /** @type {!HTMLElement} */
46 this._timelineScrubber;
47 /** @type {!HTMLElement} */
48 this._currentTime;
49 /** @type {!UI.PopoverHelper.PopoverHelper} */
50 this._popoverHelper;
51 /** @type {!UI.Toolbar.ToolbarButton} */
52 this._clearButton;
53 /** @type {?AnimationGroup} */
54 this._selectedGroup;
55 /** @type {!Array<!AnimationUI>} */
56 this._renderQueue;
57
Blink Reformat4c46d092018-04-07 15:32:3758 /** @const */ this._defaultDuration = 100;
59 this._duration = this._defaultDuration;
60 /** @const */ this._timelineControlsWidth = 150;
Paul Lewis752031f2020-01-09 14:14:2361 /** @type {!Map.<!Protocol.DOM.BackendNodeId, !NodeUI>} */
Blink Reformat4c46d092018-04-07 15:32:3762 this._nodesMap = new Map();
Paul Lewiseae437e2020-10-19 14:09:4563 /** @type {!Array<!AnimationUI>} */
Blink Reformat4c46d092018-04-07 15:32:3764 this._uiAnimations = [];
Paul Lewiseae437e2020-10-19 14:09:4565 /** @type {!Array<!AnimationGroup>} */
Blink Reformat4c46d092018-04-07 15:32:3766 this._groupBuffer = [];
Paul Lewis752031f2020-01-09 14:14:2367 /** @type {!Map.<!AnimationGroup, !AnimationGroupPreviewUI>} */
Blink Reformat4c46d092018-04-07 15:32:3768 this._previewMap = new Map();
69 this._symbol = Symbol('animationTimeline');
Paul Lewis752031f2020-01-09 14:14:2370 /** @type {!Map.<string, !AnimationImpl>} */
Blink Reformat4c46d092018-04-07 15:32:3771 this._animationsMap = new Map();
Paul Lewisdaac1062020-03-05 14:37:1072 SDK.SDKModel.TargetManager.instance().addModelListener(
Tim van der Lippe91d32562020-02-13 15:08:4773 SDK.DOMModel.DOMModel, SDK.DOMModel.Events.NodeRemoved, this._nodeRemoved, this);
Paul Lewisdaac1062020-03-05 14:37:1074 SDK.SDKModel.TargetManager.instance().observeModels(AnimationModel, this);
Tim van der Lipped1a00aa2020-08-19 16:03:5675 UI.Context.Context.instance().addFlavorChangeListener(SDK.DOMModel.DOMNode, this._nodeChanged, this);
Blink Reformat4c46d092018-04-07 15:32:3776 }
77
78 /**
79 * @override
80 */
81 wasShown() {
Paul Lewisdaac1062020-03-05 14:37:1082 for (const animationModel of SDK.SDKModel.TargetManager.instance().models(AnimationModel)) {
Blink Reformat4c46d092018-04-07 15:32:3783 this._addEventListeners(animationModel);
Tim van der Lippe1d6e57a2019-09-30 11:55:3484 }
Blink Reformat4c46d092018-04-07 15:32:3785 }
86
87 /**
88 * @override
89 */
90 willHide() {
Paul Lewisdaac1062020-03-05 14:37:1091 for (const animationModel of SDK.SDKModel.TargetManager.instance().models(AnimationModel)) {
Blink Reformat4c46d092018-04-07 15:32:3792 this._removeEventListeners(animationModel);
Tim van der Lippe1d6e57a2019-09-30 11:55:3493 }
Paul Lewiseae437e2020-10-19 14:09:4594
95 if (this._popoverHelper) {
96 this._popoverHelper.hidePopover();
97 }
Blink Reformat4c46d092018-04-07 15:32:3798 }
99
100 /**
101 * @override
Paul Lewis752031f2020-01-09 14:14:23102 * @param {!AnimationModel} animationModel
Blink Reformat4c46d092018-04-07 15:32:37103 */
104 modelAdded(animationModel) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34105 if (this.isShowing()) {
Blink Reformat4c46d092018-04-07 15:32:37106 this._addEventListeners(animationModel);
Tim van der Lippe1d6e57a2019-09-30 11:55:34107 }
Blink Reformat4c46d092018-04-07 15:32:37108 }
109
110 /**
111 * @override
Paul Lewis752031f2020-01-09 14:14:23112 * @param {!AnimationModel} animationModel
Blink Reformat4c46d092018-04-07 15:32:37113 */
114 modelRemoved(animationModel) {
115 this._removeEventListeners(animationModel);
116 }
117
118 /**
Paul Lewis752031f2020-01-09 14:14:23119 * @param {!AnimationModel} animationModel
Blink Reformat4c46d092018-04-07 15:32:37120 */
121 _addEventListeners(animationModel) {
122 animationModel.ensureEnabled();
Paul Lewis752031f2020-01-09 14:14:23123 animationModel.addEventListener(Events.AnimationGroupStarted, this._animationGroupStarted, this);
124 animationModel.addEventListener(Events.ModelReset, this._reset, this);
Blink Reformat4c46d092018-04-07 15:32:37125 }
126
127 /**
Paul Lewis752031f2020-01-09 14:14:23128 * @param {!AnimationModel} animationModel
Blink Reformat4c46d092018-04-07 15:32:37129 */
130 _removeEventListeners(animationModel) {
Paul Lewis752031f2020-01-09 14:14:23131 animationModel.removeEventListener(Events.AnimationGroupStarted, this._animationGroupStarted, this);
132 animationModel.removeEventListener(Events.ModelReset, this._reset, this);
Blink Reformat4c46d092018-04-07 15:32:37133 }
134
135 _nodeChanged() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34136 for (const nodeUI of this._nodesMap.values()) {
Blink Reformat4c46d092018-04-07 15:32:37137 nodeUI._nodeChanged();
Tim van der Lippe1d6e57a2019-09-30 11:55:34138 }
Blink Reformat4c46d092018-04-07 15:32:37139 }
140
141 /**
Paul Lewiseae437e2020-10-19 14:09:45142 * @return {!HTMLElement} element
Blink Reformat4c46d092018-04-07 15:32:37143 */
144 _createScrubber() {
Paul Lewiseae437e2020-10-19 14:09:45145 this._timelineScrubber = /** @type {!HTMLElement} */ (document.createElement('div'));
Tim van der Lippee7f27052020-05-01 15:15:28146 this._timelineScrubber.classList.add('animation-scrubber');
147 this._timelineScrubber.classList.add('hidden');
Blink Reformat4c46d092018-04-07 15:32:37148 this._timelineScrubberLine = this._timelineScrubber.createChild('div', 'animation-scrubber-line');
149 this._timelineScrubberLine.createChild('div', 'animation-scrubber-head');
150 this._timelineScrubber.createChild('div', 'animation-time-overlay');
151 return this._timelineScrubber;
152 }
153
154 _createHeader() {
155 const toolbarContainer = this.contentElement.createChild('div', 'animation-timeline-toolbar-container');
Tim van der Lippe91d32562020-02-13 15:08:47156 const topToolbar = new UI.Toolbar.Toolbar('animation-timeline-toolbar', toolbarContainer);
157 this._clearButton = new UI.Toolbar.ToolbarButton(ls`Clear all`, 'largeicon-clear');
158 this._clearButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, this._reset.bind(this));
Kalon Hinds0fae2e72020-01-08 20:02:02159 topToolbar.appendToolbarItem(this._clearButton);
Blink Reformat4c46d092018-04-07 15:32:37160 topToolbar.appendSeparator();
161
Tim van der Lippe91d32562020-02-13 15:08:47162 this._pauseButton = new UI.Toolbar.ToolbarToggle(ls`Pause all`, 'largeicon-pause', 'largeicon-resume');
163 this._pauseButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, this._togglePauseAll.bind(this));
Blink Reformat4c46d092018-04-07 15:32:37164 topToolbar.appendToolbarItem(this._pauseButton);
165
166 const playbackRateControl = toolbarContainer.createChild('div', 'animation-playback-rate-control');
Kalon Hinds0fae2e72020-01-08 20:02:02167 playbackRateControl.addEventListener('keydown', this._handlePlaybackRateControlKeyDown.bind(this));
Kalon Hinds08dd3682020-01-10 23:51:25168 UI.ARIAUtils.markAsListBox(playbackRateControl);
169 UI.ARIAUtils.setAccessibleName(playbackRateControl, ls`Playback rates`);
170
Paul Lewiseae437e2020-10-19 14:09:45171 /** @type {!Array<!HTMLElement>} */
Blink Reformat4c46d092018-04-07 15:32:37172 this._playbackRateButtons = [];
Paul Lewis752031f2020-01-09 14:14:23173 for (const playbackRate of GlobalPlaybackRates) {
Paul Lewiseae437e2020-10-19 14:09:45174 const button =
175 /** @type {!HTMLElement} */ (playbackRateControl.createChild('button', 'animation-playback-rate-button'));
Blink Reformat4c46d092018-04-07 15:32:37176 button.textContent = playbackRate ? ls`${playbackRate * 100}%` : ls`Pause`;
Paul Lewiseae437e2020-10-19 14:09:45177 playbackRates.set(button, playbackRate);
Blink Reformat4c46d092018-04-07 15:32:37178 button.addEventListener('click', this._setPlaybackRate.bind(this, playbackRate));
Kalon Hinds08dd3682020-01-10 23:51:25179 UI.ARIAUtils.markAsOption(button);
Blink Reformat4c46d092018-04-07 15:32:37180 button.title = ls`Set speed to ${button.textContent}`;
Kalon Hinds0fae2e72020-01-08 20:02:02181 button.tabIndex = -1;
Blink Reformat4c46d092018-04-07 15:32:37182 this._playbackRateButtons.push(button);
183 }
184 this._updatePlaybackControls();
185
Paul Lewiseae437e2020-10-19 14:09:45186 this._previewContainer =
187 /** @type {!HTMLElement} */ (this.contentElement.createChild('div', 'animation-timeline-buffer'));
Kalon Hinds08dd3682020-01-10 23:51:25188 UI.ARIAUtils.markAsListBox(this._previewContainer);
189 UI.ARIAUtils.setAccessibleName(this._previewContainer, ls`Animation previews`);
Tim van der Lippe91d32562020-02-13 15:08:47190 this._popoverHelper =
191 new UI.PopoverHelper.PopoverHelper(this._previewContainer, this._getPopoverRequest.bind(this));
Blink Reformat4c46d092018-04-07 15:32:37192 this._popoverHelper.setDisableOnClick(true);
193 this._popoverHelper.setTimeout(0);
194 const emptyBufferHint = this.contentElement.createChild('div', 'animation-timeline-buffer-hint');
195 emptyBufferHint.textContent = ls`Listening for animations...`;
196 const container = this.contentElement.createChild('div', 'animation-timeline-header');
197 const controls = container.createChild('div', 'animation-controls');
Paul Lewiseae437e2020-10-19 14:09:45198 this._currentTime =
199 /** @type {!HTMLElement} */ (controls.createChild('div', 'animation-timeline-current-time monospace'));
Blink Reformat4c46d092018-04-07 15:32:37200
Tim van der Lippe91d32562020-02-13 15:08:47201 const toolbar = new UI.Toolbar.Toolbar('animation-controls-toolbar', controls);
202 this._controlButton = new UI.Toolbar.ToolbarToggle(ls`Replay timeline`, 'largeicon-replay-animation');
Paul Lewis752031f2020-01-09 14:14:23203 this._controlState = _ControlState.Replay;
Blink Reformat4c46d092018-04-07 15:32:37204 this._controlButton.setToggled(true);
Tim van der Lippe91d32562020-02-13 15:08:47205 this._controlButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, this._controlButtonToggle.bind(this));
Blink Reformat4c46d092018-04-07 15:32:37206 toolbar.appendToolbarItem(this._controlButton);
207
208 const gridHeader = container.createChild('div', 'animation-grid-header');
Tim van der Lippe91d32562020-02-13 15:08:47209 UI.UIUtils.installDragHandle(
Blink Reformat4c46d092018-04-07 15:32:37210 gridHeader, this._repositionScrubber.bind(this), this._scrubberDragMove.bind(this),
211 this._scrubberDragEnd.bind(this), 'text');
212 container.appendChild(this._createScrubber());
Paul Lewiseae437e2020-10-19 14:09:45213
214 if (this._timelineScrubberLine) {
215 UI.UIUtils.installDragHandle(
216 this._timelineScrubberLine, this._scrubberDragStart.bind(this), this._scrubberDragMove.bind(this),
217 this._scrubberDragEnd.bind(this), 'col-resize');
218 }
Blink Reformat4c46d092018-04-07 15:32:37219 this._currentTime.textContent = '';
220
221 return container;
222 }
223
224 /**
225 * @param {!Event} event
Kalon Hinds0fae2e72020-01-08 20:02:02226 */
227 _handlePlaybackRateControlKeyDown(event) {
Paul Lewiseae437e2020-10-19 14:09:45228 const keyboardEvent = /** @type {!KeyboardEvent} */ (event);
229 switch (keyboardEvent.key) {
Kalon Hinds0fae2e72020-01-08 20:02:02230 case 'ArrowLeft':
231 case 'ArrowUp':
232 this._focusNextPlaybackRateButton(event.target, /* focusPrevious */ true);
233 break;
234 case 'ArrowRight':
235 case 'ArrowDown':
236 this._focusNextPlaybackRateButton(event.target);
237 break;
238 }
239 }
240
241 /**
242 * @param {!EventTarget|null} target
243 * @param {boolean=} focusPrevious
244 */
245 _focusNextPlaybackRateButton(target, focusPrevious) {
Paul Lewiseae437e2020-10-19 14:09:45246 const button = /** @type {!HTMLElement} */ (target);
247 const currentIndex = this._playbackRateButtons.indexOf(button);
Kalon Hinds0fae2e72020-01-08 20:02:02248 const nextIndex = focusPrevious ? currentIndex - 1 : currentIndex + 1;
249 if (nextIndex < 0 || nextIndex >= this._playbackRateButtons.length) {
250 return;
251 }
252 const nextButton = this._playbackRateButtons[nextIndex];
253 nextButton.tabIndex = 0;
254 nextButton.focus();
Paul Lewiseae437e2020-10-19 14:09:45255 if (target) {
256 /** @type {!HTMLElement} */ (target).tabIndex = -1;
257 }
Kalon Hinds0fae2e72020-01-08 20:02:02258 }
259
260 /**
261 * @param {!Event} event
Tim van der Lippe49ff36b2020-10-08 12:11:00262 * @return {?UI.PopoverHelper.PopoverRequest}
Blink Reformat4c46d092018-04-07 15:32:37263 */
264 _getPopoverRequest(event) {
Paul Lewiseae437e2020-10-19 14:09:45265 const element = /** @type {!HTMLElement} */ (event.target);
266 if (!element || !element.isDescendant(this._previewContainer)) {
Blink Reformat4c46d092018-04-07 15:32:37267 return null;
Tim van der Lippe1d6e57a2019-09-30 11:55:34268 }
Blink Reformat4c46d092018-04-07 15:32:37269
270 return {
Paul Lewiseae437e2020-10-19 14:09:45271 box: element.boxInWindow(),
Blink Reformat4c46d092018-04-07 15:32:37272 show: popover => {
273 let animGroup;
Simon Zündf27be3d2020-02-11 14:46:27274 for (const [group, previewUI] of this._previewMap) {
275 if (previewUI.element === element.parentElement) {
Blink Reformat4c46d092018-04-07 15:32:37276 animGroup = group;
Tim van der Lippe1d6e57a2019-09-30 11:55:34277 }
Blink Reformat4c46d092018-04-07 15:32:37278 }
Paul Lewiseae437e2020-10-19 14:09:45279 console.assert(typeof animGroup !== 'undefined');
Michael Liao64b9ee32020-05-01 17:18:29280 if (!animGroup) {
281 return Promise.resolve(false);
282 }
Blink Reformat4c46d092018-04-07 15:32:37283 const screenshots = animGroup.screenshots();
Tim van der Lippe1d6e57a2019-09-30 11:55:34284 if (!screenshots.length) {
Blink Reformat4c46d092018-04-07 15:32:37285 return Promise.resolve(false);
Tim van der Lippe1d6e57a2019-09-30 11:55:34286 }
Blink Reformat4c46d092018-04-07 15:32:37287
Paul Lewiseae437e2020-10-19 14:09:45288 /** @type {function(*):void} */
Blink Reformat4c46d092018-04-07 15:32:37289 let fulfill;
Patrick Brossete65aaac2020-06-22 08:04:40290 const promise = new Promise(x => {
291 fulfill = x;
292 });
Tim van der Lippe1d6e57a2019-09-30 11:55:34293 if (!screenshots[0].complete) {
Blink Reformat4c46d092018-04-07 15:32:37294 screenshots[0].onload = onFirstScreenshotLoaded.bind(null, screenshots);
Tim van der Lippe1d6e57a2019-09-30 11:55:34295 } else {
Blink Reformat4c46d092018-04-07 15:32:37296 onFirstScreenshotLoaded(screenshots);
Tim van der Lippe1d6e57a2019-09-30 11:55:34297 }
Blink Reformat4c46d092018-04-07 15:32:37298 return promise;
299
300 /**
Paul Lewiseae437e2020-10-19 14:09:45301 * @param {!Array.<!HTMLImageElement>} screenshots
Blink Reformat4c46d092018-04-07 15:32:37302 */
303 function onFirstScreenshotLoaded(screenshots) {
Paul Lewis752031f2020-01-09 14:14:23304 new AnimationScreenshotPopover(screenshots).show(popover.contentElement);
Blink Reformat4c46d092018-04-07 15:32:37305 fulfill(true);
306 }
Paul Lewiseae437e2020-10-19 14:09:45307 },
308 hide: undefined
Blink Reformat4c46d092018-04-07 15:32:37309 };
310 }
311
312 _togglePauseAll() {
313 this._allPaused = !this._allPaused;
Paul Lewiseae437e2020-10-19 14:09:45314 if (this._pauseButton) {
315 this._pauseButton.setToggled(this._allPaused);
316 }
Blink Reformat4c46d092018-04-07 15:32:37317 this._setPlaybackRate(this._playbackRate);
Paul Lewiseae437e2020-10-19 14:09:45318 if (this._pauseButton) {
319 this._pauseButton.setTitle(this._allPaused ? ls`Resume all` : ls`Pause all`);
320 }
Blink Reformat4c46d092018-04-07 15:32:37321 }
322
323 /**
324 * @param {number} playbackRate
325 */
326 _setPlaybackRate(playbackRate) {
327 this._playbackRate = playbackRate;
Paul Lewisdaac1062020-03-05 14:37:10328 for (const animationModel of SDK.SDKModel.TargetManager.instance().models(AnimationModel)) {
Blink Reformat4c46d092018-04-07 15:32:37329 animationModel.setPlaybackRate(this._allPaused ? 0 : this._playbackRate);
Tim van der Lippe1d6e57a2019-09-30 11:55:34330 }
Blink Reformat4c46d092018-04-07 15:32:37331 Host.userMetrics.actionTaken(Host.UserMetrics.Action.AnimationsPlaybackRateChanged);
Tim van der Lippe1d6e57a2019-09-30 11:55:34332 if (this._scrubberPlayer) {
Blink Reformat4c46d092018-04-07 15:32:37333 this._scrubberPlayer.playbackRate = this._effectivePlaybackRate();
Tim van der Lippe1d6e57a2019-09-30 11:55:34334 }
Blink Reformat4c46d092018-04-07 15:32:37335
336 this._updatePlaybackControls();
337 }
338
339 _updatePlaybackControls() {
340 for (const button of this._playbackRateButtons) {
Paul Lewiseae437e2020-10-19 14:09:45341 const selected = this._playbackRate === playbackRates.get(button);
Blink Reformat4c46d092018-04-07 15:32:37342 button.classList.toggle('selected', selected);
Kalon Hinds0fae2e72020-01-08 20:02:02343 button.tabIndex = selected ? 0 : -1;
Blink Reformat4c46d092018-04-07 15:32:37344 }
345 }
346
347 _controlButtonToggle() {
Paul Lewis752031f2020-01-09 14:14:23348 if (this._controlState === _ControlState.Play) {
Blink Reformat4c46d092018-04-07 15:32:37349 this._togglePause(false);
Paul Lewis752031f2020-01-09 14:14:23350 } else if (this._controlState === _ControlState.Replay) {
Blink Reformat4c46d092018-04-07 15:32:37351 this._replay();
Tim van der Lippe1d6e57a2019-09-30 11:55:34352 } else {
Blink Reformat4c46d092018-04-07 15:32:37353 this._togglePause(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34354 }
Blink Reformat4c46d092018-04-07 15:32:37355 }
356
357 _updateControlButton() {
Paul Lewiseae437e2020-10-19 14:09:45358 if (!this._controlButton) {
359 return;
360 }
361
Blink Reformat4c46d092018-04-07 15:32:37362 this._controlButton.setEnabled(!!this._selectedGroup);
363 if (this._selectedGroup && this._selectedGroup.paused()) {
Paul Lewis752031f2020-01-09 14:14:23364 this._controlState = _ControlState.Play;
Blink Reformat4c46d092018-04-07 15:32:37365 this._controlButton.setToggled(true);
366 this._controlButton.setTitle(ls`Play timeline`);
367 this._controlButton.setGlyph('largeicon-play-animation');
Paul Lewiseae437e2020-10-19 14:09:45368 } else if (
369 !this._scrubberPlayer || !this._scrubberPlayer.currentTime ||
370 this._scrubberPlayer.currentTime >= this.duration()) {
Paul Lewis752031f2020-01-09 14:14:23371 this._controlState = _ControlState.Replay;
Blink Reformat4c46d092018-04-07 15:32:37372 this._controlButton.setToggled(true);
373 this._controlButton.setTitle(ls`Replay timeline`);
374 this._controlButton.setGlyph('largeicon-replay-animation');
375 } else {
Paul Lewis752031f2020-01-09 14:14:23376 this._controlState = _ControlState.Pause;
Blink Reformat4c46d092018-04-07 15:32:37377 this._controlButton.setToggled(false);
378 this._controlButton.setTitle(ls`Pause timeline`);
379 this._controlButton.setGlyph('largeicon-pause-animation');
380 }
381 }
382
383 /**
384 * @return {number}
385 */
386 _effectivePlaybackRate() {
387 return (this._allPaused || (this._selectedGroup && this._selectedGroup.paused())) ? 0 : this._playbackRate;
388 }
389
390 /**
391 * @param {boolean} pause
392 */
393 _togglePause(pause) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34394 if (this._scrubberPlayer) {
Blink Reformat4c46d092018-04-07 15:32:37395 this._scrubberPlayer.playbackRate = this._effectivePlaybackRate();
Tim van der Lippe1d6e57a2019-09-30 11:55:34396 }
Paul Lewiseae437e2020-10-19 14:09:45397 if (this._selectedGroup) {
398 this._selectedGroup.togglePause(pause);
399 const preview = this._previewMap.get(this._selectedGroup);
400 if (preview) {
401 preview.element.classList.toggle('paused', pause);
402 }
403 }
Blink Reformat4c46d092018-04-07 15:32:37404 this._updateControlButton();
405 }
406
407 _replay() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34408 if (!this._selectedGroup) {
Blink Reformat4c46d092018-04-07 15:32:37409 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34410 }
Blink Reformat4c46d092018-04-07 15:32:37411 this._selectedGroup.seekTo(0);
412 this._animateTime(0);
413 this._updateControlButton();
414 }
415
416 /**
417 * @return {number}
418 */
419 duration() {
420 return this._duration;
421 }
422
423 /**
424 * @param {number} duration
425 */
426 setDuration(duration) {
427 this._duration = duration;
428 this.scheduleRedraw();
429 }
430
431 _clearTimeline() {
432 this._uiAnimations = [];
433 this._nodesMap.clear();
434 this._animationsMap.clear();
435 this._animationsContainer.removeChildren();
436 this._duration = this._defaultDuration;
437 this._timelineScrubber.classList.add('hidden');
Paul Lewiseae437e2020-10-19 14:09:45438 this._selectedGroup = null;
Tim van der Lippe1d6e57a2019-09-30 11:55:34439 if (this._scrubberPlayer) {
Blink Reformat4c46d092018-04-07 15:32:37440 this._scrubberPlayer.cancel();
Tim van der Lippe1d6e57a2019-09-30 11:55:34441 }
Blink Reformat4c46d092018-04-07 15:32:37442 delete this._scrubberPlayer;
443 this._currentTime.textContent = '';
444 this._updateControlButton();
445 }
446
447 _reset() {
448 this._clearTimeline();
Tim van der Lippe1d6e57a2019-09-30 11:55:34449 if (this._allPaused) {
Blink Reformat4c46d092018-04-07 15:32:37450 this._togglePauseAll();
Tim van der Lippe1d6e57a2019-09-30 11:55:34451 } else {
Blink Reformat4c46d092018-04-07 15:32:37452 this._setPlaybackRate(this._playbackRate);
Tim van der Lippe1d6e57a2019-09-30 11:55:34453 }
Blink Reformat4c46d092018-04-07 15:32:37454
Tim van der Lippe1d6e57a2019-09-30 11:55:34455 for (const group of this._groupBuffer) {
Blink Reformat4c46d092018-04-07 15:32:37456 group.release();
Tim van der Lippe1d6e57a2019-09-30 11:55:34457 }
Blink Reformat4c46d092018-04-07 15:32:37458 this._groupBuffer = [];
459 this._previewMap.clear();
460 this._previewContainer.removeChildren();
461 this._popoverHelper.hidePopover();
462 this._renderGrid();
463 }
464
465 /**
Tim van der Lippec02a97c2020-02-14 14:39:27466 * @param {!Common.EventTarget.EventTargetEvent} event
Blink Reformat4c46d092018-04-07 15:32:37467 */
468 _animationGroupStarted(event) {
Paul Lewis752031f2020-01-09 14:14:23469 this._addAnimationGroup(/** @type {!AnimationGroup} */ (event.data));
Blink Reformat4c46d092018-04-07 15:32:37470 }
471
472 /**
Paul Lewis752031f2020-01-09 14:14:23473 * @param {!AnimationGroup} group
Blink Reformat4c46d092018-04-07 15:32:37474 */
475 _addAnimationGroup(group) {
476 /**
Paul Lewis752031f2020-01-09 14:14:23477 * @param {!AnimationGroup} left
Paul Lewiseae437e2020-10-19 14:09:45478 * @param {!AnimationGroup} right
Blink Reformat4c46d092018-04-07 15:32:37479 */
480 function startTimeComparator(left, right) {
Paul Lewiseae437e2020-10-19 14:09:45481 if (left.startTime() === right.startTime()) {
482 return 0;
483 }
484 return left.startTime() > right.startTime() ? 1 : -1;
Blink Reformat4c46d092018-04-07 15:32:37485 }
486
Paul Lewiseae437e2020-10-19 14:09:45487 const previewGroup = this._previewMap.get(group);
488 if (previewGroup) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34489 if (this._selectedGroup === group) {
Blink Reformat4c46d092018-04-07 15:32:37490 this._syncScrubber();
Tim van der Lippe1d6e57a2019-09-30 11:55:34491 } else {
Paul Lewiseae437e2020-10-19 14:09:45492 previewGroup.replay();
Tim van der Lippe1d6e57a2019-09-30 11:55:34493 }
Blink Reformat4c46d092018-04-07 15:32:37494 return;
495 }
496 this._groupBuffer.sort(startTimeComparator);
497 // Discard oldest groups from buffer if necessary
498 const groupsToDiscard = [];
499 const bufferSize = this.width() / 50;
500 while (this._groupBuffer.length > bufferSize) {
501 const toDiscard = this._groupBuffer.splice(this._groupBuffer[0] === this._selectedGroup ? 1 : 0, 1);
502 groupsToDiscard.push(toDiscard[0]);
503 }
504 for (const g of groupsToDiscard) {
Paul Lewiseae437e2020-10-19 14:09:45505 const discardGroup = this._previewMap.get(g);
506 if (!discardGroup) {
507 continue;
508 }
509 discardGroup.element.remove();
Blink Reformat4c46d092018-04-07 15:32:37510 this._previewMap.delete(g);
511 g.release();
512 }
513 // Generate preview
Paul Lewis752031f2020-01-09 14:14:23514 const preview = new AnimationGroupPreviewUI(group);
Blink Reformat4c46d092018-04-07 15:32:37515 this._groupBuffer.push(group);
516 this._previewMap.set(group, preview);
517 this._previewContainer.appendChild(preview.element);
518 preview.removeButton().addEventListener('click', this._removeAnimationGroup.bind(this, group));
519 preview.element.addEventListener('click', this._selectAnimationGroup.bind(this, group));
Kalon Hinds0fae2e72020-01-08 20:02:02520 preview.element.addEventListener('keydown', this._handleAnimationGroupKeyDown.bind(this, group));
Kalon Hinds08dd3682020-01-10 23:51:25521 UI.ARIAUtils.setAccessibleName(preview.element, ls`Animation Preview ${this._groupBuffer.indexOf(group) + 1}`);
522 UI.ARIAUtils.markAsOption(preview.element);
523
Kalon Hinds0fae2e72020-01-08 20:02:02524 if (this._previewMap.size === 1) {
Paul Lewiseae437e2020-10-19 14:09:45525 const preview = this._previewMap.get(this._groupBuffer[0]);
526 if (preview) {
527 preview.element.tabIndex = 0;
528 }
Kalon Hinds0fae2e72020-01-08 20:02:02529 }
530 }
531
532 /**
Paul Lewis752031f2020-01-09 14:14:23533 * @param {!AnimationGroup} group
Paul Lewiseae437e2020-10-19 14:09:45534 * @param {!KeyboardEvent} event
Kalon Hinds0fae2e72020-01-08 20:02:02535 */
536 _handleAnimationGroupKeyDown(group, event) {
537 switch (event.key) {
538 case ' ':
539 case 'Enter':
540 this._selectAnimationGroup(group);
541 break;
542 case 'Backspace':
543 case 'Delete':
544 this._removeAnimationGroup(group, event);
545 break;
546 case 'ArrowLeft':
547 case 'ArrowUp':
548 this._focusNextGroup(group, /* target */ event.target, /* focusPrevious */ true);
549 break;
550 case 'ArrowRight':
551 case 'ArrowDown':
552 this._focusNextGroup(group, /* target */ event.target);
553 }
554 }
555
556 /**
Paul Lewis752031f2020-01-09 14:14:23557 * @param {!AnimationGroup} group
Kalon Hinds0fae2e72020-01-08 20:02:02558 * @param {!EventTarget|null} target
559 * @param {boolean=} focusPrevious
560 */
561 _focusNextGroup(group, target, focusPrevious) {
562 const currentGroupIndex = this._groupBuffer.indexOf(group);
563 const nextIndex = focusPrevious ? currentGroupIndex - 1 : currentGroupIndex + 1;
564 if (nextIndex < 0 || nextIndex >= this._groupBuffer.length) {
565 return;
566 }
567 const preview = this._previewMap.get(this._groupBuffer[nextIndex]);
Paul Lewiseae437e2020-10-19 14:09:45568 if (preview) {
569 preview.element.tabIndex = 0;
570 preview.element.focus();
571 }
572
573 if (target) {
574 /** @type {!HTMLElement} */ (target).tabIndex = -1;
575 }
Blink Reformat4c46d092018-04-07 15:32:37576 }
577
578 /**
Paul Lewis752031f2020-01-09 14:14:23579 * @param {!AnimationGroup} group
Blink Reformat4c46d092018-04-07 15:32:37580 * @param {!Event} event
581 */
582 _removeAnimationGroup(group, event) {
Kalon Hinds0fae2e72020-01-08 20:02:02583 const currentGroupIndex = this._groupBuffer.indexOf(group);
584
Simon Zünd684ebae2020-03-09 11:44:16585 Platform.ArrayUtilities.removeElement(this._groupBuffer, group);
Paul Lewiseae437e2020-10-19 14:09:45586 const previewGroup = this._previewMap.get(group);
587 if (previewGroup) {
588 previewGroup.element.remove();
589 }
Blink Reformat4c46d092018-04-07 15:32:37590 this._previewMap.delete(group);
591 group.release();
592 event.consume(true);
593
594 if (this._selectedGroup === group) {
595 this._clearTimeline();
596 this._renderGrid();
597 }
Kalon Hinds0fae2e72020-01-08 20:02:02598
599 const groupLength = this._groupBuffer.length;
600 if (groupLength === 0) {
Paul Lewiseae437e2020-10-19 14:09:45601 /** @type {!HTMLElement} */ (this._clearButton.element).focus();
Kalon Hinds0fae2e72020-01-08 20:02:02602 return;
603 }
604 const nextGroup = currentGroupIndex >= this._groupBuffer.length ?
605 this._previewMap.get(this._groupBuffer[this._groupBuffer.length - 1]) :
606 this._previewMap.get(this._groupBuffer[currentGroupIndex]);
Paul Lewiseae437e2020-10-19 14:09:45607
608 if (nextGroup) {
609 nextGroup.element.tabIndex = 0;
610 nextGroup.element.focus();
611 }
Blink Reformat4c46d092018-04-07 15:32:37612 }
613
614 /**
Paul Lewis752031f2020-01-09 14:14:23615 * @param {!AnimationGroup} group
Blink Reformat4c46d092018-04-07 15:32:37616 */
617 _selectAnimationGroup(group) {
618 /**
Paul Lewis752031f2020-01-09 14:14:23619 * @param {!AnimationGroupPreviewUI} ui
620 * @param {!AnimationGroup} group
621 * @this {!AnimationTimeline}
Blink Reformat4c46d092018-04-07 15:32:37622 */
623 function applySelectionClass(ui, group) {
624 ui.element.classList.toggle('selected', this._selectedGroup === group);
625 }
626
627 if (this._selectedGroup === group) {
628 this._togglePause(false);
629 this._replay();
630 return;
631 }
632 this._clearTimeline();
633 this._selectedGroup = group;
634 this._previewMap.forEach(applySelectionClass, this);
635 this.setDuration(Math.max(500, group.finiteDuration() + 100));
Tim van der Lippe1d6e57a2019-09-30 11:55:34636 for (const anim of group.animations()) {
Blink Reformat4c46d092018-04-07 15:32:37637 this._addAnimation(anim);
Tim van der Lippe1d6e57a2019-09-30 11:55:34638 }
Blink Reformat4c46d092018-04-07 15:32:37639 this.scheduleRedraw();
640 this._timelineScrubber.classList.remove('hidden');
641 this._togglePause(false);
642 this._replay();
643 }
644
645 /**
Paul Lewis752031f2020-01-09 14:14:23646 * @param {!AnimationImpl} animation
Blink Reformat4c46d092018-04-07 15:32:37647 */
648 _addAnimation(animation) {
649 /**
Tim van der Lippe91d32562020-02-13 15:08:47650 * @param {?SDK.DOMModel.DOMNode} node
Paul Lewis752031f2020-01-09 14:14:23651 * @this {AnimationTimeline}
Blink Reformat4c46d092018-04-07 15:32:37652 */
653 function nodeResolved(node) {
Blink Reformat4c46d092018-04-07 15:32:37654 uiAnimation.setNode(node);
Paul Lewiseae437e2020-10-19 14:09:45655 if (node && nodeUI) {
656 nodeUI.nodeResolved(node);
657 nodeUIsByNode.set(node, nodeUI);
Tim van der Lippe1d6e57a2019-09-30 11:55:34658 }
Blink Reformat4c46d092018-04-07 15:32:37659 }
660
661 let nodeUI = this._nodesMap.get(animation.source().backendNodeId());
662 if (!nodeUI) {
Paul Lewis752031f2020-01-09 14:14:23663 nodeUI = new NodeUI(animation.source());
Blink Reformat4c46d092018-04-07 15:32:37664 this._animationsContainer.appendChild(nodeUI.element);
665 this._nodesMap.set(animation.source().backendNodeId(), nodeUI);
666 }
667 const nodeRow = nodeUI.createNewRow();
Paul Lewis752031f2020-01-09 14:14:23668 const uiAnimation = new AnimationUI(animation, this, nodeRow);
Blink Reformat4c46d092018-04-07 15:32:37669 animation.source().deferredNode().resolve(nodeResolved.bind(this));
670 this._uiAnimations.push(uiAnimation);
671 this._animationsMap.set(animation.id(), animation);
672 }
673
674 /**
Tim van der Lippec02a97c2020-02-14 14:39:27675 * @param {!Common.EventTarget.EventTargetEvent} event
Blink Reformat4c46d092018-04-07 15:32:37676 */
677 _nodeRemoved(event) {
678 const node = event.data.node;
Paul Lewiseae437e2020-10-19 14:09:45679 const nodeUI = nodeUIsByNode.get(node);
680 if (nodeUI) {
681 nodeUI.nodeRemoved();
Tim van der Lippe1d6e57a2019-09-30 11:55:34682 }
Blink Reformat4c46d092018-04-07 15:32:37683 }
684
685 _renderGrid() {
686 /** @const */ const gridSize = 250;
Paul Lewiseae437e2020-10-19 14:09:45687 this._grid.setAttribute('width', (this.width() + 10).toString());
688 this._grid.setAttribute('height', ((this._cachedTimelineHeight || 0) + 30).toString());
Blink Reformat4c46d092018-04-07 15:32:37689 this._grid.setAttribute('shape-rendering', 'crispEdges');
690 this._grid.removeChildren();
691 let lastDraw = undefined;
692 for (let time = 0; time < this.duration(); time += gridSize) {
Paul Lewis44e37572020-10-28 15:57:26693 const line = UI.UIUtils.createSVGChild(this._grid, 'rect', 'animation-timeline-grid-line');
Paul Lewiseae437e2020-10-19 14:09:45694 line.setAttribute('x', (time * this.pixelMsRatio() + 10).toString());
695 line.setAttribute('y', '23');
Blink Reformat4c46d092018-04-07 15:32:37696 line.setAttribute('height', '100%');
Paul Lewiseae437e2020-10-19 14:09:45697 line.setAttribute('width', '1');
Blink Reformat4c46d092018-04-07 15:32:37698 }
699 for (let time = 0; time < this.duration(); time += gridSize) {
700 const gridWidth = time * this.pixelMsRatio();
701 if (lastDraw === undefined || gridWidth - lastDraw > 50) {
702 lastDraw = gridWidth;
Paul Lewis44e37572020-10-28 15:57:26703 const label = UI.UIUtils.createSVGChild(this._grid, 'text', 'animation-timeline-grid-label');
Blink Reformat4c46d092018-04-07 15:32:37704 label.textContent = Number.millisToString(time);
Paul Lewiseae437e2020-10-19 14:09:45705 label.setAttribute('x', (gridWidth + 10).toString());
706 label.setAttribute('y', '16');
Blink Reformat4c46d092018-04-07 15:32:37707 }
708 }
709 }
710
711 scheduleRedraw() {
Paul Lewiseae437e2020-10-19 14:09:45712 /** @type {!Array<!AnimationUI>} */
Blink Reformat4c46d092018-04-07 15:32:37713 this._renderQueue = [];
Tim van der Lippe1d6e57a2019-09-30 11:55:34714 for (const ui of this._uiAnimations) {
Blink Reformat4c46d092018-04-07 15:32:37715 this._renderQueue.push(ui);
Tim van der Lippe1d6e57a2019-09-30 11:55:34716 }
717 if (this._redrawing) {
Blink Reformat4c46d092018-04-07 15:32:37718 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34719 }
Blink Reformat4c46d092018-04-07 15:32:37720 this._redrawing = true;
721 this._renderGrid();
722 this._animationsContainer.window().requestAnimationFrame(this._render.bind(this));
723 }
724
725 /**
726 * @param {number=} timestamp
727 */
728 _render(timestamp) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34729 while (this._renderQueue.length && (!timestamp || window.performance.now() - timestamp < 50)) {
Paul Lewiseae437e2020-10-19 14:09:45730 const animationUI = this._renderQueue.shift();
731 if (animationUI) {
732 animationUI.redraw();
733 }
Tim van der Lippe1d6e57a2019-09-30 11:55:34734 }
735 if (this._renderQueue.length) {
Blink Reformat4c46d092018-04-07 15:32:37736 this._animationsContainer.window().requestAnimationFrame(this._render.bind(this));
Tim van der Lippe1d6e57a2019-09-30 11:55:34737 } else {
Blink Reformat4c46d092018-04-07 15:32:37738 delete this._redrawing;
Tim van der Lippe1d6e57a2019-09-30 11:55:34739 }
Blink Reformat4c46d092018-04-07 15:32:37740 }
741
742 /**
743 * @override
744 */
745 onResize() {
746 this._cachedTimelineWidth = Math.max(0, this._animationsContainer.offsetWidth - this._timelineControlsWidth) || 0;
747 this._cachedTimelineHeight = this._animationsContainer.offsetHeight;
748 this.scheduleRedraw();
Tim van der Lippe1d6e57a2019-09-30 11:55:34749 if (this._scrubberPlayer) {
Blink Reformat4c46d092018-04-07 15:32:37750 this._syncScrubber();
Tim van der Lippe1d6e57a2019-09-30 11:55:34751 }
Blink Reformat4c46d092018-04-07 15:32:37752 delete this._gridOffsetLeft;
753 }
754
755 /**
756 * @return {number}
757 */
758 width() {
759 return this._cachedTimelineWidth || 0;
760 }
761
762 /**
Paul Lewis752031f2020-01-09 14:14:23763 * @param {!AnimationImpl} animation
Blink Reformat4c46d092018-04-07 15:32:37764 * @return {boolean}
765 */
766 _resizeWindow(animation) {
767 let resized = false;
768
769 // This shows at most 3 iterations
770 const duration = animation.source().duration() * Math.min(2, animation.source().iterations());
771 const requiredDuration = animation.source().delay() + duration + animation.source().endDelay();
772 if (requiredDuration > this._duration) {
773 resized = true;
774 this._duration = requiredDuration + 200;
775 }
776 return resized;
777 }
778
779 _syncScrubber() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34780 if (!this._selectedGroup) {
Blink Reformat4c46d092018-04-07 15:32:37781 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34782 }
Blink Reformat4c46d092018-04-07 15:32:37783 this._selectedGroup.currentTimePromise()
784 .then(this._animateTime.bind(this))
785 .then(this._updateControlButton.bind(this));
786 }
787
788 /**
789 * @param {number} currentTime
790 */
791 _animateTime(currentTime) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34792 if (this._scrubberPlayer) {
Blink Reformat4c46d092018-04-07 15:32:37793 this._scrubberPlayer.cancel();
Tim van der Lippe1d6e57a2019-09-30 11:55:34794 }
Blink Reformat4c46d092018-04-07 15:32:37795
796 this._scrubberPlayer = this._timelineScrubber.animate(
797 [{transform: 'translateX(0px)'}, {transform: 'translateX(' + this.width() + 'px)'}],
798 {duration: this.duration(), fill: 'forwards'});
799 this._scrubberPlayer.playbackRate = this._effectivePlaybackRate();
800 this._scrubberPlayer.onfinish = this._updateControlButton.bind(this);
801 this._scrubberPlayer.currentTime = currentTime;
802 this.element.window().requestAnimationFrame(this._updateScrubber.bind(this));
803 }
804
805 /**
806 * @return {number}
807 */
808 pixelMsRatio() {
809 return this.width() / this.duration() || 0;
810 }
811
812 /**
813 * @param {number} timestamp
814 */
815 _updateScrubber(timestamp) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34816 if (!this._scrubberPlayer) {
Blink Reformat4c46d092018-04-07 15:32:37817 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34818 }
Paul Lewiseae437e2020-10-19 14:09:45819 this._currentTime.textContent = Number.millisToString(this._scrubberPlayer.currentTime || 0);
820 if (this._scrubberPlayer.playState.toString() === 'pending' || this._scrubberPlayer.playState === 'running') {
Blink Reformat4c46d092018-04-07 15:32:37821 this.element.window().requestAnimationFrame(this._updateScrubber.bind(this));
Tim van der Lippe1d6e57a2019-09-30 11:55:34822 } else if (this._scrubberPlayer.playState === 'finished') {
Blink Reformat4c46d092018-04-07 15:32:37823 this._currentTime.textContent = '';
Tim van der Lippe1d6e57a2019-09-30 11:55:34824 }
Blink Reformat4c46d092018-04-07 15:32:37825 }
826
827 /**
828 * @param {!Event} event
829 * @return {boolean}
830 */
831 _repositionScrubber(event) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34832 if (!this._selectedGroup) {
Blink Reformat4c46d092018-04-07 15:32:37833 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34834 }
Blink Reformat4c46d092018-04-07 15:32:37835
836 // Seek to current mouse position.
Tim van der Lippe1d6e57a2019-09-30 11:55:34837 if (!this._gridOffsetLeft) {
Blink Reformat4c46d092018-04-07 15:32:37838 this._gridOffsetLeft = this._grid.totalOffsetLeft() + 10;
Tim van der Lippe1d6e57a2019-09-30 11:55:34839 }
Paul Lewiseae437e2020-10-19 14:09:45840 const {x} = /** @type {*} */ (event);
841 const seekTime = Math.max(0, x - this._gridOffsetLeft) / this.pixelMsRatio();
Blink Reformat4c46d092018-04-07 15:32:37842 this._selectedGroup.seekTo(seekTime);
843 this._togglePause(true);
844 this._animateTime(seekTime);
845
846 // Interface with scrubber drag.
847 this._originalScrubberTime = seekTime;
Paul Lewiseae437e2020-10-19 14:09:45848 this._originalMousePosition = x;
Blink Reformat4c46d092018-04-07 15:32:37849 return true;
850 }
851
852 /**
853 * @param {!Event} event
854 * @return {boolean}
855 */
856 _scrubberDragStart(event) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34857 if (!this._scrubberPlayer || !this._selectedGroup) {
Blink Reformat4c46d092018-04-07 15:32:37858 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34859 }
Blink Reformat4c46d092018-04-07 15:32:37860
861 this._originalScrubberTime = this._scrubberPlayer.currentTime;
862 this._timelineScrubber.classList.remove('animation-timeline-end');
863 this._scrubberPlayer.pause();
Paul Lewiseae437e2020-10-19 14:09:45864 const {x} = /** @type {*} */ (event);
865 this._originalMousePosition = x;
Blink Reformat4c46d092018-04-07 15:32:37866
867 this._togglePause(true);
868 return true;
869 }
870
871 /**
872 * @param {!Event} event
873 */
874 _scrubberDragMove(event) {
Paul Lewiseae437e2020-10-19 14:09:45875 const {x} = /** @type {*} */ (event);
876 const delta = x - this._originalMousePosition;
Blink Reformat4c46d092018-04-07 15:32:37877 const currentTime =
Paul Lewiseae437e2020-10-19 14:09:45878 Math.max(0, Math.min((this._originalScrubberTime || 0) + delta / this.pixelMsRatio(), this.duration()));
879 if (this._scrubberPlayer) {
880 this._scrubberPlayer.currentTime = currentTime;
881 }
Blink Reformat4c46d092018-04-07 15:32:37882 this._currentTime.textContent = Number.millisToString(Math.round(currentTime));
Paul Lewiseae437e2020-10-19 14:09:45883
884 if (this._selectedGroup) {
885 this._selectedGroup.seekTo(currentTime);
886 }
Blink Reformat4c46d092018-04-07 15:32:37887 }
888
889 /**
890 * @param {!Event} event
891 */
892 _scrubberDragEnd(event) {
Paul Lewiseae437e2020-10-19 14:09:45893 if (this._scrubberPlayer) {
894 const currentTime = Math.max(0, this._scrubberPlayer.currentTime || 0);
895 this._scrubberPlayer.play();
896 this._scrubberPlayer.currentTime = currentTime;
897 }
Blink Reformat4c46d092018-04-07 15:32:37898 this._currentTime.window().requestAnimationFrame(this._updateScrubber.bind(this));
899 }
Paul Lewis4962e2a2019-11-19 14:20:16900}
Blink Reformat4c46d092018-04-07 15:32:37901
Paul Lewis4962e2a2019-11-19 14:20:16902export const GlobalPlaybackRates = [1, 0.25, 0.1];
Blink Reformat4c46d092018-04-07 15:32:37903
904/** @enum {string} */
Paul Lewis4962e2a2019-11-19 14:20:16905export const _ControlState = {
Blink Reformat4c46d092018-04-07 15:32:37906 Play: 'play-outline',
907 Replay: 'replay-outline',
908 Pause: 'pause-outline'
909};
910
911/**
912 * @unrestricted
913 */
Paul Lewis4962e2a2019-11-19 14:20:16914export class NodeUI {
Blink Reformat4c46d092018-04-07 15:32:37915 /**
Paul Lewis752031f2020-01-09 14:14:23916 * @param {!AnimationEffect} animationEffect
Blink Reformat4c46d092018-04-07 15:32:37917 */
918 constructor(animationEffect) {
Tim van der Lippef49e2322020-05-01 15:03:09919 this.element = document.createElement('div');
920 this.element.classList.add('animation-node-row');
Blink Reformat4c46d092018-04-07 15:32:37921 this._description = this.element.createChild('div', 'animation-node-description');
Brandon Goddard99420bb2019-12-17 23:30:29922 this._description.tabIndex = 0;
Blink Reformat4c46d092018-04-07 15:32:37923 this._timelineElement = this.element.createChild('div', 'animation-node-timeline');
Kalon Hinds08dd3682020-01-10 23:51:25924 UI.ARIAUtils.markAsApplication(this._timelineElement);
Blink Reformat4c46d092018-04-07 15:32:37925 }
926
927 /**
Tim van der Lippe91d32562020-02-13 15:08:47928 * @param {?SDK.DOMModel.DOMNode} node
Blink Reformat4c46d092018-04-07 15:32:37929 */
Jeff Fisher3f5f19c2019-08-28 19:10:02930 nodeResolved(node) {
Blink Reformat4c46d092018-04-07 15:32:37931 if (!node) {
Sigurd Schneider23c52972020-10-13 09:31:14932 UI.UIUtils.createTextChild(this._description, '<node>');
Blink Reformat4c46d092018-04-07 15:32:37933 return;
934 }
935 this._node = node;
936 this._nodeChanged();
Brandon Goddard74711eb2020-10-22 15:17:31937 Common.Linkifier.Linkifier.linkify(node).then(link => this._description.appendChild(link));
Tim van der Lippe1d6e57a2019-09-30 11:55:34938 if (!node.ownerDocument) {
Blink Reformat4c46d092018-04-07 15:32:37939 this.nodeRemoved();
Tim van der Lippe1d6e57a2019-09-30 11:55:34940 }
Blink Reformat4c46d092018-04-07 15:32:37941 }
942
943 /**
944 * @return {!Element}
945 */
946 createNewRow() {
947 return this._timelineElement.createChild('div', 'animation-timeline-row');
948 }
949
950 nodeRemoved() {
951 this.element.classList.add('animation-node-removed');
952 this._node = null;
953 }
954
955 _nodeChanged() {
Paul Lewiseae437e2020-10-19 14:09:45956 let animationNodeSelected = false;
957 if (this._node) {
958 animationNodeSelected = (this._node === UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode));
959 }
960
961 this.element.classList.toggle('animation-node-selected', animationNodeSelected);
Blink Reformat4c46d092018-04-07 15:32:37962 }
Paul Lewis4962e2a2019-11-19 14:20:16963}
Blink Reformat4c46d092018-04-07 15:32:37964
965/**
966 * @unrestricted
967 */
Paul Lewis4962e2a2019-11-19 14:20:16968export class StepTimingFunction {
Blink Reformat4c46d092018-04-07 15:32:37969 /**
970 * @param {number} steps
971 * @param {string} stepAtPosition
972 */
973 constructor(steps, stepAtPosition) {
974 this.steps = steps;
975 this.stepAtPosition = stepAtPosition;
976 }
977
978 /**
979 * @param {string} text
Paul Lewis752031f2020-01-09 14:14:23980 * @return {?StepTimingFunction}
Blink Reformat4c46d092018-04-07 15:32:37981 */
982 static parse(text) {
983 let match = text.match(/^steps\((\d+), (start|middle)\)$/);
Tim van der Lippe1d6e57a2019-09-30 11:55:34984 if (match) {
Paul Lewis752031f2020-01-09 14:14:23985 return new StepTimingFunction(parseInt(match[1], 10), match[2]);
Tim van der Lippe1d6e57a2019-09-30 11:55:34986 }
Blink Reformat4c46d092018-04-07 15:32:37987 match = text.match(/^steps\((\d+)\)$/);
Tim van der Lippe1d6e57a2019-09-30 11:55:34988 if (match) {
Paul Lewis752031f2020-01-09 14:14:23989 return new StepTimingFunction(parseInt(match[1], 10), 'end');
Tim van der Lippe1d6e57a2019-09-30 11:55:34990 }
Blink Reformat4c46d092018-04-07 15:32:37991 return null;
992 }
Paul Lewis4962e2a2019-11-19 14:20:16993}