1 /** 2 * Hilo 3 * Copyright 2015 alibaba.com 4 * Licensed under the MIT License 5 */ 6 7 /** 8 * <iframe src='../../../examples/Graphics.html?noHeader' width = '320' height = '400' scrolling='no'></iframe> 9 * <br/> 10 * @class Graphics类包含一组创建矢量图形的方法。 11 * @augments View 12 * @mixes CacheMixin 13 * @param {Object} properties 创建对象的属性参数。可包含此类所有可写属性。 14 * @module hilo/view/Graphics 15 * @requires hilo/core/Hilo 16 * @requires hilo/core/Class 17 * @requires hilo/view/View 18 * @requires hilo/view/CacheMixin 19 * @property {Number} lineWidth 笔画的线条宽度。默认为1。只读属性。 20 * @property {Number} lineAlpha 笔画的线条透明度。默认为1。只读属性。 21 * @property {String} lineCap 笔画的线条端部样式。可选值有:butt、round、square等,默认为null。只读属性。 22 * @property {String} lineJoin 笔画的线条连接样式。可选值有:miter、round、bevel等,默认为null。只读属性。 23 * @property {Number} miterLimit 斜连线长度和线条宽度的最大比率。此属性仅当lineJoin为miter时有效。默认值为10。只读属性。 24 * @property {String} strokeStyle 笔画边框的样式。默认值为'0',即黑色。只读属性。 25 * @property {String} fillStyle 内容填充的样式。默认值为'0',即黑色。只读属性。 26 * @property {Number} fillAlpha 内容填充的透明度。默认值为0。只读属性。 27 */ 28 var Graphics = (function(){ 29 30 var canvas = document.createElement('canvas'); 31 var helpContext = canvas.getContext && canvas.getContext('2d'); 32 33 return Class.create(/** @lends Graphics.prototype */{ 34 Extends: View, 35 Mixes:CacheMixin, 36 constructor: function(properties){ 37 properties = properties || {}; 38 this.id = this.id || properties.id || Hilo.getUid('Graphics'); 39 Graphics.superclass.constructor.call(this, properties); 40 41 this._actions = []; 42 }, 43 44 lineWidth: 1, 45 lineAlpha: 1, 46 lineCap: null, //'butt', 'round', 'square' 47 lineJoin: null, //'miter', 'round', 'bevel' 48 miterLimit: 10, 49 hasStroke: false, 50 strokeStyle: '0', 51 hasFill: false, 52 fillStyle: '0', 53 fillAlpha: 0, 54 55 /** 56 * 指定绘制图形的线条样式。 57 * @param {Number} thickness 线条的粗细值。默认为1。 58 * @param {String} lineColor 线条的CSS颜色值。默认为黑色,即'0'。 59 * @param {Number} lineAlpha 线条的透明度值。默认为不透明,即1。 60 * @param {String} lineCap 线条的端部样式。可选值有:butt、round、square等,默认值为null。 61 * @param {String} lineJoin 线条的连接样式。可选值有:miter、round、bevel等,默认值为null。 62 * @param {Number} miterLimit 斜连线长度和线条宽度的最大比率。此属性仅当lineJoin为miter时有效。默认值为10。 63 * @returns {Graphics} Graphics对象本身。 64 */ 65 lineStyle: function(thickness, lineColor, lineAlpha, lineCap, lineJoin, miterLimit){ 66 var me = this, addAction = me._addAction; 67 68 addAction.call(me, ['lineWidth', (me.lineWidth = thickness || 1)]); 69 addAction.call(me, ['strokeStyle', (me.strokeStyle = lineColor || '0')]); 70 addAction.call(me, ['lineAlpha', (me.lineAlpha = lineAlpha || 1)]); 71 if(lineCap != undefined) addAction.call(me, ['lineCap', (me.lineCap = lineCap)]); 72 if(lineJoin != undefined) addAction.call(me, ['lineJoin', (me.lineJoin = lineJoin)]); 73 if(miterLimit != undefined) addAction.call(me, ['miterLimit', (me.miterLimit = miterLimit)]); 74 me.hasStroke = true; 75 return me; 76 }, 77 78 /** 79 * 指定绘制图形的填充样式和透明度。 80 * @param {String} fill 填充样式。可以是color、gradient或pattern。 81 * @param {Number} alpha 透明度。 82 * @returns {Graphics} Graphics对象本身。 83 */ 84 beginFill: function(fill, alpha){ 85 var me = this, addAction = me._addAction; 86 87 addAction.call(me, ['fillStyle', (me.fillStyle = fill)]); 88 addAction.call(me, ['fillAlpha', (me.fillAlpha = alpha || 1)]); 89 me.hasFill = true; 90 return me; 91 }, 92 93 /** 94 * 应用并结束笔画的绘制和图形样式的填充。 95 * @returns {Graphics} Graphics对象本身。 96 */ 97 endFill: function(){ 98 var me = this, addAction = me._addAction; 99 100 if(me.hasStroke) addAction.call(me, ['stroke']); 101 if(me.hasFill) addAction.call(me, ['fill']); 102 me.setCacheDirty(true); 103 return me; 104 }, 105 106 /** 107 * 指定绘制图形的线性渐变填充样式。 108 * @param {Number} x0 渐变的起始点的x轴坐标。 109 * @param {Number} y0 渐变的起始点的y轴坐标。 110 * @param {Number} x1 渐变的结束点的x轴坐标。 111 * @param {Number} y1 渐变的结束点的y轴坐标。 112 * @param {Array} colors 渐变中使用的CSS颜色值数组。 113 * @param {Array} ratois 渐变中开始与结束之间的位置数组。需与colors数组里的颜色值一一对应,介于0.0与1.0之间的值。 114 * @returns {Graphics} Graphics对象本身。 115 */ 116 beginLinearGradientFill: function(x0, y0, x1, y1, colors, ratios){ 117 var me = this, gradient = helpContext.createLinearGradient(x0, y0, x1, y1); 118 119 for (var i = 0, len = colors.length; i < len; i++){ 120 gradient.addColorStop(ratios[i], colors[i]); 121 } 122 me.hasFill = true; 123 return me._addAction(['fillStyle', (me.fillStyle = gradient)]); 124 }, 125 126 /** 127 * 指定绘制图形的放射性渐变填充样式。 128 * @param {Number} x0 渐变的起始圆的x轴坐标。 129 * @param {Number} y0 渐变的起始圆的y轴坐标。 130 * @param {Number} r0 渐变的起始圆的半径。 131 * @param {Number} x1 渐变的结束圆的x轴坐标。 132 * @param {Number} y1 渐变的结束圆的y轴坐标。 133 * @param {Number} r1 渐变的结束圆的半径。 134 * @param {Array} colors 渐变中使用的CSS颜色值数组。 135 * @param {Array} ratois 渐变中开始与结束之间的位置数组。需与colors数组里的颜色值一一对应,介于0.0与1.0之间的值。 136 * @returns {Graphics} Graphics对象本身。 137 */ 138 beginRadialGradientFill: function(x0, y0, r0, x1, y1, r1, colors, ratios){ 139 var me = this, gradient = helpContext.createRadialGradient(x0, y0, r0, x1, y1, r1); 140 for (var i = 0, len = colors.length; i < len; i++) 141 { 142 gradient.addColorStop(ratios[i], colors[i]); 143 } 144 me.hasFill = true; 145 return me._addAction(['fillStyle', (me.fillStyle = gradient)]); 146 }, 147 148 /** 149 * 开始一个位图填充样式。 150 * @param {HTMLImageElement} image 指定填充的Image对象。 151 * @param {String} repetition 指定填充的重复设置参数。它可以是以下任意一个值:repeat, repeat-x, repeat-y, no-repeat。默认为''。 152 * @returns {Graphics} Graphics对象本身。 153 */ 154 beginBitmapFill: function(image, repetition){ 155 var me = this, pattern = helpContext.createPattern(image, repetition || ''); 156 me.hasFill = true; 157 return me._addAction(['fillStyle', (me.fillStyle = pattern)]); 158 }, 159 160 /** 161 * 开始一个新的路径。 162 * @returns {Graphics} Graphics对象本身。 163 */ 164 beginPath: function(){ 165 return this._addAction(['beginPath']); 166 }, 167 168 /** 169 * 关闭当前的路径。 170 * @returns {Graphics} Graphics对象本身。 171 */ 172 closePath: function(){ 173 return this._addAction(['closePath']); 174 }, 175 176 /** 177 * 将当前绘制位置移动到点(x, y)。 178 * @param {Number} x x轴坐标。 179 * @param {Number} y y轴坐标。 180 * @returns {Graphics} Graphics对象本身。 181 */ 182 moveTo: function(x, y){ 183 return this._addAction(['moveTo', x, y]); 184 }, 185 186 /** 187 * 绘制从当前位置开始到点(x, y)结束的直线。 188 * @param {Number} x x轴坐标。 189 * @param {Number} y y轴坐标。 190 * @returns {Graphics} Graphics对象本身。 191 */ 192 lineTo: function(x, y){ 193 return this._addAction(['lineTo', x, y]); 194 }, 195 196 /** 197 * 绘制从当前位置开始到点(x, y)结束的二次曲线。 198 * @param {Number} cpx 控制点cp的x轴坐标。 199 * @param {Number} cpy 控制点cp的y轴坐标。 200 * @param {Number} x x轴坐标。 201 * @param {Number} y y轴坐标。 202 * @returns {Graphics} Graphics对象本身。 203 */ 204 quadraticCurveTo: function(cpx, cpy, x, y){ 205 return this._addAction(['quadraticCurveTo', cpx, cpy, x, y]); 206 }, 207 208 /** 209 * 绘制从当前位置开始到点(x, y)结束的贝塞尔曲线。 210 * @param {Number} cp1x 控制点cp1的x轴坐标。 211 * @param {Number} cp1y 控制点cp1的y轴坐标。 212 * @param {Number} cp2x 控制点cp2的x轴坐标。 213 * @param {Number} cp2y 控制点cp2的y轴坐标。 214 * @param {Number} x x轴坐标。 215 * @param {Number} y y轴坐标。 216 * @returns {Graphics} Graphics对象本身。 217 */ 218 bezierCurveTo: function(cp1x, cp1y, cp2x, cp2y, x, y){ 219 return this._addAction(['bezierCurveTo', cp1x, cp1y, cp2x, cp2y, x, y]); 220 }, 221 222 /** 223 * 绘制一个矩形。 224 * @param {Number} x x轴坐标。 225 * @param {Number} y y轴坐标。 226 * @param {Number} width 矩形的宽度。 227 * @param {Number} height 矩形的高度。 228 * @returns {Graphics} Graphics对象本身。 229 */ 230 drawRect: function(x, y, width, height){ 231 return this._addAction(['rect', x, y, width, height]); 232 }, 233 234 /** 235 * 绘制一个复杂的圆角矩形。 236 * @param {Number} x x轴坐标。 237 * @param {Number} y y轴坐标。 238 * @param {Number} width 圆角矩形的宽度。 239 * @param {Number} height 圆角矩形的高度。 240 * @param {Number} cornerTL 圆角矩形的左上圆角大小。 241 * @param {Number} cornerTR 圆角矩形的右上圆角大小。 242 * @param {Number} cornerBR 圆角矩形的右下圆角大小。 243 * @param {Number} cornerBL 圆角矩形的左下圆角大小。 244 * @returns {Graphics} Graphics对象本身。 245 */ 246 drawRoundRectComplex: function(x, y, width, height, cornerTL, cornerTR, cornerBR, cornerBL){ 247 var me = this, addAction = me._addAction; 248 addAction.call(me, ['moveTo', x + cornerTL, y]); 249 addAction.call(me, ['lineTo', x + width - cornerTR, y]); 250 addAction.call(me, ['arc', x + width - cornerTR, y + cornerTR, cornerTR, -Math.PI/2, 0, false]); 251 addAction.call(me, ['lineTo', x + width, y + height - cornerBR]); 252 addAction.call(me, ['arc', x + width - cornerBR, y + height - cornerBR, cornerBR, 0, Math.PI/2, false]); 253 addAction.call(me, ['lineTo', x + cornerBL, y + height]); 254 addAction.call(me, ['arc', x + cornerBL, y + height - cornerBL, cornerBL, Math.PI/2, Math.PI, false]); 255 addAction.call(me, ['lineTo', x, y + cornerTL]); 256 addAction.call(me, ['arc', x + cornerTL, y + cornerTL, cornerTL, Math.PI, Math.PI*3/2, false]); 257 return me; 258 }, 259 260 /** 261 * 绘制一个圆角矩形。 262 * @param {Number} x x轴坐标。 263 * @param {Number} y y轴坐标。 264 * @param {Number} width 圆角矩形的宽度。 265 * @param {Number} height 圆角矩形的高度。 266 * @param {Number} cornerSize 圆角矩形的圆角大小。 267 * @returns {Graphics} Graphics对象本身。 268 */ 269 drawRoundRect: function(x, y, width, height, cornerSize){ 270 return this.drawRoundRectComplex(x, y, width, height, cornerSize, cornerSize, cornerSize, cornerSize); 271 }, 272 273 /** 274 * 绘制一个圆。 275 * @param {Number} x x轴坐标。 276 * @param {Number} y y轴坐标。 277 * @param {Number} radius 圆的半径。 278 * @returns {Graphics} Graphics对象本身。 279 */ 280 drawCircle: function(x, y, radius){ 281 return this._addAction(['arc', x + radius, y + radius, radius, 0, Math.PI * 2, 0]); 282 }, 283 284 /** 285 * 绘制一个椭圆。 286 * @param {Number} x x轴坐标。 287 * @param {Number} y y轴坐标。 288 * @param {Number} width 椭圆的宽度。 289 * @param {Number} height 椭圆的高度。 290 * @returns {Graphics} Graphics对象本身。 291 */ 292 drawEllipse: function(x, y, width, height){ 293 var me = this; 294 if(width == height) return me.drawCircle(x, y, width); 295 296 var addAction = me._addAction; 297 var w = width / 2, h = height / 2, C = 0.5522847498307933, cx = C * w, cy = C * h; 298 x = x + w; 299 y = y + h; 300 301 addAction.call(me, ['moveTo', x + w, y]); 302 addAction.call(me, ['bezierCurveTo', x + w, y - cy, x + cx, y - h, x, y - h]); 303 addAction.call(me, ['bezierCurveTo', x - cx, y - h, x - w, y - cy, x - w, y]); 304 addAction.call(me, ['bezierCurveTo', x - w, y + cy, x - cx, y + h, x, y + h]); 305 addAction.call(me, ['bezierCurveTo', x + cx, y + h, x + w, y + cy, x + w, y]); 306 return me; 307 }, 308 309 /** 310 * 根据参数指定的SVG数据绘制一条路径。 311 * 代码示例: 312 * <p>var path = 'M250 150 L150 350 L350 350 Z';</p> 313 * <p>var shape = new Hilo.Graphics({width:500, height:500});</p> 314 * <p>shape.drawSVGPath(path).beginFill('#0ff').endFill();</p> 315 * @param {String} pathData 要绘制的SVG路径数据。 316 * @returns {Graphics} Graphics对象本身。 317 */ 318 drawSVGPath: function(pathData){ 319 var me = this, addAction = me._addAction, 320 path = pathData.split(/,| (?=[a-zA-Z])/); 321 322 addAction.call(me, ['beginPath']); 323 for(var i = 0, len = path.length; i < len; i++){ 324 var str = path[i], cmd = str[0].toUpperCase(), p = str.substring(1).split(/,| /); 325 if(p[0].length == 0) p.shift(); 326 327 switch(cmd){ 328 case 'M': 329 addAction.call(me, ['moveTo', p[0], p[1]]); 330 break; 331 case 'L': 332 addAction.call(me, ['lineTo', p[0], p[1]]); 333 break; 334 case 'C': 335 addAction.call(me, ['bezierCurveTo', p[0], p[1], p[2], p[3], p[4], p[5]]); 336 break; 337 case 'Z': 338 addAction.call(me, ['closePath']); 339 break; 340 } 341 } 342 return me; 343 }, 344 345 /** 346 * 执行全部绘制动作。内部私有方法。 347 * @private 348 */ 349 _draw: function(context){ 350 var me = this, actions = me._actions, len = actions.length, i; 351 352 context.beginPath(); 353 for(i = 0; i < len; i++){ 354 var action = actions[i], 355 f = action[0], 356 args = action.length > 1 ? action.slice(1) : null; 357 358 if(typeof(context[f]) == 'function') context[f].apply(context, args); 359 else context[f] = action[1]; 360 } 361 }, 362 363 /** 364 * 重写渲染实现。 365 * @private 366 */ 367 render: function(renderer, delta){ 368 var me = this, canvas = renderer.canvas; 369 if(renderer.renderType === 'canvas'){ 370 me._draw(renderer.context); 371 }else{ 372 me.cache(); 373 renderer.draw(me); 374 } 375 }, 376 377 /** 378 * 清除所有绘制动作并复原所有初始状态。 379 * @returns {Graphics} Graphics对象本身。 380 */ 381 clear: function(){ 382 var me = this; 383 384 me._actions.length = 0; 385 me.lineWidth = 1; 386 me.lineAlpha = 1; 387 me.lineCap = null; 388 me.lineJoin = null; 389 me.miterLimit = 10; 390 me.hasStroke = false; 391 me.strokeStyle = '0'; 392 me.hasFill = false; 393 me.fillStyle = '0'; 394 me.fillAlpha = 1; 395 396 me.setCacheDirty(true); 397 return me; 398 }, 399 400 /** 401 * 添加一个绘制动作。内部私有方法。 402 * @private 403 */ 404 _addAction: function(action){ 405 var me = this; 406 me._actions.push(action); 407 return me; 408 } 409 410 }); 411 412 })();