| 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 |