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