RectanglePath2D.as
8.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
/**
* VERSION: 0.2 (beta)
* DATE: 2010-04-21
* ACTIONSCRIPT VERSION: 3.0
* UPDATES AND DOCUMENTATION AT: http://www.GreenSock.com
**/
package com.greensock.motionPaths {
import flash.display.Graphics;
import flash.geom.Matrix;
/**
* A RectanglePath2D defines a rectangular path on which a PathFollower can be placed, making it simple to tween objects
* along a rectangle's perimeter. A PathFollower's position along the path is described using its <code>progress</code> property,
* a value between 0 and 1 where 0 is at the beginning of the path (top left corner), and as the value increases, it
* moves clockwise along the path so that 0.5 would be at the lower right corner, and 1 is all the way back at the
* upper left corner of the path. So to tween a PathFollower along the path, you can simply tween its
* <code>progress</code> property. To tween ALL of the followers on the path at once, you can tween the
* RectanglePath2D's <code>progress</code> property. PathFollowers automatically wrap so that if the <code>progress</code>
* value exceeds 1 it continues at the beginning of the path.<br /><br />
*
* Since RectanglePath2D extends the Shape class, you can add an instance to the display list to see a line representation
* of the path drawn which can be helpful especially during the production phase. Use <code>lineStyle()</code>
* to adjust the color, thickness, and other attributes of the line that is drawn (or set the RectanglePath2D's
* <code>visible</code> property to false or don't add it to the display list if you don't want to see the line
* at all). You can also adjust all of its properties like <code>scaleX, scaleY, rotation, width, height, x,</code>
* and <code>y</code>. That means you can tween those values as well to achieve very dynamic, complex effects
* with ease.<br /><br />
*
* @example Example AS3 code:<listing version="3.0">
import com.greensock.~~;
import com.greensock.motionPaths.~~;
//create a rectangular motion path at coordinates x:25, y:25 with a width of 150 and a height of 100
var rect:RectanglePath2D = new RectanglePath2D(25, 25, 150, 100, false);
//position the MovieClip "mc" at the beginning of the path (upper left corner), and reference the resulting PathFollower instance with a "follower" variable.
var follower:PathFollower = rect.addFollower(mc, 0);
//tween the follower clockwise along the path all the way to the end, one full revolution
TweenLite.to(follower, 2, {progress:1});
//tween the follower counter-clockwise by using a negative progress value
TweenLite.to(follower, 2, {progress:-1});
</listing>
*
* <b>NOTES</b><br />
* <ul>
* <li>All followers' positions are automatically updated when you alter the MotionPath that they're following.</li>
* <li>To tween all followers along the path at once, simply tween the MotionPath's <code>progress</code>
* property which will provide better performance than tweening each follower independently.</li>
* </ul>
*
* <b>Copyright 2010, GreenSock. All rights reserved.</b> This work is subject to the terms in <a href="http://www.greensock.com/terms_of_use.html">http://www.greensock.com/terms_of_use.html</a> or for corporate Club GreenSock members, the software agreement that was issued with the corporate membership.
*
* @author Jack Doyle, jack@greensock.com
*/
public class RectanglePath2D extends MotionPath {
/** @private **/
protected var _rawWidth:Number;
/** @private **/
protected var _rawHeight:Number;
/** @private **/
protected var _centerOrigin:Boolean;
/**
* Constructor
*
* @param x The x coordinate of the origin of the rectangle (typically its top left corner unless <code>centerOrigin</code> is <code>true</code>)
* @param y The y coordinate of the origin of the rectangle (typically its top left corner unless <code>centerOrigin</code> is <code>true</code>)
* @param rawWidth The width of the rectangle in its unrotated and unscaled state
* @param rawHeight The height of the rectangle in its unrotated and unscaled state
* @param centerOrigin To position the origin (registration point around which transformations occur) at the center of the rectangle instead of its upper left corner, set <code>centerOrigin</code> to <code>true</code> (it is false by default).
*/
public function RectanglePath2D(x:Number, y:Number, rawWidth:Number, rawHeight:Number, centerOrigin:Boolean=false) {
super();
_rawWidth = rawWidth;
_rawHeight = rawHeight;
_centerOrigin = centerOrigin;
super.x = x;
super.y = y;
}
/** @private **/
override protected function renderAll():void {
var xOffset:Number = _centerOrigin ? _rawWidth / -2 : 0;
var yOffset:Number = _centerOrigin ? _rawHeight / -2 : 0;
var length:Number, px:Number, py:Number, xFactor:Number, yFactor:Number;
var m:Matrix = this.transform.matrix;
var a:Number = m.a, b:Number = m.b, c:Number = m.c, d:Number = m.d, tx:Number = m.tx, ty:Number = m.ty;
var f:PathFollower = _rootFollower;
while (f) {
px = xOffset;
py = yOffset;
if (f.cachedProgress < 0.5) {
length = f.cachedProgress * (_rawWidth + _rawHeight) * 2;
if (length > _rawWidth) { //top
px += _rawWidth;
py += length - _rawWidth;
xFactor = 0;
yFactor = _rawHeight;
} else { //right
px += length;
xFactor = _rawWidth;
yFactor = 0;
}
} else {
length = (f.cachedProgress - 0.5) / 0.5 * (_rawWidth + _rawHeight);
if (length <= _rawWidth) { //bottom
px += _rawWidth - length;
py += _rawHeight;
xFactor = -_rawWidth;
yFactor = 0;
} else { //left
py += _rawHeight - (length - _rawWidth);
xFactor = 0;
yFactor = -_rawHeight;
}
}
f.target.x = px * a + py * c + tx;
f.target.y = px * b + py * d + ty;
if (f.autoRotate) {
f.target.rotation = Math.atan2(xFactor * b + yFactor * d, xFactor * a + yFactor * c) * _RAD2DEG + f.rotationOffset;
}
f = f.cachedNext;
}
if (_redrawLine && this.visible && this.parent) {
var g:Graphics = this.graphics;
g.clear();
g.lineStyle(_thickness, _color, _lineAlpha, _pixelHinting, _scaleMode, _caps, _joints, _miterLimit);
g.drawRect(xOffset, yOffset, _rawWidth, _rawHeight);
_redrawLine = false;
}
}
/** @inheritDoc **/
override public function renderObjectAt(target:Object, progress:Number, autoRotate:Boolean=false, rotationOffset:Number=0):void {
if (progress > 1) {
progress -= int(progress);
} else if (progress < 0) {
progress -= int(progress) - 1;
}
var px:Number = _centerOrigin ? _rawWidth / -2 : 0;
var py:Number = _centerOrigin ? _rawHeight / -2 : 0;
var length:Number, xFactor:Number, yFactor:Number;
if (progress < 0.5) {
length = progress * (_rawWidth + _rawHeight) * 2;
if (length > _rawWidth) {
px += _rawWidth;
py += length - _rawWidth;
xFactor = 0;
yFactor = _rawHeight;
} else {
px += length;
xFactor = _rawWidth;
yFactor = 0;
}
} else {
length = (progress - 0.5) / 0.5 * (_rawWidth + _rawHeight);
if (length <= _rawWidth) {
px += _rawWidth - length;
py += _rawHeight;
xFactor = -_rawWidth;
yFactor = 0;
} else {
py += _rawHeight - (length - _rawWidth);
xFactor = 0;
yFactor = -_rawHeight;
}
}
var m:Matrix = this.transform.matrix;
target.x = px * m.a + py * m.c + m.tx;
target.y = px * m.b + py * m.d + m.ty;
if (autoRotate) {
target.rotation = Math.atan2(xFactor * m.b + yFactor * m.d, xFactor * m.a + yFactor * m.c) * _RAD2DEG + rotationOffset;
}
}
//---- GETTERS / SETTERS ----------------------------------------------------------------------
/** width of the rectangle in its unrotated, unscaled state (does not factor in any transformations like scaleX/scaleY/rotation) **/
public function get rawWidth():Number {
return _rawWidth;
}
public function set rawWidth(value:Number):void {
_rawWidth = value;
_redrawLine = true;
renderAll();
}
/** height of the rectangle in its unrotated, unscaled state (does not factor in any transformations like scaleX/scaleY/rotation) **/
public function get rawHeight():Number {
return _rawHeight;
}
public function set rawHeight(value:Number):void {
_rawHeight = value;
_redrawLine = true;
renderAll();
}
/** height of the rectangle in its unrotated, unscaled state (does not factor in any transformations like scaleX/scaleY/rotation) **/
public function get centerOrigin():Boolean {
return _centerOrigin;
}
public function set centerOrigin(value:Boolean):void {
_centerOrigin;
_redrawLine = true;
renderAll();
}
}
}