OLD | NEW |
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved. | 1 // 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * @constructor | 6 * @constructor |
7 * @extends {WebInspector.VBox} | 7 * @extends {WebInspector.VBox} |
8 * @implements {WebInspector.TargetManager.Observer} | 8 * @implements {WebInspector.TargetManager.Observer} |
9 */ | 9 */ |
10 WebInspector.AnimationTimeline = function() | 10 WebInspector.AnimationTimeline = function() |
(...skipping 13 matching lines...) Expand all Loading... |
24 | 24 |
25 this._emptyTimelineMessage = this._animationsContainer.createChild("div", "a
nimation-timeline-empty-message"); | 25 this._emptyTimelineMessage = this._animationsContainer.createChild("div", "a
nimation-timeline-empty-message"); |
26 var message = this._emptyTimelineMessage.createChild("div"); | 26 var message = this._emptyTimelineMessage.createChild("div"); |
27 message.textContent = WebInspector.UIString("Trigger animations on the page
to view and tweak them on the animation timeline."); | 27 message.textContent = WebInspector.UIString("Trigger animations on the page
to view and tweak them on the animation timeline."); |
28 | 28 |
29 this._duration = this._defaultDuration(); | 29 this._duration = this._defaultDuration(); |
30 this._scrubberRadius = 30; | 30 this._scrubberRadius = 30; |
31 this._timelineControlsWidth = 230; | 31 this._timelineControlsWidth = 230; |
32 /** @type {!Map.<!DOMAgent.BackendNodeId, !WebInspector.AnimationTimeline.No
deUI>} */ | 32 /** @type {!Map.<!DOMAgent.BackendNodeId, !WebInspector.AnimationTimeline.No
deUI>} */ |
33 this._nodesMap = new Map(); | 33 this._nodesMap = new Map(); |
| 34 this._groupBuffer = []; |
| 35 this._groupBufferSize = 8; |
| 36 /** @type {!Map.<!WebInspector.AnimationModel.AnimationGroup, !WebInspector.
AnimationGroupPreviewUI>} */ |
| 37 this._previewMap = new Map(); |
34 this._symbol = Symbol("animationTimeline"); | 38 this._symbol = Symbol("animationTimeline"); |
35 /** @type {!Map.<string, !WebInspector.AnimationModel.Animation>} */ | 39 /** @type {!Map.<string, !WebInspector.AnimationModel.Animation>} */ |
36 this._animationsMap = new Map(); | 40 this._animationsMap = new Map(); |
37 WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel,
WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._mainFrameNav
igated, this); | 41 WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel,
WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._mainFrameNav
igated, this); |
38 WebInspector.targetManager.addModelListener(WebInspector.DOMModel, WebInspec
tor.DOMModel.Events.NodeRemoved, this._nodeRemoved, this); | 42 WebInspector.targetManager.addModelListener(WebInspector.DOMModel, WebInspec
tor.DOMModel.Events.NodeRemoved, this._nodeRemoved, this); |
39 | 43 |
40 WebInspector.targetManager.observeTargets(this, WebInspector.Target.Type.Pag
e); | 44 WebInspector.targetManager.observeTargets(this, WebInspector.Target.Type.Pag
e); |
41 } | 45 } |
42 | 46 |
43 WebInspector.AnimationTimeline.GlobalPlaybackRates = [0.1, 0.25, 0.5, 1.0]; | 47 WebInspector.AnimationTimeline.GlobalPlaybackRates = [0.1, 0.25, 0.5, 1.0]; |
(...skipping 30 matching lines...) Expand all Loading... |
74 this._removeEventListeners(target); | 78 this._removeEventListeners(target); |
75 }, | 79 }, |
76 | 80 |
77 /** | 81 /** |
78 * @param {!WebInspector.Target} target | 82 * @param {!WebInspector.Target} target |
79 */ | 83 */ |
80 _addEventListeners: function(target) | 84 _addEventListeners: function(target) |
81 { | 85 { |
82 var animationModel = WebInspector.AnimationModel.fromTarget(target); | 86 var animationModel = WebInspector.AnimationModel.fromTarget(target); |
83 animationModel.ensureEnabled(); | 87 animationModel.ensureEnabled(); |
84 animationModel.addEventListener(WebInspector.AnimationModel.Events.Anima
tionCreated, this._animationCreated, this); | 88 animationModel.addEventListener(WebInspector.AnimationModel.Events.Anima
tionGroupStarted, this._animationGroupStarted, this); |
85 animationModel.addEventListener(WebInspector.AnimationModel.Events.Anima
tionCanceled, this._animationCanceled, this); | 89 animationModel.addEventListener(WebInspector.AnimationModel.Events.Anima
tionCanceled, this._animationCanceled, this); |
86 }, | 90 }, |
87 | 91 |
88 /** | 92 /** |
89 * @param {!WebInspector.Target} target | 93 * @param {!WebInspector.Target} target |
90 */ | 94 */ |
91 _removeEventListeners: function(target) | 95 _removeEventListeners: function(target) |
92 { | 96 { |
93 var animationModel = WebInspector.AnimationModel.fromTarget(target); | 97 var animationModel = WebInspector.AnimationModel.fromTarget(target); |
94 animationModel.removeEventListener(WebInspector.AnimationModel.Events.An
imationCreated, this._animationCreated, this); | 98 animationModel.removeEventListener(WebInspector.AnimationModel.Events.An
imationGroupStarted, this._animationGroupStarted, this); |
95 animationModel.removeEventListener(WebInspector.AnimationModel.Events.An
imationCanceled, this._animationCanceled, this); | 99 animationModel.removeEventListener(WebInspector.AnimationModel.Events.An
imationCanceled, this._animationCanceled, this); |
96 }, | 100 }, |
97 | 101 |
98 /** | 102 /** |
99 * @param {?WebInspector.DOMNode} node | 103 * @param {?WebInspector.DOMNode} node |
100 */ | 104 */ |
101 setNode: function(node) | 105 setNode: function(node) |
102 { | 106 { |
103 for (var nodeUI of this._nodesMap.values()) | 107 for (var nodeUI of this._nodesMap.values()) |
104 nodeUI.setNode(node); | 108 nodeUI.setNode(node); |
(...skipping 24 matching lines...) Expand all Loading... |
129 * @this {WebInspector.AnimationTimeline} | 133 * @this {WebInspector.AnimationTimeline} |
130 */ | 134 */ |
131 function playbackSliderInputHandler(event) | 135 function playbackSliderInputHandler(event) |
132 { | 136 { |
133 this._underlyingPlaybackRate = WebInspector.AnimationTimeline.Global
PlaybackRates[event.target.value]; | 137 this._underlyingPlaybackRate = WebInspector.AnimationTimeline.Global
PlaybackRates[event.target.value]; |
134 this._updatePlaybackControls(); | 138 this._updatePlaybackControls(); |
135 } | 139 } |
136 | 140 |
137 var container = createElementWithClass("div", "animation-timeline-header
"); | 141 var container = createElementWithClass("div", "animation-timeline-header
"); |
138 var controls = container.createChild("div", "animation-controls"); | 142 var controls = container.createChild("div", "animation-controls"); |
139 container.createChild("div", "animation-timeline-markers"); | 143 this._previewContainer = container.createChild("div", "animation-timelin
e-buffer"); |
140 | 144 |
141 var toolbar = new WebInspector.Toolbar(controls); | 145 var toolbar = new WebInspector.Toolbar(controls); |
142 toolbar.element.classList.add("animation-controls-toolbar"); | 146 toolbar.element.classList.add("animation-controls-toolbar"); |
143 this._controlButton = new WebInspector.ToolbarButton(WebInspector.UIStri
ng("Replay timeline"), "replay-outline-toolbar-item"); | 147 this._controlButton = new WebInspector.ToolbarButton(WebInspector.UIStri
ng("Replay timeline"), "replay-outline-toolbar-item"); |
144 this._controlButton.addEventListener("click", this._controlButtonToggle.
bind(this)); | 148 this._controlButton.addEventListener("click", this._controlButtonToggle.
bind(this)); |
145 toolbar.appendToolbarItem(this._controlButton); | 149 toolbar.appendToolbarItem(this._controlButton); |
146 | 150 |
147 this._playbackLabel = controls.createChild("span", "animation-playback-l
abel"); | 151 this._playbackLabel = controls.createChild("span", "animation-playback-l
abel"); |
148 this._playbackLabel.createTextChild("1x"); | 152 this._playbackLabel.createTextChild("1x"); |
149 | 153 |
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
309 */ | 313 */ |
310 _mainFrameNavigated: function(event) | 314 _mainFrameNavigated: function(event) |
311 { | 315 { |
312 this._reset(); | 316 this._reset(); |
313 this._updateAnimationsPlaybackRate(); | 317 this._updateAnimationsPlaybackRate(); |
314 if (this._scrubberPlayer) | 318 if (this._scrubberPlayer) |
315 this._scrubberPlayer.cancel(); | 319 this._scrubberPlayer.cancel(); |
316 delete this._scrubberPlayer; | 320 delete this._scrubberPlayer; |
317 this._timelineScrubberHead.textContent = WebInspector.UIString(Number.mi
llisToString(0)); | 321 this._timelineScrubberHead.textContent = WebInspector.UIString(Number.mi
llisToString(0)); |
318 this._updateControlButton(); | 322 this._updateControlButton(); |
| 323 this._groupBuffer = []; |
| 324 this._previewMap.clear(); |
| 325 this._previewContainer.removeChildren(); |
319 }, | 326 }, |
320 | 327 |
321 /** | 328 /** |
322 * @param {!WebInspector.Event} event | 329 * @param {!WebInspector.Event} event |
323 */ | 330 */ |
324 _animationCreated: function(event) | 331 _animationGroupStarted: function(event) |
325 { | 332 { |
326 this._addAnimation(/** @type {!WebInspector.AnimationModel.Animation} */
(event.data.player), event.data.resetTimeline) | 333 /** |
| 334 * @param {!WebInspector.AnimationModel.AnimationGroup} left |
| 335 * @param {!WebInspector.AnimationModel.AnimationGroup} right |
| 336 */ |
| 337 function startTimeComparator(left, right) |
| 338 { |
| 339 return left.startTime() > right.startTime(); |
| 340 } |
| 341 |
| 342 var newGroup = /** @type {!WebInspector.AnimationModel.AnimationGroup} *
/(event.data); |
| 343 this._groupBuffer.push(newGroup); |
| 344 this._groupBuffer.sort(startTimeComparator); |
| 345 // Discard oldest groups from buffer if necessary |
| 346 var groupsToDiscard = []; |
| 347 while (this._groupBuffer.length > this._groupBufferSize) { |
| 348 var toDiscard = this._groupBuffer.splice(this._groupBuffer[0] === th
is._selectedGroup ? 1 : 0, 1); |
| 349 groupsToDiscard.push(toDiscard[0]); |
| 350 } |
| 351 for (var g of groupsToDiscard) { |
| 352 this._previewMap.get(g).element.remove(); |
| 353 this._previewMap.delete(g); |
| 354 // TODO(samli): needs to discard model too |
| 355 } |
| 356 // Generate preview |
| 357 var preview = new WebInspector.AnimationGroupPreviewUI(newGroup); |
| 358 this._previewMap.set(newGroup, preview); |
| 359 this._previewContainer.appendChild(preview.element); |
| 360 preview.element.addEventListener("click", this._selectAnimationGroup.bin
d(this, newGroup)); |
| 361 }, |
| 362 |
| 363 /** |
| 364 * @param {!WebInspector.AnimationModel.AnimationGroup} group |
| 365 */ |
| 366 _selectAnimationGroup: function(group) |
| 367 { |
| 368 /** |
| 369 * @param {!WebInspector.AnimationGroupPreviewUI} ui |
| 370 * @param {!WebInspector.AnimationModel.AnimationGroup} group |
| 371 * @this {!WebInspector.AnimationTimeline} |
| 372 */ |
| 373 function applySelectionClass(ui, group) |
| 374 { |
| 375 ui.element.classList.toggle("selected", this._selectedGroup === grou
p); |
| 376 } |
| 377 |
| 378 if (this._selectedGroup === group) |
| 379 return; |
| 380 this._selectedGroup = group; |
| 381 this._previewMap.forEach(applySelectionClass, this); |
| 382 this._reset(); |
| 383 for (var anim of group.animations()) |
| 384 this._addAnimation(anim); |
| 385 this.scheduleRedraw(); |
327 }, | 386 }, |
328 | 387 |
329 /** | 388 /** |
330 * @param {!WebInspector.AnimationModel.Animation} animation | 389 * @param {!WebInspector.AnimationModel.Animation} animation |
331 * @param {boolean} resetTimeline | |
332 */ | 390 */ |
333 _addAnimation: function(animation, resetTimeline) | 391 _addAnimation: function(animation) |
334 { | 392 { |
335 /** | 393 /** |
336 * @param {?WebInspector.DOMNode} node | 394 * @param {?WebInspector.DOMNode} node |
337 * @this {WebInspector.AnimationTimeline} | 395 * @this {WebInspector.AnimationTimeline} |
338 */ | 396 */ |
339 function nodeResolved(node) | 397 function nodeResolved(node) |
340 { | 398 { |
341 if (!node) | 399 if (!node) |
342 return; | 400 return; |
343 uiAnimation.setNode(node); | 401 uiAnimation.setNode(node); |
344 node[this._symbol] = nodeUI; | 402 node[this._symbol] = nodeUI; |
345 } | 403 } |
346 | 404 |
347 if (this._emptyTimelineMessage) { | 405 if (this._emptyTimelineMessage) { |
348 this._emptyTimelineMessage.remove(); | 406 this._emptyTimelineMessage.remove(); |
349 delete this._emptyTimelineMessage; | 407 delete this._emptyTimelineMessage; |
350 } | 408 } |
351 | 409 |
352 if (resetTimeline) | |
353 this._reset(); | |
354 | |
355 // Ignore Web Animations custom effects & groups | 410 // Ignore Web Animations custom effects & groups |
356 if (animation.type() === "WebAnimation" && animation.source().keyframesR
ule().keyframes().length === 0) | 411 if (animation.type() === "WebAnimation" && animation.source().keyframesR
ule().keyframes().length === 0) |
357 return; | 412 return; |
358 | 413 |
359 if (this._resizeWindow(animation)) | 414 this._resizeWindow(animation); |
360 this.scheduleRedraw(); | |
361 | 415 |
362 var nodeUI = this._nodesMap.get(animation.source().backendNodeId()); | 416 var nodeUI = this._nodesMap.get(animation.source().backendNodeId()); |
363 if (!nodeUI) { | 417 if (!nodeUI) { |
364 nodeUI = new WebInspector.AnimationTimeline.NodeUI(animation.source(
)); | 418 nodeUI = new WebInspector.AnimationTimeline.NodeUI(animation.source(
)); |
365 this._animationsContainer.appendChild(nodeUI.element); | 419 this._animationsContainer.appendChild(nodeUI.element); |
366 this._nodesMap.set(animation.source().backendNodeId(), nodeUI); | 420 this._nodesMap.set(animation.source().backendNodeId(), nodeUI); |
367 } | 421 } |
368 var nodeRow = nodeUI.findRow(animation); | 422 var nodeRow = nodeUI.findRow(animation); |
369 var uiAnimation = new WebInspector.AnimationUI(animation, this, nodeRow.
element); | 423 var uiAnimation = new WebInspector.AnimationUI(animation, this, nodeRow.
element); |
370 animation.source().deferredNode().resolve(nodeResolved.bind(this)); | 424 animation.source().deferredNode().resolve(nodeResolved.bind(this)); |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
416 line.setAttribute("y", 0); | 470 line.setAttribute("y", 0); |
417 line.setAttribute("height", "100%"); | 471 line.setAttribute("height", "100%"); |
418 line.setAttribute("width", 1); | 472 line.setAttribute("width", 1); |
419 } | 473 } |
420 for (var time = 0; time < this.duration(); time += gridSize) { | 474 for (var time = 0; time < this.duration(); time += gridSize) { |
421 var gridWidth = time * this.pixelMsRatio(); | 475 var gridWidth = time * this.pixelMsRatio(); |
422 if (!lastDraw || gridWidth - lastDraw > 50) { | 476 if (!lastDraw || gridWidth - lastDraw > 50) { |
423 lastDraw = gridWidth; | 477 lastDraw = gridWidth; |
424 var label = this._grid.createSVGChild("text", "animation-timelin
e-grid-label"); | 478 var label = this._grid.createSVGChild("text", "animation-timelin
e-grid-label"); |
425 label.setAttribute("x", gridWidth + 5); | 479 label.setAttribute("x", gridWidth + 5); |
426 label.setAttribute("y", 35); | 480 label.setAttribute("y", 15); |
427 label.textContent = WebInspector.UIString(Number.millisToString(
time)); | 481 label.textContent = WebInspector.UIString(Number.millisToString(
time)); |
428 } | 482 } |
429 } | 483 } |
430 }, | 484 }, |
431 | 485 |
432 scheduleRedraw: function() { | 486 scheduleRedraw: function() { |
433 if (this._redrawing) | 487 if (this._redrawing) |
434 return; | 488 return; |
435 this._redrawing = true; | 489 this._redrawing = true; |
436 this._animationsContainer.window().requestAnimationFrame(this._redraw.bi
nd(this)); | 490 this._animationsContainer.window().requestAnimationFrame(this._redraw.bi
nd(this)); |
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
580 this._timelineScrubberHead.window().requestAnimationFrame(this._updateSc
rubber.bind(this)); | 634 this._timelineScrubberHead.window().requestAnimationFrame(this._updateSc
rubber.bind(this)); |
581 }, | 635 }, |
582 | 636 |
583 __proto__: WebInspector.VBox.prototype | 637 __proto__: WebInspector.VBox.prototype |
584 } | 638 } |
585 | 639 |
586 /** | 640 /** |
587 * @constructor | 641 * @constructor |
588 * @param {!WebInspector.AnimationModel.AnimationEffect} animationEffect | 642 * @param {!WebInspector.AnimationModel.AnimationEffect} animationEffect |
589 */ | 643 */ |
590 WebInspector.AnimationTimeline.NodeUI = function(animationEffect) { | 644 WebInspector.AnimationTimeline.NodeUI = function(animationEffect) |
| 645 { |
591 /** | 646 /** |
592 * @param {?WebInspector.DOMNode} node | 647 * @param {?WebInspector.DOMNode} node |
593 * @this {WebInspector.AnimationTimeline.NodeUI} | 648 * @this {WebInspector.AnimationTimeline.NodeUI} |
594 */ | 649 */ |
595 function nodeResolved(node) | 650 function nodeResolved(node) |
596 { | 651 { |
597 if (!node) | 652 if (!node) |
598 return; | 653 return; |
599 this._node = node; | 654 this._node = node; |
600 WebInspector.DOMPresentationUtils.decorateNodeLabel(node, this._descript
ion); | 655 WebInspector.DOMPresentationUtils.decorateNodeLabel(node, this._descript
ion); |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
687 */ | 742 */ |
688 WebInspector.AnimationTimeline.StepTimingFunction.parse = function(text) { | 743 WebInspector.AnimationTimeline.StepTimingFunction.parse = function(text) { |
689 var match = text.match(/^step-(start|middle|end)$/); | 744 var match = text.match(/^step-(start|middle|end)$/); |
690 if (match) | 745 if (match) |
691 return new WebInspector.AnimationTimeline.StepTimingFunction(1, match[1]
); | 746 return new WebInspector.AnimationTimeline.StepTimingFunction(1, match[1]
); |
692 match = text.match(/^steps\((\d+), (start|middle|end)\)$/); | 747 match = text.match(/^steps\((\d+), (start|middle|end)\)$/); |
693 if (match) | 748 if (match) |
694 return new WebInspector.AnimationTimeline.StepTimingFunction(parseInt(ma
tch[1], 10), match[2]); | 749 return new WebInspector.AnimationTimeline.StepTimingFunction(parseInt(ma
tch[1], 10), match[2]); |
695 return null; | 750 return null; |
696 } | 751 } |
697 | |
698 /** | |
699 * @constructor | |
700 * @param {!WebInspector.AnimationModel.Animation} animation | |
701 * @param {!WebInspector.AnimationTimeline} timeline | |
702 * @param {!Element} parentElement | |
703 */ | |
704 WebInspector.AnimationUI = function(animation, timeline, parentElement) { | |
705 this._animation = animation; | |
706 this._timeline = timeline; | |
707 this._parentElement = parentElement; | |
708 | |
709 if (this._animation.source().keyframesRule()) | |
710 this._keyframes = this._animation.source().keyframesRule().keyframes(); | |
711 | |
712 this._nameElement = parentElement.createChild("div", "animation-name"); | |
713 this._nameElement.textContent = this._animation.name(); | |
714 | |
715 this._svg = parentElement.createSVGChild("svg", "animation-ui"); | |
716 this._svg.setAttribute("height", WebInspector.AnimationUI.Options.AnimationS
VGHeight); | |
717 this._svg.style.marginLeft = "-" + WebInspector.AnimationUI.Options.Animatio
nMargin + "px"; | |
718 this._svg.addEventListener("mousedown", this._mouseDown.bind(this, WebInspec
tor.AnimationUI.MouseEvents.AnimationDrag, null)); | |
719 this._activeIntervalGroup = this._svg.createSVGChild("g"); | |
720 | |
721 /** @type {!Array.<{group: ?Element, animationLine: ?Element, keyframePoints
: !Object.<number, !Element>, keyframeRender: !Object.<number, !Element>}>} */ | |
722 this._cachedElements = []; | |
723 | |
724 this._movementInMs = 0; | |
725 this.redraw(); | |
726 } | |
727 | |
728 /** | |
729 * @enum {string} | |
730 */ | |
731 WebInspector.AnimationUI.MouseEvents = { | |
732 AnimationDrag: "AnimationDrag", | |
733 KeyframeMove: "KeyframeMove", | |
734 StartEndpointMove: "StartEndpointMove", | |
735 FinishEndpointMove: "FinishEndpointMove" | |
736 } | |
737 | |
738 WebInspector.AnimationUI.prototype = { | |
739 /** | |
740 * @return {!WebInspector.AnimationModel.Animation} | |
741 */ | |
742 animation: function() | |
743 { | |
744 return this._animation; | |
745 }, | |
746 | |
747 /** | |
748 * @param {?WebInspector.DOMNode} node | |
749 */ | |
750 setNode: function(node) | |
751 { | |
752 this._node = node; | |
753 }, | |
754 | |
755 /** | |
756 * @param {!Element} parentElement | |
757 * @param {string} className | |
758 */ | |
759 _createLine: function(parentElement, className) | |
760 { | |
761 var line = parentElement.createSVGChild("line", className); | |
762 line.setAttribute("x1", WebInspector.AnimationUI.Options.AnimationMargin
); | |
763 line.setAttribute("y1", WebInspector.AnimationUI.Options.AnimationHeight
); | |
764 line.setAttribute("y2", WebInspector.AnimationUI.Options.AnimationHeight
); | |
765 line.style.stroke = this._color(); | |
766 return line; | |
767 }, | |
768 | |
769 /** | |
770 * @param {number} iteration | |
771 * @param {!Element} parentElement | |
772 */ | |
773 _drawAnimationLine: function(iteration, parentElement) | |
774 { | |
775 var cache = this._cachedElements[iteration]; | |
776 if (!cache.animationLine) | |
777 cache.animationLine = this._createLine(parentElement, "animation-lin
e"); | |
778 cache.animationLine.setAttribute("x2", (this._duration() * this._timelin
e.pixelMsRatio() + WebInspector.AnimationUI.Options.AnimationMargin).toFixed(2))
; | |
779 }, | |
780 | |
781 /** | |
782 * @param {!Element} parentElement | |
783 */ | |
784 _drawDelayLine: function(parentElement) | |
785 { | |
786 if (!this._delayLine) { | |
787 this._delayLine = this._createLine(parentElement, "animation-delay-l
ine"); | |
788 this._endDelayLine = this._createLine(parentElement, "animation-dela
y-line"); | |
789 } | |
790 this._delayLine.setAttribute("x1", WebInspector.AnimationUI.Options.Anim
ationMargin); | |
791 this._delayLine.setAttribute("x2", (this._delay() * this._timeline.pixel
MsRatio() + WebInspector.AnimationUI.Options.AnimationMargin).toFixed(2)); | |
792 var leftMargin = (this._delay() + this._duration() * this._animation.sou
rce().iterations()) * this._timeline.pixelMsRatio(); | |
793 this._endDelayLine.style.transform = "translateX(" + Math.min(leftMargin
, this._timeline.width()).toFixed(2) + "px)"; | |
794 this._endDelayLine.setAttribute("x1", WebInspector.AnimationUI.Options.A
nimationMargin); | |
795 this._endDelayLine.setAttribute("x2", (this._animation.source().endDelay
() * this._timeline.pixelMsRatio() + WebInspector.AnimationUI.Options.AnimationM
argin).toFixed(2)); | |
796 }, | |
797 | |
798 /** | |
799 * @param {number} iteration | |
800 * @param {!Element} parentElement | |
801 * @param {number} x | |
802 * @param {number} keyframeIndex | |
803 * @param {boolean} attachEvents | |
804 */ | |
805 _drawPoint: function(iteration, parentElement, x, keyframeIndex, attachEvent
s) | |
806 { | |
807 if (this._cachedElements[iteration].keyframePoints[keyframeIndex]) { | |
808 this._cachedElements[iteration].keyframePoints[keyframeIndex].setAtt
ribute("cx", x.toFixed(2)); | |
809 return; | |
810 } | |
811 | |
812 var circle = parentElement.createSVGChild("circle", keyframeIndex <= 0 ?
"animation-endpoint" : "animation-keyframe-point"); | |
813 circle.setAttribute("cx", x.toFixed(2)); | |
814 circle.setAttribute("cy", WebInspector.AnimationUI.Options.AnimationHeig
ht); | |
815 circle.style.stroke = this._color(); | |
816 circle.setAttribute("r", WebInspector.AnimationUI.Options.AnimationMargi
n / 2); | |
817 | |
818 if (keyframeIndex <= 0) | |
819 circle.style.fill = this._color(); | |
820 | |
821 this._cachedElements[iteration].keyframePoints[keyframeIndex] = circle; | |
822 | |
823 if (!attachEvents) | |
824 return; | |
825 | |
826 if (keyframeIndex === 0) { | |
827 circle.addEventListener("mousedown", this._mouseDown.bind(this, WebI
nspector.AnimationUI.MouseEvents.StartEndpointMove, keyframeIndex)); | |
828 } else if (keyframeIndex === -1) { | |
829 circle.addEventListener("mousedown", this._mouseDown.bind(this, WebI
nspector.AnimationUI.MouseEvents.FinishEndpointMove, keyframeIndex)); | |
830 } else { | |
831 circle.addEventListener("mousedown", this._mouseDown.bind(this, WebI
nspector.AnimationUI.MouseEvents.KeyframeMove, keyframeIndex)); | |
832 } | |
833 }, | |
834 | |
835 /** | |
836 * @param {number} iteration | |
837 * @param {number} keyframeIndex | |
838 * @param {!Element} parentElement | |
839 * @param {number} leftDistance | |
840 * @param {number} width | |
841 * @param {string} easing | |
842 */ | |
843 _renderKeyframe: function(iteration, keyframeIndex, parentElement, leftDista
nce, width, easing) | |
844 { | |
845 /** | |
846 * @param {!Element} parentElement | |
847 * @param {number} x | |
848 * @param {string} strokeColor | |
849 */ | |
850 function createStepLine(parentElement, x, strokeColor) | |
851 { | |
852 var line = parentElement.createSVGChild("line"); | |
853 line.setAttribute("x1", x); | |
854 line.setAttribute("x2", x); | |
855 line.setAttribute("y1", WebInspector.AnimationUI.Options.AnimationMa
rgin); | |
856 line.setAttribute("y2", WebInspector.AnimationUI.Options.AnimationHe
ight); | |
857 line.style.stroke = strokeColor; | |
858 } | |
859 | |
860 var bezier = WebInspector.Geometry.CubicBezier.parse(easing); | |
861 var cache = this._cachedElements[iteration].keyframeRender; | |
862 if (!cache[keyframeIndex]) | |
863 cache[keyframeIndex] = bezier ? parentElement.createSVGChild("path",
"animation-keyframe") : parentElement.createSVGChild("g", "animation-keyframe-s
tep"); | |
864 var group = cache[keyframeIndex]; | |
865 group.style.transform = "translateX(" + leftDistance.toFixed(2) + "px)"; | |
866 | |
867 if (bezier) { | |
868 group.style.fill = this._color(); | |
869 WebInspector.BezierUI.drawVelocityChart(bezier, group, width); | |
870 } else { | |
871 var stepFunction = WebInspector.AnimationTimeline.StepTimingFunction
.parse(easing); | |
872 group.removeChildren(); | |
873 const offsetMap = {"start": 0, "middle": 0.5, "end": 1}; | |
874 const offsetWeight = offsetMap[stepFunction.stepAtPosition]; | |
875 for (var i = 0; i < stepFunction.steps; i++) | |
876 createStepLine(group, (i + offsetWeight) * width / stepFunction.
steps, this._color()); | |
877 } | |
878 }, | |
879 | |
880 redraw: function() | |
881 { | |
882 var durationWithDelay = this._delay() + this._duration() * this._animati
on.source().iterations() + this._animation.source().endDelay(); | |
883 var leftMargin = ((this._animation.startTime() - this._timeline.startTim
e()) * this._timeline.pixelMsRatio()); | |
884 var maxWidth = this._timeline.width() - WebInspector.AnimationUI.Options
.AnimationMargin - leftMargin; | |
885 var svgWidth = Math.min(maxWidth, durationWithDelay * this._timeline.pix
elMsRatio()); | |
886 | |
887 this._svg.classList.toggle("animation-ui-canceled", this._animation.play
State() === "idle"); | |
888 this._svg.setAttribute("width", (svgWidth + 2 * WebInspector.AnimationUI
.Options.AnimationMargin).toFixed(2)); | |
889 this._svg.style.transform = "translateX(" + leftMargin.toFixed(2) + "px
)"; | |
890 this._activeIntervalGroup.style.transform = "translateX(" + (this._delay
() * this._timeline.pixelMsRatio()).toFixed(2) + "px)"; | |
891 | |
892 this._nameElement.style.transform = "translateX(" + (leftMargin + this._
delay() * this._timeline.pixelMsRatio() + WebInspector.AnimationUI.Options.Anima
tionMargin).toFixed(2) + "px)"; | |
893 this._nameElement.style.width = (this._duration() * this._timeline.pixel
MsRatio().toFixed(2)) + "px"; | |
894 this._drawDelayLine(this._svg); | |
895 | |
896 if (this._animation.type() === "CSSTransition") { | |
897 this._renderTransition(); | |
898 return; | |
899 } | |
900 | |
901 this._renderIteration(this._activeIntervalGroup, 0); | |
902 if (!this._tailGroup) | |
903 this._tailGroup = this._activeIntervalGroup.createSVGChild("g", "ani
mation-tail-iterations"); | |
904 var iterationWidth = this._duration() * this._timeline.pixelMsRatio(); | |
905 for (var iteration = 1; iteration < this._animation.source().iterations(
) && iterationWidth * (iteration - 1) < this._timeline.width(); iteration++) | |
906 this._renderIteration(this._tailGroup, iteration); | |
907 while (iteration < this._cachedElements.length) | |
908 this._cachedElements.pop().group.remove(); | |
909 }, | |
910 | |
911 | |
912 _renderTransition: function() | |
913 { | |
914 if (!this._cachedElements[0]) | |
915 this._cachedElements[0] = { animationLine: null, keyframePoints: {},
keyframeRender: {}, group: null }; | |
916 this._drawAnimationLine(0, this._activeIntervalGroup); | |
917 this._renderKeyframe(0, 0, this._activeIntervalGroup, WebInspector.Anima
tionUI.Options.AnimationMargin, this._duration() * this._timeline.pixelMsRatio()
, this._animation.source().easing()); | |
918 this._drawPoint(0, this._activeIntervalGroup, WebInspector.AnimationUI.O
ptions.AnimationMargin, 0, true); | |
919 this._drawPoint(0, this._activeIntervalGroup, this._duration() * this._t
imeline.pixelMsRatio() + WebInspector.AnimationUI.Options.AnimationMargin, -1, t
rue); | |
920 }, | |
921 | |
922 /** | |
923 * @param {!Element} parentElement | |
924 * @param {number} iteration | |
925 */ | |
926 _renderIteration: function(parentElement, iteration) | |
927 { | |
928 if (!this._cachedElements[iteration]) | |
929 this._cachedElements[iteration] = { animationLine: null, keyframePoi
nts: {}, keyframeRender: {}, group: parentElement.createSVGChild("g") }; | |
930 var group = this._cachedElements[iteration].group; | |
931 group.style.transform = "translateX(" + (iteration * this._duration() *
this._timeline.pixelMsRatio()).toFixed(2) + "px)"; | |
932 this._drawAnimationLine(iteration, group); | |
933 console.assert(this._keyframes.length > 1); | |
934 for (var i = 0; i < this._keyframes.length - 1; i++) { | |
935 var leftDistance = this._offset(i) * this._duration() * this._timeli
ne.pixelMsRatio() + WebInspector.AnimationUI.Options.AnimationMargin; | |
936 var width = this._duration() * (this._offset(i + 1) - this._offset(i
)) * this._timeline.pixelMsRatio(); | |
937 this._renderKeyframe(iteration, i, group, leftDistance, width, this.
_keyframes[i].easing()); | |
938 if (i || (!i && iteration === 0)) | |
939 this._drawPoint(iteration, group, leftDistance, i, iteration ===
0); | |
940 } | |
941 this._drawPoint(iteration, group, this._duration() * this._timeline.pixe
lMsRatio() + WebInspector.AnimationUI.Options.AnimationMargin, -1, iteration ===
0); | |
942 }, | |
943 | |
944 /** | |
945 * @return {number} | |
946 */ | |
947 _delay: function() | |
948 { | |
949 var delay = this._animation.source().delay(); | |
950 if (this._mouseEventType === WebInspector.AnimationUI.MouseEvents.Animat
ionDrag || this._mouseEventType === WebInspector.AnimationUI.MouseEvents.StartEn
dpointMove) | |
951 delay += this._movementInMs; | |
952 // FIXME: add support for negative start delay | |
953 return Math.max(0, delay); | |
954 }, | |
955 | |
956 /** | |
957 * @return {number} | |
958 */ | |
959 _duration: function() | |
960 { | |
961 var duration = this._animation.source().duration(); | |
962 if (this._mouseEventType === WebInspector.AnimationUI.MouseEvents.Finish
EndpointMove) | |
963 duration += this._movementInMs; | |
964 else if (this._mouseEventType === WebInspector.AnimationUI.MouseEvents.S
tartEndpointMove) | |
965 duration -= Math.max(this._movementInMs, -this._animation.source().d
elay()); // Cannot have negative delay | |
966 return Math.max(0, duration); | |
967 }, | |
968 | |
969 /** | |
970 * @param {number} i | |
971 * @return {number} offset | |
972 */ | |
973 _offset: function(i) | |
974 { | |
975 var offset = this._keyframes[i].offsetAsNumber(); | |
976 if (this._mouseEventType === WebInspector.AnimationUI.MouseEvents.Keyfra
meMove && i === this._keyframeMoved) { | |
977 console.assert(i > 0 && i < this._keyframes.length - 1, "First and l
ast keyframe cannot be moved"); | |
978 offset += this._movementInMs / this._animation.source().duration(); | |
979 offset = Math.max(offset, this._keyframes[i - 1].offsetAsNumber()); | |
980 offset = Math.min(offset, this._keyframes[i + 1].offsetAsNumber()); | |
981 } | |
982 return offset; | |
983 }, | |
984 | |
985 /** | |
986 * @param {!WebInspector.AnimationUI.MouseEvents} mouseEventType | |
987 * @param {?number} keyframeIndex | |
988 * @param {!Event} event | |
989 */ | |
990 _mouseDown: function(mouseEventType, keyframeIndex, event) | |
991 { | |
992 if (this._animation.playState() === "idle") | |
993 return; | |
994 this._mouseEventType = mouseEventType; | |
995 this._keyframeMoved = keyframeIndex; | |
996 this._downMouseX = event.clientX; | |
997 this._mouseMoveHandler = this._mouseMove.bind(this); | |
998 this._mouseUpHandler = this._mouseUp.bind(this); | |
999 this._parentElement.ownerDocument.addEventListener("mousemove", this._mo
useMoveHandler); | |
1000 this._parentElement.ownerDocument.addEventListener("mouseup", this._mous
eUpHandler); | |
1001 event.preventDefault(); | |
1002 event.stopPropagation(); | |
1003 | |
1004 if (this._node) | |
1005 WebInspector.Revealer.reveal(this._node); | |
1006 }, | |
1007 | |
1008 /** | |
1009 * @param {!Event} event | |
1010 */ | |
1011 _mouseMove: function (event) | |
1012 { | |
1013 this._movementInMs = (event.clientX - this._downMouseX) / this._timeline
.pixelMsRatio(); | |
1014 if (this._animation.startTime() + this._delay() + this._duration() - thi
s._timeline.startTime() > this._timeline.duration() * 0.8) | |
1015 this._timeline.setDuration(this._timeline.duration() * 1.2); | |
1016 this.redraw(); | |
1017 }, | |
1018 | |
1019 /** | |
1020 * @param {!Event} event | |
1021 */ | |
1022 _mouseUp: function(event) | |
1023 { | |
1024 this._movementInMs = (event.clientX - this._downMouseX) / this._timeline
.pixelMsRatio(); | |
1025 | |
1026 // Commit changes | |
1027 if (this._mouseEventType === WebInspector.AnimationUI.MouseEvents.Keyfra
meMove) { | |
1028 this._keyframes[this._keyframeMoved].setOffset(this._offset(this._ke
yframeMoved)); | |
1029 } else { | |
1030 var delay = this._delay(); | |
1031 var duration = this._duration(); | |
1032 this._setDelay(delay); | |
1033 this._setDuration(duration); | |
1034 if (this._animation.type() !== "CSSAnimation") { | |
1035 for (var target of WebInspector.targetManager.targets(WebInspect
or.Target.Type.Page)) | |
1036 target.animationAgent().setTiming(this._animation.id(), dura
tion, delay); | |
1037 } | |
1038 } | |
1039 | |
1040 this._movementInMs = 0; | |
1041 this.redraw(); | |
1042 | |
1043 this._parentElement.ownerDocument.removeEventListener("mousemove", this.
_mouseMoveHandler); | |
1044 this._parentElement.ownerDocument.removeEventListener("mouseup", this._m
ouseUpHandler); | |
1045 delete this._mouseMoveHandler; | |
1046 delete this._mouseUpHandler; | |
1047 delete this._mouseEventType; | |
1048 delete this._downMouseX; | |
1049 delete this._keyframeMoved; | |
1050 }, | |
1051 | |
1052 /** | |
1053 * @param {number} value | |
1054 */ | |
1055 _setDelay: function(value) | |
1056 { | |
1057 if (!this._node || this._animation.source().delay() == this._delay()) | |
1058 return; | |
1059 | |
1060 this._animation.source().setDelay(this._delay()); | |
1061 var propertyName; | |
1062 if (this._animation.type() == "CSSTransition") | |
1063 propertyName = "transition-delay"; | |
1064 else if (this._animation.type() == "CSSAnimation") | |
1065 propertyName = "animation-delay"; | |
1066 else | |
1067 return; | |
1068 this._setNodeStyle(propertyName, Math.round(value) + "ms"); | |
1069 }, | |
1070 | |
1071 /** | |
1072 * @param {number} value | |
1073 */ | |
1074 _setDuration: function(value) | |
1075 { | |
1076 if (!this._node || this._animation.source().duration() == value) | |
1077 return; | |
1078 | |
1079 this._animation.source().setDuration(value); | |
1080 var propertyName; | |
1081 if (this._animation.type() == "CSSTransition") | |
1082 propertyName = "transition-duration"; | |
1083 else if (this._animation.type() == "CSSAnimation") | |
1084 propertyName = "animation-duration"; | |
1085 else | |
1086 return; | |
1087 this._setNodeStyle(propertyName, Math.round(value) + "ms"); | |
1088 }, | |
1089 | |
1090 /** | |
1091 * @param {string} name | |
1092 * @param {string} value | |
1093 */ | |
1094 _setNodeStyle: function(name, value) | |
1095 { | |
1096 var style = this._node.getAttribute("style") || ""; | |
1097 if (style) | |
1098 style = style.replace(new RegExp("\\s*(-webkit-)?" + name + ":[^;]*;
?\\s*", "g"), ""); | |
1099 var valueString = name + ": " + value; | |
1100 this._node.setAttributeValue("style", style + " " + valueString + "; -we
bkit-" + valueString + ";"); | |
1101 }, | |
1102 | |
1103 /** | |
1104 * @return {string} | |
1105 */ | |
1106 _color: function() | |
1107 { | |
1108 /** | |
1109 * @param {string} string | |
1110 * @return {number} | |
1111 */ | |
1112 function hash(string) | |
1113 { | |
1114 var hash = 0; | |
1115 for (var i = 0; i < string.length; i++) | |
1116 hash = (hash << 5) + hash + string.charCodeAt(i); | |
1117 return Math.abs(hash); | |
1118 } | |
1119 | |
1120 if (!this._selectedColor) { | |
1121 var names = Object.keys(WebInspector.AnimationUI.Colors); | |
1122 var color = WebInspector.AnimationUI.Colors[names[hash(this._animati
on.name() || this._animation.id()) % names.length]]; | |
1123 this._selectedColor = color.asString(WebInspector.Color.Format.RGB); | |
1124 } | |
1125 return this._selectedColor; | |
1126 } | |
1127 } | |
1128 | |
1129 WebInspector.AnimationUI.Options = { | |
1130 AnimationHeight: 32, | |
1131 AnimationSVGHeight: 80, | |
1132 AnimationMargin: 7, | |
1133 EndpointsClickRegionSize: 10, | |
1134 GridCanvasHeight: 40 | |
1135 } | |
1136 | |
1137 WebInspector.AnimationUI.Colors = { | |
1138 "Purple": WebInspector.Color.parse("#9C27B0"), | |
1139 "Light Blue": WebInspector.Color.parse("#03A9F4"), | |
1140 "Deep Orange": WebInspector.Color.parse("#FF5722"), | |
1141 "Blue": WebInspector.Color.parse("#5677FC"), | |
1142 "Lime": WebInspector.Color.parse("#CDDC39"), | |
1143 "Pink": WebInspector.Color.parse("#E91E63"), | |
1144 "Green": WebInspector.Color.parse("#0F9D58"), | |
1145 "Brown": WebInspector.Color.parse("#795548"), | |
1146 "Cyan": WebInspector.Color.parse("#00BCD4") | |
1147 } | |
OLD | NEW |