1 /** 2 * Hilo 3 * Copyright 2015 alibaba.com 4 * Licensed under the MIT License 5 */ 6 7 /** 8 * @language=en 9 * <iframe src='../../../examples/Graphics.html?noHeader' width = '320' height = '400' scrolling='no'></iframe> 10 * <br/> 11 * @class Graphics class contains a group of functions for creating vector graphics. 12 * @augments View 13 * @mixes CacheMixin 14 * @param {Object} properties Properties parameters of the object to create. Contains all writable properties of this class. 15 * @module hilo/view/Graphics 16 * @requires hilo/core/Hilo 17 * @requires hilo/core/Class 18 * @requires hilo/view/View 19 * @requires hilo/view/CacheMixin 20 * @property {Number} lineWidth The thickness of lines in space units, default value is 1, readonly! 21 * @property {Number} lineAlpha The alpha value (transparency) of line, default value is 1, readonly! 22 * @property {String} lineCap The style of how every end point of line are drawn, value options: butt, round, square. default value is null, readonly! 23 * @property {String} lineJoin The joint style of two lines. value options: miter, round, bevel. default value is null, readonly! 24 * @property {Number} miterLimit The miter limit ratio in space units, works only when lineJoin value is miter. default value is 10, readonly! 25 * @property {String} strokeStyle The color or style to use for lines around shapes, default value is 0 (the black color), readonly! 26 * @property {String} fillStyle The color or style to use inside shapes, default value is 0 (the black color), readonly! 27 * @property {Number} fillAlpha The transparency of color or style inside shapes, default value is 0, readonly! 28 */ 29 var Graphics = (function(){ 30 31 var canvas = document.createElement('canvas'); 32 var helpContext = canvas.getContext && canvas.getContext('2d'); 33 34 return Class.create(/** @lends Graphics.prototype */{ 35 Extends: View, 36 Mixes:CacheMixin, 37 constructor: function(properties){ 38 properties = properties || {}; 39 this.id = this.id || properties.id || Hilo.getUid('Graphics'); 40 Graphics.superclass.constructor.call(this, properties); 41 42 this._actions = []; 43 }, 44 45 lineWidth: 1, 46 lineAlpha: 1, 47 lineCap: null, //'butt', 'round', 'square' 48 lineJoin: null, //'miter', 'round', 'bevel' 49 miterLimit: 10, 50 hasStroke: false, 51 strokeStyle: '0', 52 hasFill: false, 53 fillStyle: '0', 54 fillAlpha: 0, 55 56 /** 57 * @language=en 58 * Set the lines style for drawing shapes. 59 * @param {Number} thickness The thickness of lines, default value is 1. 60 * @param {String} lineColor The CSS color value of lines, default value is 0 (the black color). 61 * @param {Number} lineAlpha The transparency of lines, default value is 1 (fully opaque). 62 * @param {String} lineCap The style of how every end point of line are drawn, value options: butt, round, square. default value is null. 63 * @param {String} lineJoin The joint style of two lines. value options: miter, round, bevel. default value is null. 64 * @param {Number} miterLimit The miter limit ratio in space units, works only when lineJoin value is miter. default value is 10. 65 * @returns {Graphics} The Graphics Object. 66 */ 67 lineStyle: function(thickness, lineColor, lineAlpha, lineCap, lineJoin, miterLimit){ 68 var me = this, addAction = me._addAction; 69 70 addAction.call(me, ['lineWidth', (me.lineWidth = thickness || 1)]); 71 addAction.call(me, ['strokeStyle', (me.strokeStyle = lineColor || '0')]); 72 addAction.call(me, ['lineAlpha', (me.lineAlpha = lineAlpha || 1)]); 73 if(lineCap != undefined) addAction.call(me, ['lineCap', (me.lineCap = lineCap)]); 74 if(lineJoin != undefined) addAction.call(me, ['lineJoin', (me.lineJoin = lineJoin)]); 75 if(miterLimit != undefined) addAction.call(me, ['miterLimit', (me.miterLimit = miterLimit)]); 76 me.hasStroke = true; 77 return me; 78 }, 79 80 /** 81 * @language=en 82 * Set how to fill shapes and the transparency. 83 * @param {String} fill Filling style. this can be color, gradient or pattern. 84 * @param {Number} alpha Transparency. 85 * @returns {Graphics} The Graphics Object. 86 */ 87 beginFill: function(fill, alpha){ 88 var me = this, addAction = me._addAction; 89 90 addAction.call(me, ['fillStyle', (me.fillStyle = fill)]); 91 addAction.call(me, ['fillAlpha', (me.fillAlpha = alpha || 1)]); 92 me.hasFill = true; 93 return me; 94 }, 95 96 /** 97 * @language=en 98 * Apply and end lines-drawing and shapes-filling. 99 * @returns {Graphics} The Graphics Object. 100 */ 101 endFill: function(){ 102 var me = this, addAction = me._addAction; 103 104 if(me.hasStroke) addAction.call(me, ['stroke']); 105 if(me.hasFill) addAction.call(me, ['fill']); 106 me.setCacheDirty(true); 107 return me; 108 }, 109 110 /** 111 * @language=en 112 * Set linear gradient filling style to draw shapes. 113 * @param {Number} x0 The x-coordinate value of the linear gradient start point. 114 * @param {Number} y0 The y-coordinate value of the linear gradient start point. 115 * @param {Number} x1 The x-coordinate value of the linear gradient end point. 116 * @param {Number} y1 The y-coordinate value of the linear gradient end point. 117 * @param {Array} colors An array of CSS colors used in the linear gradient. 118 * @param {Array} ratios An array of position between start point and end point, should be one-to-one to colors in the colors array. each value range between 0.0 to 1.0. 119 * @returns {Graphics} The Graphics Object. 120 */ 121 beginLinearGradientFill: function(x0, y0, x1, y1, colors, ratios){ 122 var me = this, gradient = helpContext.createLinearGradient(x0, y0, x1, y1); 123 124 for (var i = 0, len = colors.length; i < len; i++){ 125 gradient.addColorStop(ratios[i], colors[i]); 126 } 127 me.hasFill = true; 128 return me._addAction(['fillStyle', (me.fillStyle = gradient)]); 129 }, 130 131 /** 132 * @language=en 133 * Set radial gradient filling style to draw shapes. 134 * @param {Number} x0 The x-coordinate value of the radial gradient start circle. 135 * @param {Number} y0 The y-coordinate value of the radial gradient start circle. 136 * @param {Number} r0 The diameter of the radial gradient start circle. 137 * @param {Number} x1 The x-coordinate value of the radial gradient end circle. 138 * @param {Number} y1 The y-coordinate value of the radial gradient end circle. 139 * @param {Number} r1 The radius of the radial gradient end circle. 140 * @param {Array} colors An array of CSS colors used in the radial gradient. 141 * @param {Array} ratios An array of position between start circle and end circle, should be one-to-one to colors in the colors array. each value range between 0.0 to 1.0. 142 * @returns {Graphics} The Graphics Object. 143 */ 144 beginRadialGradientFill: function(x0, y0, r0, x1, y1, r1, colors, ratios){ 145 var me = this, gradient = helpContext.createRadialGradient(x0, y0, r0, x1, y1, r1); 146 for (var i = 0, len = colors.length; i < len; i++) 147 { 148 gradient.addColorStop(ratios[i], colors[i]); 149 } 150 me.hasFill = true; 151 return me._addAction(['fillStyle', (me.fillStyle = gradient)]); 152 }, 153 154 /** 155 * @language=en 156 * Begin an image filling pattern. 157 * @param {HTMLImageElement} image The Image to fill. 158 * @param {String} repetition The fill repetition style, can be one of valus:repeat, repeat-x, repeat-y, no-repeat. default valus is ''. 159 * @returns {Graphics} The Graphics Object. 160 */ 161 beginBitmapFill: function(image, repetition){ 162 var me = this, pattern = helpContext.createPattern(image, repetition || ''); 163 me.hasFill = true; 164 return me._addAction(['fillStyle', (me.fillStyle = pattern)]); 165 }, 166 167 /** 168 * @language=en 169 * Begin a new path. 170 * @returns {Graphics} The Graphics Object. 171 */ 172 beginPath: function(){ 173 return this._addAction(['beginPath']); 174 }, 175 176 /** 177 * @language=en 178 * Close current path. 179 * @returns {Graphics} The Graphics Object. 180 */ 181 closePath: function(){ 182 return this._addAction(['closePath']); 183 }, 184 185 /** 186 * @language=en 187 * Move current drawing point to a new point on coordinate values (x, y). 188 * @param {Number} x The x-coordinate value. 189 * @param {Number} y The y-coordinate value. 190 * @returns {Graphics} The Graphics Object. 191 */ 192 moveTo: function(x, y){ 193 return this._addAction(['moveTo', x, y]); 194 }, 195 196 /** 197 * @language=en 198 * Draw a line from current point to the point on the coordinate value (x, y). 199 * @param {Number} x The x-coordinate value. 200 * @param {Number} y The y-coordinate value. 201 * @returns {Graphics} The Graphics Object. 202 */ 203 lineTo: function(x, y){ 204 return this._addAction(['lineTo', x, y]); 205 }, 206 207 /** 208 * @language=en 209 * Draw a quadratic Bézier curve from current point to the point on coordinate (x, y). 210 * @param {Number} cpx The x-coordinate value of the Bézier curve control point cp. 211 * @param {Number} cpy The y-coordinate value of the Bézier curve control point cp. 212 * @param {Number} x The x-coordinate value. 213 * @param {Number} y The y-coordinate value. 214 * @returns {Graphics} The Graphics Object. 215 */ 216 quadraticCurveTo: function(cpx, cpy, x, y){ 217 return this._addAction(['quadraticCurveTo', cpx, cpy, x, y]); 218 }, 219 220 /** 221 * @language=en 222 * Draw a Bézier curve from current point to the point on coordinate (x, y). 223 * @param {Number} cp1x The x-coordinate value of the Bézier curve control point cp1. 224 * @param {Number} cp1y The y-coordinate value of the Bézier curve control point cp1. 225 * @param {Number} cp2x The x-coordinate value of the Bézier curve control point cp2. 226 * @param {Number} cp2y The y-coordinate value of the Bézier curve control point cp2. 227 * @param {Number} x The x-coordinate value. 228 * @param {Number} y The y-coordinate value. 229 * @returns {Graphics} The Graphics Object. 230 */ 231 bezierCurveTo: function(cp1x, cp1y, cp2x, cp2y, x, y){ 232 return this._addAction(['bezierCurveTo', cp1x, cp1y, cp2x, cp2y, x, y]); 233 }, 234 235 /** 236 * @language=en 237 * Draw a rectangle. 238 * @param {Number} x The x-coordinate value. 239 * @param {Number} y The y-coordinate value. 240 * @param {Number} width The width of the rectangle. 241 * @param {Number} height The height of the rectangle. 242 * @returns {Graphics} The Graphics Object. 243 */ 244 drawRect: function(x, y, width, height){ 245 return this._addAction(['rect', x, y, width, height]); 246 }, 247 248 /** 249 * @language=en 250 * Draw a complex rounded rectangle. 251 * @param {Number} x The x-coordinate value. 252 * @param {Number} y The y-coordinate value. 253 * @param {Number} width The width of rounded rectangle. 254 * @param {Number} height The height of rounded rectangle. 255 * @param {Number} cornerTL The size of the rounded corner on the top-left of the rounded rectangle. 256 * @param {Number} cornerTR The size of the rounded corner on the top-right of the rounded rectangle. 257 * @param {Number} cornerBR The size of the rounded corner on the bottom-left of the rounded rectangle. 258 * @param {Number} cornerBL The size of the rounded corner on the bottom-right of the rounded rectangle. 259 * @returns {Graphics} The Graphics Object. 260 */ 261 drawRoundRectComplex: function(x, y, width, height, cornerTL, cornerTR, cornerBR, cornerBL){ 262 var me = this, addAction = me._addAction; 263 addAction.call(me, ['moveTo', x + cornerTL, y]); 264 addAction.call(me, ['lineTo', x + width - cornerTR, y]); 265 addAction.call(me, ['arc', x + width - cornerTR, y + cornerTR, cornerTR, -Math.PI/2, 0, false]); 266 addAction.call(me, ['lineTo', x + width, y + height - cornerBR]); 267 addAction.call(me, ['arc', x + width - cornerBR, y + height - cornerBR, cornerBR, 0, Math.PI/2, false]); 268 addAction.call(me, ['lineTo', x + cornerBL, y + height]); 269 addAction.call(me, ['arc', x + cornerBL, y + height - cornerBL, cornerBL, Math.PI/2, Math.PI, false]); 270 addAction.call(me, ['lineTo', x, y + cornerTL]); 271 addAction.call(me, ['arc', x + cornerTL, y + cornerTL, cornerTL, Math.PI, Math.PI*3/2, false]); 272 return me; 273 }, 274 275 /** 276 * @language=en 277 * Draw a rounded rectangle. 278 * @param {Number} x The x-coordinate value. 279 * @param {Number} y The y-coordinate value. 280 * @param {Number} width The width of rounded rectangle. 281 * @param {Number} height The height of rounded rectangle. 282 * @param {Number} cornerSize The size of all rounded corners. 283 * @returns {Graphics} The Graphics Object. 284 */ 285 drawRoundRect: function(x, y, width, height, cornerSize){ 286 return this.drawRoundRectComplex(x, y, width, height, cornerSize, cornerSize, cornerSize, cornerSize); 287 }, 288 289 /** 290 * @language=en 291 * Draw a circle. 292 * @param {Number} x The x-coordinate value. 293 * @param {Number} y The y-coordinate value. 294 * @param {Number} radius The radius of the circle. 295 * @returns {Graphics} The Graphics Object. 296 */ 297 drawCircle: function(x, y, radius){ 298 return this._addAction(['arc', x + radius, y + radius, radius, 0, Math.PI * 2, 0]); 299 }, 300 301 /** 302 * @language=en 303 * Draw an ellipse. 304 * @param {Number} x The x-coordinate value. 305 * @param {Number} y The y-coordinate value. 306 * @param {Number} width The width of the ellipse. 307 * @param {Number} height The height of the ellipse. 308 * @returns {Graphics} The Graphics Object. 309 */ 310 drawEllipse: function(x, y, width, height){ 311 var me = this; 312 if(width == height) return me.drawCircle(x, y, width); 313 314 var addAction = me._addAction; 315 var w = width / 2, h = height / 2, C = 0.5522847498307933, cx = C * w, cy = C * h; 316 x = x + w; 317 y = y + h; 318 319 addAction.call(me, ['moveTo', x + w, y]); 320 addAction.call(me, ['bezierCurveTo', x + w, y - cy, x + cx, y - h, x, y - h]); 321 addAction.call(me, ['bezierCurveTo', x - cx, y - h, x - w, y - cy, x - w, y]); 322 addAction.call(me, ['bezierCurveTo', x - w, y + cy, x - cx, y + h, x, y + h]); 323 addAction.call(me, ['bezierCurveTo', x + cx, y + h, x + w, y + cy, x + w, y]); 324 return me; 325 }, 326 327 /** 328 * @language=en 329 * Draw a path from the SVG data given by parameters. 330 * Demo: 331 * <p>var path = 'M250 150 L150 350 L350 350 Z';</p> 332 * <p>var shape = new Hilo.Graphics({width:500, height:500});</p> 333 * <p>shape.drawSVGPath(path).beginFill('#0ff').endFill();</p> 334 * @param {String} pathData The SVG path data to draw. 335 * @returns {Graphics} The Graphics Object. 336 */ 337 drawSVGPath: function(pathData){ 338 var me = this, addAction = me._addAction, 339 path = pathData.split(/,| (?=[a-zA-Z])/); 340 341 addAction.call(me, ['beginPath']); 342 for(var i = 0, len = path.length; i < len; i++){ 343 var str = path[i], cmd = str[0].toUpperCase(), p = str.substring(1).split(/,| /); 344 if(p[0].length == 0) p.shift(); 345 346 switch(cmd){ 347 case 'M': 348 addAction.call(me, ['moveTo', p[0], p[1]]); 349 break; 350 case 'L': 351 addAction.call(me, ['lineTo', p[0], p[1]]); 352 break; 353 case 'C': 354 addAction.call(me, ['bezierCurveTo', p[0], p[1], p[2], p[3], p[4], p[5]]); 355 break; 356 case 'Z': 357 addAction.call(me, ['closePath']); 358 break; 359 } 360 } 361 return me; 362 }, 363 364 /** 365 * @language=en 366 * Apply all draw actions. private function. 367 * @private 368 */ 369 _draw: function(context){ 370 var me = this, actions = me._actions, len = actions.length, i; 371 372 context.beginPath(); 373 for(i = 0; i < len; i++){ 374 var action = actions[i], 375 f = action[0], 376 args = action.length > 1 ? action.slice(1) : null; 377 378 if(typeof(context[f]) == 'function') context[f].apply(context, args); 379 else context[f] = action[1]; 380 } 381 }, 382 383 /** 384 * @language=en 385 * Overwrite render function. 386 * @private 387 */ 388 render: function(renderer, delta){ 389 var me = this, canvas = renderer.canvas; 390 if(renderer.renderType === 'canvas'){ 391 me._draw(renderer.context); 392 }else{ 393 me.cache(); 394 renderer.draw(me); 395 } 396 }, 397 398 /** 399 * @language=en 400 * Clear all draw actions and reset to the initial state. 401 * @returns {Graphics} The Graphics Object. 402 */ 403 clear: function(){ 404 var me = this; 405 406 me._actions.length = 0; 407 me.lineWidth = 1; 408 me.lineAlpha = 1; 409 me.lineCap = null; 410 me.lineJoin = null; 411 me.miterLimit = 10; 412 me.hasStroke = false; 413 me.strokeStyle = '0'; 414 me.hasFill = false; 415 me.fillStyle = '0'; 416 me.fillAlpha = 1; 417 418 me.setCacheDirty(true); 419 return me; 420 }, 421 422 /** 423 * @language=en 424 * Add a draw action, this is a private function. 425 * @private 426 */ 427 _addAction: function(action){ 428 var me = this; 429 me._actions.push(action); 430 return me; 431 } 432 433 }); 434 435 })(); 436