1<!DOCTYPE html>
2<!--
3Copyright (c) 2013 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/utils.html">
8<link rel="import" href="/tracing/ui/base/animation.html">
9
10<script>
11'use strict';
12
13tr.exportTo('tr.ui', function() {
14  var kDefaultPanAnimationDurationMs = 100.0;
15
16  /**
17   * Pans a TimelineDisplayTransform by a given amount.
18   * @constructor
19   * @extends {tr.ui.b.Animation}
20   * @param {Number} deltaX The total amount of change to the transform's panX.
21   * @param {Number} deltaY The total amount of change to the transform's panY.
22   * @param {Number=} opt_durationMs How long the pan animation should run.
23   * Defaults to kDefaultPanAnimationDurationMs.
24   */
25  function TimelineDisplayTransformPanAnimation(
26      deltaX, deltaY, opt_durationMs) {
27    this.deltaX = deltaX;
28    this.deltaY = deltaY;
29    if (opt_durationMs === undefined)
30      this.durationMs = kDefaultPanAnimationDurationMs;
31    else
32      this.durationMs = opt_durationMs;
33
34    this.startPanX = undefined;
35    this.startPanY = undefined;
36    this.startTimeMs = undefined;
37  }
38
39  TimelineDisplayTransformPanAnimation.prototype = {
40    __proto__: tr.ui.b.Animation.prototype,
41
42    get affectsPanY() {
43      return this.deltaY !== 0;
44    },
45
46    canTakeOverFor: function(existingAnimation) {
47      return existingAnimation instanceof TimelineDisplayTransformPanAnimation;
48    },
49
50    takeOverFor: function(existing, timestamp, target) {
51      var remainingDeltaXOnExisting = existing.goalPanX - target.panX;
52      var remainingDeltaYOnExisting = existing.goalPanY - target.panY;
53      var remainingTimeOnExisting = timestamp - (
54          existing.startTimeMs + existing.durationMs);
55      remainingTimeOnExisting = Math.max(remainingTimeOnExisting, 0);
56
57      this.deltaX += remainingDeltaXOnExisting;
58      this.deltaY += remainingDeltaYOnExisting;
59      this.durationMs += remainingTimeOnExisting;
60    },
61
62    start: function(timestamp, target) {
63      this.startTimeMs = timestamp;
64      this.startPanX = target.panX;
65      this.startPanY = target.panY;
66    },
67
68    tick: function(timestamp, target) {
69      var percentDone = (timestamp - this.startTimeMs) / this.durationMs;
70      percentDone = tr.b.clamp(percentDone, 0, 1);
71
72      target.panX = tr.b.lerp(percentDone, this.startPanX, this.goalPanX);
73      if (this.affectsPanY)
74        target.panY = tr.b.lerp(percentDone, this.startPanY, this.goalPanY);
75      return timestamp >= this.startTimeMs + this.durationMs;
76    },
77
78    get goalPanX() {
79      return this.startPanX + this.deltaX;
80    },
81
82    get goalPanY() {
83      return this.startPanY + this.deltaY;
84    }
85  };
86
87  /**
88   * Zooms in/out on a specified location in the world.
89   *
90   * Zooming in and out is all about keeping the area under the mouse cursor,
91   * here called the "focal point" in the same place under the zoom. If one
92   * simply changes the scale, the area under the mouse cursor will change. To
93   * keep the focal point from moving during the zoom, the pan needs to change
94   * in order to compensate. Thus, a ZoomTo animation is given both a focal
95   * point in addition to the amount by which to zoom.
96   *
97   * @constructor
98   * @extends {tr.ui.b.Animation}
99   * @param {Number} goalFocalPointXWorld The X coordinate in the world which is
100   * of interest.
101   * @param {Number} goalFocalPointXView Where on the screen the
102   * goalFocalPointXWorld should stay centered during the zoom.
103   * @param {Number} goalFocalPointY Where the panY should be when the zoom
104   * completes.
105   * @param {Number} zoomInRatioX The ratio of the current scaleX to the goal
106   * scaleX.
107   */
108  function TimelineDisplayTransformZoomToAnimation(
109      goalFocalPointXWorld,
110      goalFocalPointXView,
111      goalFocalPointY,
112      zoomInRatioX,
113      opt_durationMs) {
114    this.goalFocalPointXWorld = goalFocalPointXWorld;
115    this.goalFocalPointXView = goalFocalPointXView;
116    this.goalFocalPointY = goalFocalPointY;
117    this.zoomInRatioX = zoomInRatioX;
118    if (opt_durationMs === undefined)
119      this.durationMs = kDefaultPanAnimationDurationMs;
120    else
121      this.durationMs = opt_durationMs;
122
123    this.startTimeMs = undefined;
124    this.startScaleX = undefined;
125    this.goalScaleX = undefined;
126    this.startPanY = undefined;
127  }
128
129  TimelineDisplayTransformZoomToAnimation.prototype = {
130    __proto__: tr.ui.b.Animation.prototype,
131
132    get affectsPanY() {
133      return this.startPanY != this.goalFocalPointY;
134    },
135
136    canTakeOverFor: function(existingAnimation) {
137      return false;
138    },
139
140    takeOverFor: function(existingAnimation, timestamp, target) {
141      this.goalScaleX = target.scaleX * this.zoomInRatioX;
142    },
143
144    start: function(timestamp, target) {
145      this.startTimeMs = timestamp;
146      this.startScaleX = target.scaleX;
147      this.goalScaleX = this.zoomInRatioX * target.scaleX;
148      this.startPanY = target.panY;
149    },
150
151    tick: function(timestamp, target) {
152      var percentDone = (timestamp - this.startTimeMs) / this.durationMs;
153      percentDone = tr.b.clamp(percentDone, 0, 1);
154
155      target.scaleX = tr.b.lerp(percentDone, this.startScaleX, this.goalScaleX);
156      if (this.affectsPanY) {
157        target.panY = tr.b.lerp(
158            percentDone, this.startPanY, this.goalFocalPointY);
159      }
160
161      target.xPanWorldPosToViewPos(
162          this.goalFocalPointXWorld, this.goalFocalPointXView);
163      return timestamp >= this.startTimeMs + this.durationMs;
164    }
165  };
166
167  return {
168    TimelineDisplayTransformPanAnimation:
169        TimelineDisplayTransformPanAnimation,
170    TimelineDisplayTransformZoomToAnimation:
171        TimelineDisplayTransformZoomToAnimation
172  };
173});
174</script>
175