Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(30)

Side by Side Diff: Source/devtools/front_end/animation/AnimationTimeline.js

Issue 1218433007: Devtools Animations: Add buffer and effect selection to animation timeline (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698