1<!DOCTYPE html> 2<!-- 3Copyright (c) 2014 The Chromium Authors. All rights reserved. 4Use of this source code is governed by a BSD-style license that can be 5found in the LICENSE file. 6--> 7<link rel="import" href="/tracing/base/event_target.html"> 8<link rel="import" href="/tracing/base/raf.html"> 9<link rel="import" href="/tracing/ui/base/animation.html"> 10<script> 11'use strict'; 12 13tr.exportTo('tr.ui.b', function() { 14 /** 15 * Manages execution, queueing and blending of tr.ui.b.Animations against 16 * a single target. 17 * 18 * Targets must have a cloneAnimationState() method that returns all the 19 * animatable states of that target. 20 * 21 * @constructor 22 * @extends {tr.b.EventTarget} 23 */ 24 function AnimationController() { 25 tr.b.EventTarget.call(this); 26 27 this.target_ = undefined; 28 29 this.activeAnimation_ = undefined; 30 31 this.tickScheduled_ = false; 32 } 33 34 AnimationController.prototype = { 35 __proto__: tr.b.EventTarget.prototype, 36 37 get target() { 38 return this.target_; 39 }, 40 41 set target(target) { 42 if (this.activeAnimation_) 43 throw new Error('Cannot change target while animation is running.'); 44 if (target.cloneAnimationState === undefined || 45 typeof target.cloneAnimationState !== 'function') 46 throw new Error('target must have a cloneAnimationState function'); 47 48 this.target_ = target; 49 }, 50 51 get activeAnimation() { 52 return this.activeAnimation_; 53 }, 54 55 get hasActiveAnimation() { 56 return !!this.activeAnimation_; 57 }, 58 59 queueAnimation: function(animation, opt_now) { 60 if (this.target_ === undefined) 61 throw new Error('Cannot queue animations without a target'); 62 63 var now; 64 if (opt_now !== undefined) 65 now = opt_now; 66 else 67 now = window.performance.now(); 68 69 if (this.activeAnimation_) { 70 // Must tick the animation before stopping it case its about to stop, 71 // and to update the target with its final sets of edits up to this 72 // point. 73 var done = this.activeAnimation_.tick(now, this.target_); 74 if (done) 75 this.activeAnimation_ = undefined; 76 } 77 78 if (this.activeAnimation_) { 79 if (animation.canTakeOverFor(this.activeAnimation_)) { 80 this.activeAnimation_.didStopEarly(now, this.target_, true); 81 animation.takeOverFor(this.activeAnimation_, now, this.target_); 82 } else { 83 this.activeAnimation_.didStopEarly(now, this.target_, false); 84 } 85 } 86 this.activeAnimation_ = animation; 87 this.activeAnimation_.start(now, this.target_); 88 89 if (this.tickScheduled_) 90 return; 91 this.tickScheduled_ = true; 92 tr.b.requestAnimationFrame(this.tickActiveAnimation_, this); 93 }, 94 95 cancelActiveAnimation: function(opt_now) { 96 if (!this.activeAnimation_) 97 return; 98 var now; 99 if (opt_now !== undefined) 100 now = opt_now; 101 else 102 now = window.performance.now(); 103 this.activeAnimation_.didStopEarly(now, this.target_, false); 104 this.activeAnimation_ = undefined; 105 }, 106 107 tickActiveAnimation_: function(frameBeginTime) { 108 this.tickScheduled_ = false; 109 if (!this.activeAnimation_) 110 return; 111 112 if (this.target_ === undefined) { 113 this.activeAnimation_.didStopEarly(frameBeginTime, this.target_, false); 114 return; 115 } 116 117 var oldTargetState = this.target_.cloneAnimationState(); 118 119 var done = this.activeAnimation_.tick(frameBeginTime, this.target_); 120 if (done) 121 this.activeAnimation_ = undefined; 122 123 if (this.activeAnimation_) { 124 this.tickScheduled_ = true; 125 tr.b.requestAnimationFrame(this.tickActiveAnimation_, this); 126 } 127 128 if (oldTargetState) { 129 var e = new tr.b.Event('didtick'); 130 e.oldTargetState = oldTargetState; 131 this.dispatchEvent(e, false, false); 132 } 133 } 134 }; 135 136 return { 137 AnimationController: AnimationController 138 }; 139}); 140</script> 141