3
Copyright 2012 Yahoo! Inc. All rights reserved.
4
Licensed under the BSD License.
5
http://yuilibrary.com/license/
7
YUI.add('graphics-canvas', function(Y) {
9
var SHAPE = "canvasShape",
10
DOCUMENT = Y.config.doc,
12
AttributeLite = Y.AttributeLite,
21
PARSE_FLOAT = parseFloat,
22
IS_NUMBER = Y_LANG.isNumber,
24
TORGB = Y_Color.toRGB,
25
TOHEX = Y_Color.toHex;
28
* <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> implementation of the <a href="Drawing.html">`Drawing`</a> class.
29
* `CanvasDrawing` is not intended to be used directly. Instead, use the <a href="Drawing.html">`Drawing`</a> class.
30
* If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities but has
31
* <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> capabilities, the <a href="Drawing.html">`Drawing`</a>
32
* class will point to the `CanvasDrawing` class.
35
* @class CanvasDrawing
38
function CanvasDrawing()
42
CanvasDrawing.prototype = {
44
* Parses hex color string and alpha value to rgba
47
* @param {Object} val Color value to parse. Can be hex string, rgb or name.
48
* @param {Number} alpha Numeric value between 0 and 1 representing the alpha level.
51
_toRGBA: function(val, alpha) {
52
alpha = (alpha !== undefined) ? alpha : 1;
53
if (!Y_Color.re_RGB.test(val)) {
57
if(Y_Color.re_hex.exec(val)) {
62
].join(',') + ',' + alpha + ')';
68
* Converts color to rgb format
71
* @param val Color value to convert.
74
_toRGB: function(val) {
79
* Sets the size of the graphics object.
82
* @param w {Number} width to set for the instance.
83
* @param h {Number} height to set for the instance.
86
setSize: function(w, h) {
87
if(this.get("autoSize"))
89
if(w > this.node.getAttribute("width"))
91
this.node.style.width = w + "px";
92
this.node.setAttribute("width", w);
94
if(h > this.node.getAttribute("height"))
96
this.node.style.height = h + "px";
97
this.node.setAttribute("height", h);
103
* Tracks coordinates. Used to calculate the start point of dashed lines.
105
* @method _updateCoords
106
* @param {Number} x x-coordinate
107
* @param {Number} y y-coordinate
110
_updateCoords: function(x, y)
112
this._xcoords.push(x);
113
this._ycoords.push(y);
117
* Clears the coordinate arrays. Called at the end of a drawing operation.
119
* @method _clearAndUpdateCoords
122
_clearAndUpdateCoords: function()
124
var x = this._xcoords.pop() || 0,
125
y = this._ycoords.pop() || 0;
126
this._updateCoords(x, y);
130
* Moves the shape's dom node.
132
* @method _updateNodePosition
135
_updateNodePosition: function()
137
var node = this.get("node"),
140
node.style.position = "absolute";
141
node.style.left = (x + this._left) + "px";
142
node.style.top = (y + this._top) + "px";
146
* Queues up a method to be executed when a shape redraws.
148
* @method _updateDrawingQueue
149
* @param {Array} val An array containing data that can be parsed into a method and arguments. The value at zero-index of the array is a string reference of
150
* the drawing method that will be called. All subsequent indices are argument for that method. For example, `lineTo(10, 100)` would be structured as:
151
* `["lineTo", 10, 100]`.
154
_updateDrawingQueue: function(val)
156
this._methods.push(val);
160
* Draws a line segment using the current line style from the current drawing position to the specified x and y coordinates.
163
* @param {Number} point1 x-coordinate for the end point.
164
* @param {Number} point2 y-coordinate for the end point.
166
lineTo: function(point1, point2, etc)
168
var args = arguments,
173
wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0;
174
if(!this._lineToMethods)
176
this._lineToMethods = [];
178
if (typeof point1 === 'string' || typeof point1 === 'number') {
179
args = [[point1, point2]];
189
this._updateDrawingQueue(["lineTo", x, y]);
190
this._lineToMethods[this._lineToMethods.length] = this._methods[this._methods.length - 1];
191
this._trackSize(x - wt, y - wt);
192
this._trackSize(x + wt, y + wt);
193
this._updateCoords(x, y);
196
this._drawingComplete = false;
201
* Moves the current drawing position to specified x and y coordinates.
204
* @param {Number} x x-coordinate for the end point.
205
* @param {Number} y y-coordinate for the end point.
207
moveTo: function(x, y) {
208
var wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0;
209
this._updateDrawingQueue(["moveTo", x, y]);
210
this._trackSize(x - wt, y - wt);
211
this._trackSize(x + wt, y + wt);
212
this._updateCoords(x, y);
213
this._drawingComplete = false;
218
* Draws a bezier curve.
221
* @param {Number} cp1x x-coordinate for the first control point.
222
* @param {Number} cp1y y-coordinate for the first control point.
223
* @param {Number} cp2x x-coordinate for the second control point.
224
* @param {Number} cp2y y-coordinate for the second control point.
225
* @param {Number} x x-coordinate for the end point.
226
* @param {Number} y y-coordinate for the end point.
228
curveTo: function(cp1x, cp1y, cp2x, cp2y, x, y) {
233
this._updateDrawingQueue(["bezierCurveTo", cp1x, cp1y, cp2x, cp2y, x, y]);
234
this._drawingComplete = false;
235
hiX = Math.max(x, Math.max(cp1x, cp2x));
236
hiY = Math.max(y, Math.max(cp1y, cp2y));
237
loX = Math.min(x, Math.min(cp1x, cp2x));
238
loY = Math.min(y, Math.min(cp1y, cp2y));
239
this._trackSize(hiX, hiY);
240
this._trackSize(loX, loY);
241
this._updateCoords(hiX, hiY);
246
* Draws a quadratic bezier curve.
248
* @method quadraticCurveTo
249
* @param {Number} cpx x-coordinate for the control point.
250
* @param {Number} cpy y-coordinate for the control point.
251
* @param {Number} x x-coordinate for the end point.
252
* @param {Number} y y-coordinate for the end point.
254
quadraticCurveTo: function(cpx, cpy, x, y) {
259
wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0;
260
this._updateDrawingQueue(["quadraticCurveTo", cpx, cpy, x, y]);
261
this._drawingComplete = false;
262
hiX = Math.max(x, cpx);
263
hiY = Math.max(y, cpy);
264
loX = Math.min(x, cpx);
265
loY = Math.min(y, cpy);
266
this._trackSize(hiX + wt, hiY + wt);
267
this._trackSize(loX - wt, loY - wt);
268
this._updateCoords(hiX, hiY);
273
* Draws a circle. Used internally by `CanvasCircle` class.
276
* @param {Number} x y-coordinate
277
* @param {Number} y x-coordinate
278
* @param {Number} r radius
281
drawCircle: function(x, y, radius) {
283
endAngle = 2 * Math.PI,
284
wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0,
287
this._drawingComplete = false;
288
this._trackSize(x + circum, y + circum);
289
this._trackSize(x - wt, y - wt);
290
this._updateCoords(x, y);
291
this._updateDrawingQueue(["arc", x + radius, y + radius, radius, startAngle, endAngle, false]);
298
* @method drawDiamond
299
* @param {Number} x y-coordinate
300
* @param {Number} y x-coordinate
301
* @param {Number} width width
302
* @param {Number} height height
305
drawDiamond: function(x, y, width, height)
307
var midWidth = width * 0.5,
308
midHeight = height * 0.5;
309
this.moveTo(x + midWidth, y);
310
this.lineTo(x + width, y + midHeight);
311
this.lineTo(x + midWidth, y + height);
312
this.lineTo(x, y + midHeight);
313
this.lineTo(x + midWidth, y);
318
* Draws an ellipse. Used internally by `CanvasEllipse` class.
320
* @method drawEllipse
321
* @param {Number} x x-coordinate
322
* @param {Number} y y-coordinate
323
* @param {Number} w width
324
* @param {Number} h height
327
drawEllipse: function(x, y, w, h) {
329
theta = -(45/180) * Math.PI,
335
centerX = x + radius,
336
centerY = y + yRadius,
337
ax, ay, bx, by, cx, cy,
338
wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0;
340
ax = centerX + Math.cos(0) * radius;
341
ay = centerY + Math.sin(0) * yRadius;
346
angleMid = angle - (theta / 2);
347
bx = centerX + Math.cos(angle) * radius;
348
by = centerY + Math.sin(angle) * yRadius;
349
cx = centerX + Math.cos(angleMid) * (radius / Math.cos(theta / 2));
350
cy = centerY + Math.sin(angleMid) * (yRadius / Math.cos(theta / 2));
351
this._updateDrawingQueue(["quadraticCurveTo", cx, cy, bx, by]);
353
this._trackSize(x + w + wt, y + h + wt);
354
this._trackSize(x - wt, y - wt);
355
this._updateCoords(x, y);
363
* @param {Number} x x-coordinate
364
* @param {Number} y y-coordinate
365
* @param {Number} w width
366
* @param {Number} h height
368
drawRect: function(x, y, w, h) {
369
var wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0;
370
this._drawingComplete = false;
371
this._updateDrawingQueue(["moveTo", x, y]);
372
this._updateDrawingQueue(["lineTo", x + w, y]);
373
this._updateDrawingQueue(["lineTo", x + w, y + h]);
374
this._updateDrawingQueue(["lineTo", x, y + h]);
375
this._updateDrawingQueue(["lineTo", x, y]);
376
this._trackSize(x - wt, y - wt);
377
this._trackSize(x + w + wt, y + h + wt);
382
* Draws a rectangle with rounded corners.
385
* @param {Number} x x-coordinate
386
* @param {Number} y y-coordinate
387
* @param {Number} w width
388
* @param {Number} h height
389
* @param {Number} ew width of the ellipse used to draw the rounded corners
390
* @param {Number} eh height of the ellipse used to draw the rounded corners
392
drawRoundRect: function(x, y, w, h, ew, eh) {
393
var wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0;
394
this._drawingComplete = false;
395
this._updateDrawingQueue(["moveTo", x, y + eh]);
396
this._updateDrawingQueue(["lineTo", x, y + h - eh]);
397
this._updateDrawingQueue(["quadraticCurveTo", x, y + h, x + ew, y + h]);
398
this._updateDrawingQueue(["lineTo", x + w - ew, y + h]);
399
this._updateDrawingQueue(["quadraticCurveTo", x + w, y + h, x + w, y + h - eh]);
400
this._updateDrawingQueue(["lineTo", x + w, y + eh]);
401
this._updateDrawingQueue(["quadraticCurveTo", x + w, y, x + w - ew, y]);
402
this._updateDrawingQueue(["lineTo", x + ew, y]);
403
this._updateDrawingQueue(["quadraticCurveTo", x, y, x, y + eh]);
404
this._trackSize(x - wt, y - wt);
405
this._trackSize(x + w + wt, y + h + wt);
406
this._updateCoords(w, h);
414
* @param {Number} x x-coordinate of the wedge's center point
415
* @param {Number} y y-coordinate of the wedge's center point
416
* @param {Number} startAngle starting angle in degrees
417
* @param {Number} arc sweep of the wedge. Negative values draw clockwise.
418
* @param {Number} radius radius of wedge. If [optional] yRadius is defined, then radius is the x radius.
419
* @param {Number} yRadius [optional] y radius for wedge.
422
drawWedge: function(x, y, startAngle, arc, radius, yRadius)
436
yRadius = yRadius || radius;
438
this._drawingComplete = false;
439
// move to x,y position
440
this._updateDrawingQueue(["moveTo", x, y]);
442
yRadius = yRadius || radius;
444
// limit sweep to reasonable numbers
445
if(Math.abs(arc) > 360)
450
// First we calculate how many segments are needed
452
segs = Math.ceil(Math.abs(arc) / 45);
454
// Now calculate the sweep of each segment.
455
segAngle = arc / segs;
457
// The math requires radians rather than degrees. To convert from degrees
458
// use the formula (degrees/180)*Math.PI to get radians.
459
theta = -(segAngle / 180) * Math.PI;
461
// convert angle startAngle to radians
462
angle = (startAngle / 180) * Math.PI;
464
// draw the curve in segments no larger than 45 degrees.
467
// draw a line from the center to the start of the curve
468
ax = x + Math.cos(startAngle / 180 * Math.PI) * radius;
469
ay = y + Math.sin(startAngle / 180 * Math.PI) * yRadius;
471
// Loop for drawing curve segments
475
angleMid = angle - (theta / 2);
476
bx = x + Math.cos(angle) * radius;
477
by = y + Math.sin(angle) * yRadius;
478
cx = x + Math.cos(angleMid) * (radius / Math.cos(theta / 2));
479
cy = y + Math.sin(angleMid) * (yRadius / Math.cos(theta / 2));
480
this._updateDrawingQueue(["quadraticCurveTo", cx, cy, bx, by]);
482
// close the wedge by drawing a line to the center
483
this._updateDrawingQueue(["lineTo", x, y]);
485
this._trackSize(0 , 0);
486
this._trackSize(radius * 2, radius * 2);
491
* Completes a drawing operation.
501
* Ends a fill and stroke
505
closePath: function()
507
this._updateDrawingQueue(["closePath"]);
508
this._updateDrawingQueue(["beginPath"]);
512
* Clears the graphics object.
518
* Returns a linear gradient fill
520
* @method _getLinearGradient
521
* @return CanvasGradient
524
_getLinearGradient: function() {
525
var isNumber = Y.Lang.isNumber,
526
fill = this.get("fill"),
536
w = this.get("width"),
537
h = this.get("height"),
538
r = fill.rotation || 0,
543
radCon = Math.PI/180,
544
tanRadians = parseFloat(parseFloat(Math.tan(r * radCon)).toFixed(8));
545
if(Math.abs(tanRadians) * w/2 >= h/2)
557
x1 = cx - ((cy - y1)/tanRadians);
558
x2 = cx - ((cy - y2)/tanRadians);
562
if(r > 90 && r < 270)
572
y1 = ((tanRadians * (cx - x1)) - cy) * -1;
573
y2 = ((tanRadians * (cx - x2)) - cy) * -1;
575
gradient = this._context.createLinearGradient(x1, y1, x2, y2);
579
opacity = stop.opacity;
581
offset = stop.offset;
582
if(isNumber(opacity))
584
opacity = Math.max(0, Math.min(1, opacity));
585
color = this._toRGBA(color, opacity);
589
color = TORGB(color);
591
offset = stop.offset || i/(len - 1);
592
gradient.addColorStop(offset, color);
598
* Returns a radial gradient fill
600
* @method _getRadialGradient
601
* @return CanvasGradient
604
_getRadialGradient: function() {
605
var isNumber = Y.Lang.isNumber,
606
fill = this.get("fill"),
619
w = this.get("width"),
620
h = this.get("height"),
633
d = Math.sqrt( Math.pow(Math.abs(xc - x1), 2) + Math.pow(Math.abs(yc - y1), 2) );
637
//hack. gradient won't show if it is exactly on the edge of the arc
642
xn = (x1 - xc)/ratio;
643
yn = (y1 - yc)/ratio;
644
xn = xn > 0 ? Math.floor(xn) : Math.ceil(xn);
645
yn = yn > 0 ? Math.floor(yn) : Math.ceil(yn);
650
//If the gradient radius is greater than the circle's, adjusting the radius stretches the gradient properly.
651
//If the gradient radius is less than the circle's, adjusting the radius of the gradient will not work.
652
//Instead, adjust the color stops to reflect the smaller radius.
655
gradient = this._context.createRadialGradient(x1, y1, r, x2, y2, r * w);
660
gradient = this._context.createRadialGradient(x1, y1, r, x2, y2, w/2);
661
stopMultiplier = r * 2;
666
opacity = stop.opacity;
668
offset = stop.offset;
669
if(isNumber(opacity))
671
opacity = Math.max(0, Math.min(1, opacity));
672
color = this._toRGBA(color, opacity);
676
color = TORGB(color);
678
offset = stop.offset || i/(len - 1);
679
offset *= stopMultiplier;
682
gradient.addColorStop(offset, color);
695
_initProps: function() {
697
this._lineToMethods = [];
709
* Indicates a drawing has completed.
711
* @property _drawingComplete
715
_drawingComplete: false,
718
* Creates canvas element
720
* @method _createGraphic
721
* @return HTMLCanvasElement
724
_createGraphic: function(config) {
725
var graphic = Y.config.doc.createElement('canvas');
730
* Updates the size of the graphics object
733
* @param {Number} w width
734
* @param {Number} h height
737
_trackSize: function(w, h) {
738
if (w > this._right) {
749
if (h > this._bottom)
753
this._width = this._right - this._left;
754
this._height = this._bottom - this._top;
757
Y.CanvasDrawing = CanvasDrawing;
759
* <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> implementation of the <a href="Shape.html">`Shape`</a> class.
760
* `CanvasShape` is not intended to be used directly. Instead, use the <a href="Shape.html">`Shape`</a> class.
761
* If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities but has
762
* <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> capabilities, the <a href="Shape.html">`Shape`</a>
763
* class will point to the `CanvasShape` class.
769
CanvasShape = function(cfg)
771
this._transforms = [];
772
this.matrix = new Y.Matrix();
773
CanvasShape.superclass.constructor.apply(this, arguments);
776
CanvasShape.NAME = "canvasShape";
778
Y.extend(CanvasShape, Y.GraphicBase, Y.mix({
780
* Init method, invoked during construction.
781
* Calls `initializer` method.
788
this.initializer.apply(this, arguments);
792
* Initializes the shape
795
* @method _initialize
797
initializer: function(cfg)
800
graphic = cfg.graphic;
807
this._setGraphic(graphic);
809
host._updateHandler();
813
* Set the Graphic instance for the shape.
815
* @method _setGraphic
816
* @param {Graphic | Node | HTMLElement | String} render This param is used to determine the graphic instance. If it is a `Graphic` instance, it will be assigned
817
* to the `graphic` attribute. Otherwise, a new Graphic instance will be created and rendered into the dom element that the render represents.
820
_setGraphic: function(render)
823
if(render instanceof Y.CanvasGraphic)
825
this._graphic = render;
829
render = Y.one(render);
830
graphic = new Y.CanvasGraphic({
833
graphic._appendShape(this);
834
this._graphic = graphic;
839
* Add a class name to each node.
842
* @param {String} className the class name to add to the node's class attribute
844
addClass: function(className)
846
var node = Y.one(this.get("node"));
847
node.addClass(className);
851
* Removes a class name from each node.
853
* @method removeClass
854
* @param {String} className the class name to remove from the node's class attribute
856
removeClass: function(className)
858
var node = Y.one(this.get("node"));
859
node.removeClass(className);
863
* Gets the current position of the node in page coordinates.
866
* @return Array The XY position of the shape.
870
var graphic = this.get("graphic"),
871
parentXY = graphic.getXY(),
874
return [parentXY[0] + x, parentXY[1] + y];
878
* Set the position of the shape in page coordinates, regardless of how the node is positioned.
881
* @param {Array} Contains X & Y values for new position (coordinates are page-based)
885
var graphic = this.get("graphic"),
886
parentXY = graphic.getXY(),
887
x = xy[0] - parentXY[0],
888
y = xy[1] - parentXY[1];
891
this._updateNodePosition(x, y);
895
* Determines whether the node is an ancestor of another HTML element in the DOM hierarchy.
898
* @param {CanvasShape | HTMLElement} needle The possible node or descendent
899
* @return Boolean Whether or not this shape is the needle or its ancestor.
901
contains: function(needle)
903
return needle === Y.one(this.node);
907
* Test if the supplied node matches the supplied selector.
910
* @param {String} selector The CSS selector to test against.
911
* @return Boolean Wheter or not the shape matches the selector.
913
test: function(selector)
915
return Y.one(this.get("node")).test(selector);
916
//return Y.Selector.test(this.node, selector);
920
* Compares nodes to determine if they match.
921
* Node instances can be compared to each other and/or HTMLElements.
923
* @param {HTMLElement | Node} refNode The reference node to compare to the node.
924
* @return {Boolean} True if the nodes match, false if they do not.
926
compareTo: function(refNode) {
927
var node = this.node;
928
return node === refNode;
932
* Value function for fill attribute
934
* @method _getDefaultFill
938
_getDefaultFill: function() {
950
* Value function for stroke attribute
952
* @method _getDefaultStroke
956
_getDefaultStroke: function()
967
* Left edge of the path
976
* Right edge of the path
985
* Top edge of the path
994
* Bottom edge of the path
1003
* Creates the dom node for the shape.
1005
* @method createNode
1006
* @return HTMLElement
1009
createNode: function()
1011
var node = Y.config.doc.createElement('canvas'),
1012
id = this.get("id");
1013
this._context = node.getContext('2d');
1014
node.setAttribute("overflow", "visible");
1015
node.style.overflow = "visible";
1016
if(!this.get("visible"))
1018
node.style.visibility = "hidden";
1020
node.setAttribute("id", id);
1023
this.addClass("yui3-" + SHAPE + " yui3-" + this.name);
1027
* Overrides default `on` method. Checks to see if its a dom interaction event. If so,
1028
* return an event attached to the `node` element. If not, return the normal functionality.
1031
* @param {String} type event type
1032
* @param {Object} callback function
1035
on: function(type, fn)
1037
if(Y.Node.DOM_EVENTS[type])
1039
return Y.one("#" + this.get("id")).on(type, fn);
1041
return Y.on.apply(this, arguments);
1045
* Adds a stroke to the shape node.
1047
* @method _strokeChangeHandler
1048
* @param {Object} stroke Properties of the `stroke` attribute.
1051
_setStrokeProps: function(stroke)
1053
var color = stroke.color,
1054
weight = PARSE_FLOAT(stroke.weight),
1055
opacity = PARSE_FLOAT(stroke.opacity),
1056
linejoin = stroke.linejoin || "round",
1057
linecap = stroke.linecap || "butt",
1058
dashstyle = stroke.dashstyle;
1059
this._miterlimit = null;
1060
this._dashstyle = (dashstyle && Y.Lang.isArray(dashstyle) && dashstyle.length > 1) ? dashstyle : null;
1061
this._strokeWeight = weight;
1063
if (IS_NUMBER(weight) && weight > 0)
1071
if (IS_NUMBER(opacity)) {
1072
this._strokeStyle = this._toRGBA(color, opacity);
1076
this._strokeStyle = color;
1078
this._linecap = linecap;
1079
if(linejoin == "round" || linejoin == "square")
1081
this._linejoin = linejoin;
1085
linejoin = parseInt(linejoin, 10);
1086
if(IS_NUMBER(linejoin))
1088
this._miterlimit = Math.max(linejoin, 1);
1089
this._linejoin = "miter";
1095
* Sets the value of an attribute.
1098
* @param {String|Object} name The name of the attribute. Alternatively, an object of key value pairs can
1099
* be passed in to set multiple attributes at once.
1100
* @param {Any} value The value to set the attribute to. This value is ignored if an object is received as
1107
AttributeLite.prototype.set.apply(host, arguments);
1108
if(host.initialized)
1110
host._updateHandler();
1115
* Adds a fill to the shape node.
1117
* @method _setFillProps
1118
* @param {Object} fill Properties of the `fill` attribute.
1121
_setFillProps: function(fill)
1123
var isNumber = IS_NUMBER,
1127
if(type == "linear" || type == "radial")
1129
this._fillType = type;
1133
opacity = fill.opacity;
1134
if (isNumber(opacity))
1136
opacity = Math.max(0, Math.min(1, opacity));
1137
color = this._toRGBA(color, opacity);
1141
color = TORGB(color);
1144
this._fillColor = color;
1145
this._fillType = 'solid';
1149
this._fillColor = null;
1154
* Specifies a 2d translation.
1157
* @param {Number} x The value to transate on the x-axis.
1158
* @param {Number} y The value to translate on the y-axis.
1160
translate: function(x, y)
1162
this._translateX += x;
1163
this._translateY += y;
1164
this._addTransform("translate", arguments);
1168
* Translates the shape along the x-axis. When translating x and y coordinates,
1169
* use the `translate` method.
1171
* @method translateX
1172
* @param {Number} x The value to translate.
1174
translateX: function(x)
1176
this._translateX += x;
1177
this._addTransform("translateX", arguments);
1181
* Performs a translate on the y-coordinate. When translating x and y coordinates,
1182
* use the `translate` method.
1184
* @method translateY
1185
* @param {Number} y The value to translate.
1187
translateY: function(y)
1189
this._translateY += y;
1190
this._addTransform("translateY", arguments);
1194
* Skews the shape around the x-axis and y-axis.
1197
* @param {Number} x The value to skew on the x-axis.
1198
* @param {Number} y The value to skew on the y-axis.
1200
skew: function(x, y)
1202
this._addTransform("skew", arguments);
1206
* Skews the shape around the x-axis.
1209
* @param {Number} x x-coordinate
1213
this._addTransform("skewX", arguments);
1217
* Skews the shape around the y-axis.
1220
* @param {Number} y y-coordinate
1224
this._addTransform("skewY", arguments);
1228
* Rotates the shape clockwise around it transformOrigin.
1231
* @param {Number} deg The degree of the rotation.
1233
rotate: function(deg)
1235
this._rotation = deg;
1236
this._addTransform("rotate", arguments);
1240
* Specifies a 2d scaling operation.
1243
* @param {Number} val
1245
scale: function(x, y)
1247
this._addTransform("scale", arguments);
1251
* Storage for `rotation` atribute.
1253
* @property _rotation
1260
* Storage for the transform attribute.
1262
* @property _transform
1269
* Adds a transform to the shape.
1271
* @method _addTransform
1272
* @param {String} type The transform being applied.
1273
* @param {Array} args The arguments for the transform.
1276
_addTransform: function(type, args)
1278
args = Y.Array(args);
1279
this._transform = Y_LANG.trim(this._transform + " " + type + "(" + args.join(", ") + ")");
1281
this._transforms.push(args);
1282
if(this.initialized)
1284
this._updateTransform();
1289
* Applies all transforms.
1291
* @method _updateTransform
1294
_updateTransform: function()
1296
var node = this.node,
1299
transformOrigin = this.get("transformOrigin"),
1300
matrix = this.matrix,
1302
len = this._transforms.length;
1304
if(this._transforms && this._transforms.length > 0)
1308
key = this._transforms[i].shift();
1311
matrix[key].apply(matrix, this._transforms[i]);
1314
transform = matrix.toCSSText();
1317
this._graphic.addToRedrawQueue(this);
1318
transformOrigin = (100 * transformOrigin[0]) + "% " + (100 * transformOrigin[1]) + "%";
1319
node.style.MozTransformOrigin = transformOrigin;
1320
node.style.webkitTransformOrigin = transformOrigin;
1321
node.style.msTransformOrigin = transformOrigin;
1322
node.style.OTransformOrigin = transformOrigin;
1325
node.style.MozTransform = transform;
1326
node.style.webkitTransform = transform;
1327
node.style.msTransform = transform;
1328
node.style.OTransform = transform;
1330
this._transforms = [];
1334
* Updates `Shape` based on attribute changes.
1336
* @method _updateHandler
1339
_updateHandler: function()
1342
this._updateTransform();
1346
* Updates the shape.
1353
var node = this.node;
1356
node.style.left = this.get("x") + "px";
1357
node.style.top = this.get("y") + "px";
1361
* Completes a shape or drawing
1363
* @method _closePath
1366
_closePath: function()
1372
var node = this.get("node"),
1373
w = this._right - this._left,
1374
h = this._bottom - this._top,
1375
context = this._context,
1377
cachedMethods = this._methods.concat(),
1384
this._context.clearRect(0, 0, node.width, node.height);
1387
len = cachedMethods.length;
1394
methods[i] = cachedMethods[i].concat();
1396
argsLen = args[0] == "quadraticCurveTo" ? args.length : 3;
1397
for(j = 1; j < argsLen; ++j)
1401
args[j] = args[j] - this._top;
1405
args[j] = args[j] - this._left;
1409
node.setAttribute("width", Math.min(w, 2000));
1410
node.setAttribute("height", Math.min(2000, h));
1411
context.beginPath();
1412
for(i = 0; i < len; ++i)
1414
args = methods[i].concat();
1415
if(args && args.length > 0)
1417
method = args.shift();
1420
if(method == "closePath")
1422
this._strokeAndFill(context);
1424
if(method && method == "lineTo" && this._dashstyle)
1426
args.unshift(this._xcoords[i] - this._left, this._ycoords[i] - this._top);
1427
this._drawDashedLine.apply(this, args);
1431
context[method].apply(context, args);
1437
this._strokeAndFill(context);
1438
this._drawingComplete = true;
1439
this._clearAndUpdateCoords();
1440
this._updateNodePosition();
1441
this._methods = cachedMethods;
1446
* Completes a stroke and/or fill operation on the context.
1448
* @method _strokeAndFill
1449
* @param {Context} Reference to the context element of the canvas instance.
1452
_strokeAndFill: function(context)
1456
if(this._fillType == "linear")
1458
context.fillStyle = this._getLinearGradient();
1460
else if(this._fillType == "radial")
1462
context.fillStyle = this._getRadialGradient();
1466
context.fillStyle = this._fillColor;
1468
context.closePath();
1473
if(this._strokeWeight)
1475
context.lineWidth = this._strokeWeight;
1477
context.lineCap = this._linecap;
1478
context.lineJoin = this._linejoin;
1479
if(this._miterlimit)
1481
context.miterLimit = this._miterlimit;
1483
context.strokeStyle = this._strokeStyle;
1489
* Draws a dashed line between two points.
1491
* @method _drawDashedLine
1492
* @param {Number} xStart The x position of the start of the line
1493
* @param {Number} yStart The y position of the start of the line
1494
* @param {Number} xEnd The x position of the end of the line
1495
* @param {Number} yEnd The y position of the end of the line
1498
_drawDashedLine: function(xStart, yStart, xEnd, yEnd)
1500
var context = this._context,
1501
dashsize = this._dashstyle[0],
1502
gapsize = this._dashstyle[1],
1503
segmentLength = dashsize + gapsize,
1504
xDelta = xEnd - xStart,
1505
yDelta = yEnd - yStart,
1506
delta = Math.sqrt(Math.pow(xDelta, 2) + Math.pow(yDelta, 2)),
1507
segmentCount = Math.floor(Math.abs(delta / segmentLength)),
1508
radians = Math.atan2(yDelta, xDelta),
1512
xDelta = Math.cos(radians) * segmentLength;
1513
yDelta = Math.sin(radians) * segmentLength;
1515
for(i = 0; i < segmentCount; ++i)
1517
context.moveTo(xCurrent, yCurrent);
1518
context.lineTo(xCurrent + Math.cos(radians) * dashsize, yCurrent + Math.sin(radians) * dashsize);
1523
context.moveTo(xCurrent, yCurrent);
1524
delta = Math.sqrt((xEnd - xCurrent) * (xEnd - xCurrent) + (yEnd - yCurrent) * (yEnd - yCurrent));
1526
if(delta > dashsize)
1528
context.lineTo(xCurrent + Math.cos(radians) * dashsize, yCurrent + Math.sin(radians) * dashsize);
1532
context.lineTo(xCurrent + Math.cos(radians) * delta, yCurrent + Math.sin(radians) * delta);
1535
context.moveTo(xEnd, yEnd);
1538
//This should move to CanvasDrawing class.
1539
//Currently docmented in CanvasDrawing class.
1544
this._context.clearRect(0, 0, this.node.width, this.node.height);
1550
* Returns the bounds for a shape.
1552
* Calculates the a new bounding box from the original corner coordinates (base on size and position) and the transform matrix.
1553
* The calculated bounding box is used by the graphic instance to calculate its viewBox.
1558
getBounds: function()
1560
var stroke = this.get("stroke"),
1561
w = this.get("width"),
1562
h = this.get("height"),
1566
if(stroke && stroke.weight)
1570
w = (x + w + wt) - (x - wt);
1571
h = (y + h + wt) - (y - wt);
1574
return this.matrix.getContentRect(w, h, x, y);
1578
* Destroys the shape instance.
1584
var graphic = this.get("graphic");
1587
graphic.removeShape(this);
1596
* Implementation for shape destruction
1601
_destroy: function()
1605
Y.one(this.node).remove(true);
1606
this._context = null;
1610
}, Y.CanvasDrawing.prototype));
1612
CanvasShape.ATTRS = {
1614
* An array of x, y values which indicates the transformOrigin in which to rotate the shape. Valid values range between 0 and 1 representing a
1615
* fraction of the shape's corresponding bounding box dimension. The default value is [0.5, 0.5].
1617
* @config transformOrigin
1628
* <p>A string containing, in order, transform operations applied to the shape instance. The `transform` string can contain the following values:
1631
* <dt>rotate</dt><dd>Rotates the shape clockwise around it transformOrigin.</dd>
1632
* <dt>translate</dt><dd>Specifies a 2d translation.</dd>
1633
* <dt>skew</dt><dd>Skews the shape around the x-axis and y-axis.</dd>
1634
* <dt>scale</dt><dd>Specifies a 2d scaling operation.</dd>
1635
* <dt>translateX</dt><dd>Translates the shape along the x-axis.</dd>
1636
* <dt>translateY</dt><dd>Translates the shape along the y-axis.</dd>
1637
* <dt>skewX</dt><dd>Skews the shape around the x-axis.</dd>
1638
* <dt>skewY</dt><dd>Skews the shape around the y-axis.</dd>
1639
* <dt>matrix</dt><dd>Specifies a 2D transformation matrix comprised of the specified six values.</dd>
1642
* <p>Applying transforms through the transform attribute will reset the transform matrix and apply a new transform. The shape class also contains corresponding methods for each transform
1643
* that will apply the transform to the current matrix. The below code illustrates how you might use the `transform` attribute to instantiate a recangle with a rotation of 45 degrees.</p>
1644
var myRect = new Y.Rect({
1648
transform: "rotate(45)"
1650
* <p>The code below would apply `translate` and `rotate` to an existing shape.</p>
1652
myRect.set("transform", "translate(40, 50) rotate(45)");
1657
setter: function(val)
1660
this._transforms = this.matrix.getTransformArray(val);
1661
this._transform = val;
1667
return this._transform;
1672
* Dom node for the shape
1688
* Unique id for class instance.
1699
setter: function(val)
1701
var node = this.node;
1704
node.setAttribute("id", val);
1711
* Indicates the width of the shape
1721
* Indicates the height of the shape
1731
* Indicates the x position of shape.
1741
* Indicates the y position of shape.
1751
* Indicates whether the shape is visible.
1759
setter: function(val){
1760
var node = this.get("node"),
1761
visibility = val ? "visible" : "hidden";
1764
node.style.visibility = visibility;
1771
* Contains information about the fill of the shape.
1773
* <dt>color</dt><dd>The color of the fill.</dd>
1774
* <dt>opacity</dt><dd>Number between 0 and 1 that indicates the opacity of the fill. The default value is 1.</dd>
1775
* <dt>type</dt><dd>Type of fill.
1777
* <dt>solid</dt><dd>Solid single color fill. (default)</dd>
1778
* <dt>linear</dt><dd>Linear gradient fill.</dd>
1779
* <dt>radial</dt><dd>Radial gradient fill.</dd>
1783
* <p>If a `linear` or `radial` is specified as the fill type. The following additional property is used:
1785
* <dt>stops</dt><dd>An array of objects containing the following properties:
1787
* <dt>color</dt><dd>The color of the stop.</dd>
1788
* <dt>opacity</dt><dd>Number between 0 and 1 that indicates the opacity of the stop. The default value is 1. Note: No effect for IE 6 - 8</dd>
1789
* <dt>offset</dt><dd>Number between 0 and 1 indicating where the color stop is positioned.</dd>
1792
* <p>Linear gradients also have the following property:</p>
1793
* <dt>rotation</dt><dd>Linear gradients flow left to right by default. The rotation property allows you to change the flow by rotation. (e.g. A rotation of 180 would make the gradient pain from right to left.)</dd>
1794
* <p>Radial gradients have the following additional properties:</p>
1795
* <dt>r</dt><dd>Radius of the gradient circle.</dd>
1796
* <dt>fx</dt><dd>Focal point x-coordinate of the gradient.</dd>
1797
* <dt>fy</dt><dd>Focal point y-coordinate of the gradient.</dd>
1799
* <p>The corresponding `SVGShape` class implements the following additional properties.</p>
1802
* <p>The x-coordinate of the center of the gradient circle. Determines where the color stop begins. The default value 0.5.</p>
1805
* <p>The y-coordinate of the center of the gradient circle. Determines where the color stop begins. The default value 0.5.</p>
1808
* <p>These properties are not currently implemented in `CanvasShape` or `VMLShape`.</p>
1814
valueFn: "_getDefaultFill",
1816
setter: function(val)
1819
tmpl = this.get("fill") || this._getDefaultFill();
1820
fill = (val) ? Y.merge(tmpl, val) : null;
1821
if(fill && fill.color)
1823
if(fill.color === undefined || fill.color == "none")
1828
this._setFillProps(fill);
1834
* Contains information about the stroke of the shape.
1836
* <dt>color</dt><dd>The color of the stroke.</dd>
1837
* <dt>weight</dt><dd>Number that indicates the width of the stroke.</dd>
1838
* <dt>opacity</dt><dd>Number between 0 and 1 that indicates the opacity of the stroke. The default value is 1.</dd>
1839
* <dt>dashstyle</dt>Indicates whether to draw a dashed stroke. When set to "none", a solid stroke is drawn. When set to an array, the first index indicates the
1840
* length of the dash. The second index indicates the length of gap.
1841
* <dt>linecap</dt><dd>Specifies the linecap for the stroke. The following values can be specified:
1843
* <dt>butt (default)</dt><dd>Specifies a butt linecap.</dd>
1844
* <dt>square</dt><dd>Specifies a sqare linecap.</dd>
1845
* <dt>round</dt><dd>Specifies a round linecap.</dd>
1848
* <dt>linejoin</dt><dd>Specifies a linejoin for the stroke. The following values can be specified:
1850
* <dt>round (default)</dt><dd>Specifies that the linejoin will be round.</dd>
1851
* <dt>bevel</dt><dd>Specifies a bevel for the linejoin.</dd>
1852
* <dt>miter limit</dt><dd>An integer specifying the miter limit of a miter linejoin. If you want to specify a linejoin of miter, you simply specify the limit as opposed to having
1853
* separate miter and miter limit values.</dd>
1862
valueFn: "_getDefaultStroke",
1864
setter: function(val)
1866
var tmpl = this.get("stroke") || this._getDefaultStroke(),
1868
if(val && val.hasOwnProperty("weight"))
1870
wt = parseInt(val.weight, 10);
1876
val = (val) ? Y.merge(tmpl, val) : null;
1877
this._setStrokeProps(val);
1882
//Not used. Remove in future.
1887
// Only implemented in SVG
1888
// Determines whether the instance will receive mouse events.
1890
// @config pointerEvents
1894
value: "visiblePainted"
1898
* Reference to the container Graphic.
1908
return this._graphic;
1912
Y.CanvasShape = CanvasShape;
1914
* <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> implementation of the <a href="Path.html">`Path`</a> class.
1915
* `CanvasPath` is not intended to be used directly. Instead, use the <a href="Path.html">`Path`</a> class.
1916
* If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities but has
1917
* <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> capabilities, the <a href="Path.html">`Path`</a>
1918
* class will point to the `CanvasPath` class.
1922
* @extends CanvasShape
1924
CanvasPath = function(cfg)
1926
CanvasPath.superclass.constructor.apply(this, arguments);
1928
CanvasPath.NAME = "canvasPath";
1929
Y.extend(CanvasPath, Y.CanvasShape, {
1931
* Indicates the type of shape
1951
* Creates the dom node for the shape.
1953
* @method createNode
1954
* @return HTMLElement
1957
createNode: function()
1959
var node = Y.config.doc.createElement('canvas'),
1960
id = this.get("id");
1961
this._context = node.getContext('2d');
1962
node.setAttribute("overflow", "visible");
1963
node.setAttribute("pointer-events", "none");
1964
node.style.pointerEvents = "none";
1965
node.style.overflow = "visible";
1966
node.setAttribute("id", id);
1969
this.addClass("yui3-" + SHAPE + " yui3-" + this.name);
1973
* Completes a drawing operation.
1983
CanvasPath.ATTRS = Y.merge(Y.CanvasShape.ATTRS, {
1985
* Indicates the width of the shape
1993
var offset = this._stroke && this._strokeWeight ? (this._strokeWeight * 2) : 0;
1994
return this._width - offset;
1997
setter: function(val)
2005
* Indicates the height of the shape
2013
var offset = this._stroke && this._strokeWeight ? (this._strokeWeight * 2) : 0;
2014
return this._height - offset;
2017
setter: function(val)
2025
* Indicates the path used for the node.
2040
Y.CanvasPath = CanvasPath;
2042
* <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> implementation of the <a href="Rect.html">`Rect`</a> class.
2043
* `CanvasRect` is not intended to be used directly. Instead, use the <a href="Rect.html">`Rect`</a> class.
2044
* If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities but has
2045
* <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> capabilities, the <a href="Rect.html">`Rect`</a>
2046
* class will point to the `CanvasRect` class.
2052
CanvasRect = function()
2054
CanvasRect.superclass.constructor.apply(this, arguments);
2056
CanvasRect.NAME = "canvasRect";
2057
Y.extend(CanvasRect, Y.CanvasShape, {
2059
* Indicates the type of shape
2075
var w = this.get("width"),
2076
h = this.get("height");
2078
this.drawRect(0, 0, w, h);
2082
CanvasRect.ATTRS = Y.CanvasShape.ATTRS;
2083
Y.CanvasRect = CanvasRect;
2085
* <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> implementation of the <a href="Ellipse.html">`Ellipse`</a> class.
2086
* `CanvasEllipse` is not intended to be used directly. Instead, use the <a href="Ellipse.html">`Ellipse`</a> class.
2087
* If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities but has
2088
* <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> capabilities, the <a href="Ellipse.html">`Ellipse`</a>
2089
* class will point to the `CanvasEllipse` class.
2092
* @class CanvasEllipse
2095
CanvasEllipse = function(cfg)
2097
CanvasEllipse.superclass.constructor.apply(this, arguments);
2100
CanvasEllipse.NAME = "canvasEllipse";
2102
Y.extend(CanvasEllipse, CanvasShape, {
2104
* Indicates the type of shape
2120
var w = this.get("width"),
2121
h = this.get("height");
2123
this.drawEllipse(0, 0, w, h);
2127
CanvasEllipse.ATTRS = CanvasShape.ATTRS;
2128
Y.CanvasEllipse = CanvasEllipse;
2130
* <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> implementation of the <a href="Circle.html">`Circle`</a> class.
2131
* `CanvasCircle` is not intended to be used directly. Instead, use the <a href="Circle.html">`Circle`</a> class.
2132
* If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities but has
2133
* <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> capabilities, the <a href="Circle.html">`Circle`</a>
2134
* class will point to the `CanvasCircle` class.
2137
* @class CanvasCircle
2140
CanvasCircle = function(cfg)
2142
CanvasCircle.superclass.constructor.apply(this, arguments);
2145
CanvasCircle.NAME = "canvasCircle";
2147
Y.extend(CanvasCircle, Y.CanvasShape, {
2149
* Indicates the type of shape
2165
var radius = this.get("radius");
2169
this.drawCircle(0, 0, radius);
2175
CanvasCircle.ATTRS = Y.merge(Y.CanvasShape.ATTRS, {
2177
* Indicates the width of the shape
2183
setter: function(val)
2185
this.set("radius", val/2);
2191
return this.get("radius") * 2;
2196
* Indicates the height of the shape
2202
setter: function(val)
2204
this.set("radius", val/2);
2210
return this.get("radius") * 2;
2215
* Radius of the circle
2224
Y.CanvasCircle = CanvasCircle;
2229
* @class CanvasPieSlice
2232
CanvasPieSlice = function()
2234
CanvasPieSlice.superclass.constructor.apply(this, arguments);
2236
CanvasPieSlice.NAME = "canvasPieSlice";
2237
Y.extend(CanvasPieSlice, Y.CanvasShape, {
2239
* Indicates the type of shape
2248
* Change event listener
2251
* @method _updateHandler
2255
var x = this.get("cx"),
2257
startAngle = this.get("startAngle"),
2258
arc = this.get("arc"),
2259
radius = this.get("radius");
2262
this._right = radius;
2264
this._bottom = radius;
2265
this.drawWedge(x, y, startAngle, arc, radius);
2269
CanvasPieSlice.ATTRS = Y.mix({
2278
* Starting angle in relation to a circle in which to begin the pie slice drawing.
2280
* @config startAngle
2298
* Radius of the circle in which the pie slice is drawn
2306
}, Y.CanvasShape.ATTRS);
2307
Y.CanvasPieSlice = CanvasPieSlice;
2309
* <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> implementation of the `Graphic` class.
2310
* `CanvasGraphic` is not intended to be used directly. Instead, use the <a href="Graphic.html">`Graphic`</a> class.
2311
* If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities but has
2312
* <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> capabilities, the <a href="Graphic.html">`Graphic`</a>
2313
* class will point to the `CanvasGraphic` class.
2316
* @class CanvasGraphic
2319
function CanvasGraphic(config) {
2321
CanvasGraphic.superclass.constructor.apply(this, arguments);
2324
CanvasGraphic.NAME = "canvasGraphic";
2326
CanvasGraphic.ATTRS = {
2328
* Whether or not to render the `Graphic` automatically after to a specified parent node after init. This can be a Node instance or a CSS selector string.
2331
* @type Node | String
2336
* Unique id for class instance.
2347
setter: function(val)
2349
var node = this._node;
2352
node.setAttribute("id", val);
2359
* Key value pairs in which a shape instance is associated with its id.
2370
return this._shapes;
2375
* Object containing size and coordinate data for the content of a Graphic in relation to the graphic instance's position.
2377
* @config contentBounds
2386
return this._contentBounds;
2391
* The outermost html element of the Graphic instance.
2407
* Indicates the width of the `Graphic`.
2413
setter: function(val)
2417
this._node.style.width = val + "px";
2424
* Indicates the height of the `Graphic`.
2430
setter: function(val)
2434
this._node.style.height = val + "px";
2441
* Determines how the size of instance is calculated. If true, the width and height are determined by the size of the contents.
2442
* If false, the width and height values are either explicitly set or determined by the size of the parent node's dimensions.
2453
* The contentBounds will resize to greater values but not smaller values. (for performance)
2454
* When resizing the contentBounds down is desirable, set the resizeDown value to true.
2456
* @config resizeDown
2462
return this._resizeDown;
2465
setter: function(val)
2467
this._resizeDown = val;
2477
* Indicates the x-coordinate for the instance.
2488
setter: function(val)
2493
this._node.style.left = val + "px";
2500
* Indicates the y-coordinate for the instance.
2511
setter: function(val)
2516
this._node.style.top = val + "px";
2523
* Indicates whether or not the instance will automatically redraw after a change is made to a shape.
2524
* This property will get set to false when batching operations.
2536
* Indicates whether the `Graphic` and its children are visible.
2544
setter: function(val)
2546
this._toggleVisible(val);
2552
Y.extend(CanvasGraphic, Y.GraphicBase, {
2554
* Storage for `x` attribute.
2563
* Storage for `y` attribute.
2572
* Gets the current position of the graphic instance in page coordinates.
2575
* @return Array The XY position of the shape.
2579
var node = Y.one(this._node),
2589
* Storage for `resizeDown` attribute.
2591
* @property _resizeDown
2598
* Initializes the class.
2600
* @method initializer
2601
* @param {Object} config Optional attributes
2604
initializer: function(config) {
2605
var render = this.get("render"),
2606
w = this.get("width") || 0,
2607
h = this.get("height") || 0;
2609
this._redrawQueue = {};
2610
this._contentBounds = {
2616
this._node = DOCUMENT.createElement('div');
2617
this._node.style.position = "absolute";
2618
this.set("width", w);
2619
this.set("height", h);
2622
this.render(render);
2627
* Adds the graphics node to the dom.
2630
* @param {HTMLElement} parentNode node in which to render the graphics node into.
2632
render: function(render) {
2633
var parentNode = Y.one(render),
2635
w = this.get("width") || parseInt(parentNode.getComputedStyle("width"), 10),
2636
h = this.get("height") || parseInt(parentNode.getComputedStyle("height"), 10);
2637
parentNode = parentNode || DOCUMENT.body;
2638
parentNode.appendChild(node);
2639
node.style.display = "block";
2640
node.style.position = "absolute";
2641
node.style.left = "0px";
2642
node.style.top = "0px";
2643
this.set("width", w);
2644
this.set("height", h);
2645
this.parentNode = parentNode;
2650
* Removes all nodes.
2656
this.removeAllShapes();
2659
this._removeChildren(this._node);
2660
Y.one(this._node).destroy();
2665
* Generates a shape instance by type.
2668
* @param {Object} cfg attributes for the shape
2671
addShape: function(cfg)
2674
var shapeClass = this._getShapeClass(cfg.type),
2675
shape = new shapeClass(cfg);
2676
this._appendShape(shape);
2681
* Adds a shape instance to the graphic instance.
2683
* @method _appendShape
2684
* @param {Shape} shape The shape instance to be added to the graphic.
2687
_appendShape: function(shape)
2689
var node = shape.node,
2690
parentNode = this._frag || this._node;
2691
if(this.get("autoDraw"))
2693
parentNode.appendChild(node);
2697
this._getDocFrag().appendChild(node);
2702
* Removes a shape instance from from the graphic instance.
2704
* @method removeShape
2705
* @param {Shape|String} shape The instance or id of the shape to be removed.
2707
removeShape: function(shape)
2709
if(!(shape instanceof CanvasShape))
2711
if(Y_LANG.isString(shape))
2713
shape = this._shapes[shape];
2716
if(shape && shape instanceof CanvasShape)
2719
delete this._shapes[shape.get("id")];
2721
if(this.get("autoDraw"))
2729
* Removes all shape instances from the dom.
2731
* @method removeAllShapes
2733
removeAllShapes: function()
2735
var shapes = this._shapes,
2739
if(shapes.hasOwnProperty(i))
2741
shapes[i].destroy();
2748
* Removes all child nodes.
2750
* @method _removeChildren
2751
* @param {HTMLElement} node
2754
_removeChildren: function(node)
2756
if(node && node.hasChildNodes())
2759
while(node.firstChild)
2761
child = node.firstChild;
2762
this._removeChildren(child);
2763
node.removeChild(child);
2769
* Toggles visibility
2771
* @method _toggleVisible
2772
* @param {Boolean} val indicates visibilitye
2775
_toggleVisible: function(val)
2778
shapes = this._shapes,
2779
visibility = val ? "visible" : "hidden";
2784
if(shapes.hasOwnProperty(i))
2786
shapes[i].set("visible", val);
2790
this._node.style.visibility = visibility;
2794
* Returns a shape class. Used by `addShape`.
2796
* @method _getShapeClass
2797
* @param {Shape | String} val Indicates which shape class.
2801
_getShapeClass: function(val)
2803
var shape = this._shapeClass[val];
2812
* Look up for shape classes. Used by `addShape` to retrieve a class for instantiation.
2814
* @property _shapeClass
2819
circle: Y.CanvasCircle,
2822
ellipse: Y.CanvasEllipse,
2823
pieslice: Y.CanvasPieSlice
2827
* Returns a shape based on the id of its dom node.
2829
* @method getShapeById
2830
* @param {String} id Dom id of the shape's node attribute.
2833
getShapeById: function(id)
2835
var shape = this._shapes[id];
2840
* Allows for creating multiple shapes in order to batch appending and redraw operations.
2843
* @param {Function} method Method to execute.
2845
batch: function(method)
2847
var autoDraw = this.get("autoDraw");
2848
this.set("autoDraw", false);
2851
this.set("autoDraw", autoDraw);
2855
* Returns a document fragment to for attaching shapes.
2857
* @method _getDocFrag
2858
* @return DocumentFragment
2861
_getDocFrag: function()
2865
this._frag = DOCUMENT.createDocumentFragment();
2871
* Redraws all shapes.
2878
var box = this.get("resizeDown") ? this._getUpdatedContentBounds() : this._contentBounds;
2879
if(this.get("autoSize"))
2881
this.set("width", box.right);
2882
this.set("height", box.bottom);
2886
this._node.appendChild(this._frag);
2892
* Adds a shape to the redraw queue and calculates the contentBounds. Used internally
2893
* by `Shape` instances.
2895
* @method addToRedrawQueue
2896
* @param Shape shape The shape instance to add to the queue
2899
addToRedrawQueue: function(shape)
2903
this._shapes[shape.get("id")] = shape;
2904
if(!this.get("resizeDown"))
2906
shapeBox = shape.getBounds();
2907
box = this._contentBounds;
2908
box.left = box.left < shapeBox.left ? box.left : shapeBox.left;
2909
box.top = box.top < shapeBox.top ? box.top : shapeBox.top;
2910
box.right = box.right > shapeBox.right ? box.right : shapeBox.right;
2911
box.bottom = box.bottom > shapeBox.bottom ? box.bottom : shapeBox.bottom;
2912
box.width = box.right - box.left;
2913
box.height = box.bottom - box.top;
2914
this._contentBounds = box;
2916
if(this.get("autoDraw"))
2923
* Recalculates and returns the `contentBounds` for the `Graphic` instance.
2925
* @method _getUpdatedContentBounds
2929
_getUpdatedContentBounds: function()
2934
queue = this._shapes,
2943
if(queue.hasOwnProperty(i))
2946
bounds = shape.getBounds();
2947
box.left = Math.min(box.left, bounds.left);
2948
box.top = Math.min(box.top, bounds.top);
2949
box.right = Math.max(box.right, bounds.right);
2950
box.bottom = Math.max(box.bottom, bounds.bottom);
2953
box.width = box.right - box.left;
2954
box.height = box.bottom - box.top;
2955
this._contentBounds = box;
2960
Y.CanvasGraphic = CanvasGraphic;
2963
}, '3.5.1' ,{skinnable:false, requires:['graphics']});