1 /**
  2  * Hilo
  3  * Copyright 2015 alibaba.com
  4  * Licensed under the MIT License
  5  */
  6 
  7 /**
  8  * @class canvas画布渲染器。所有可视对象将渲染在canvas画布上。舞台Stage会根据参数canvas选择不同的渲染器,开发者无需直接使用此类。
  9  * @augments Renderer
 10  * @param {Object} properties 创建对象的属性参数。可包含此类所有可写属性。
 11  * @module hilo/renderer/CanvasRenderer
 12  * @requires hilo/core/Class
 13  * @requires hilo/core/Hilo
 14  * @requires hilo/renderer/Renderer
 15  * @property {CanvasRenderingContext2D} context canvas画布的上下文。只读属性。
 16  */
 17 var CanvasRenderer = Class.create( /** @lends CanvasRenderer.prototype */ {
 18     Extends: Renderer,
 19     constructor: function(properties) {
 20         CanvasRenderer.superclass.constructor.call(this, properties);
 21 
 22         this.context = this.canvas.getContext("2d");
 23     },
 24     renderType: 'canvas',
 25     context: null,
 26 
 27     /**
 28      * @private
 29      * @see Renderer#startDraw
 30      */
 31     startDraw: function(target) {
 32         if (target.visible && target.alpha > 0) {
 33             if (target === this.stage) {
 34                 this.context.clearRect(0, 0, target.width, target.height);
 35             }
 36             if (target.blendMode !== this.blendMode) {
 37                 this.context.globalCompositeOperation = this.blendMode = target.blendMode;
 38             }
 39             this.context.save();
 40             return true;
 41         }
 42         return false;
 43     },
 44 
 45     /**
 46      * @private
 47      * @see Renderer#draw
 48      */
 49     draw: function(target) {
 50         var ctx = this.context,
 51             w = target.width,
 52             h = target.height;
 53 
 54         //draw background
 55         var bg = target.background;
 56         if (bg) {
 57             ctx.fillStyle = bg;
 58             ctx.fillRect(0, 0, w, h);
 59         }
 60 
 61         //draw image
 62         var drawable = target.drawable,
 63             image = drawable && drawable.image;
 64         if (image) {
 65             var rect = drawable.rect,
 66                 sw = rect[2],
 67                 sh = rect[3],
 68                 offsetX = rect[4],
 69                 offsetY = rect[5];
 70             //ie9+浏览器宽高为0时会报错 fixed ie9 bug.
 71             if (!sw || !sh) {
 72                 return;
 73             }
 74             if (!w && !h) {
 75                 //fix width/height TODO: how to get rid of this?
 76                 w = target.width = sw;
 77                 h = target.height = sh;
 78             }
 79             //the pivot is the center of frame if has offset, otherwise is (0, 0)
 80             if (offsetX || offsetY) ctx.translate(offsetX - sw * 0.5, offsetY - sh * 0.5);
 81             ctx.drawImage(image, rect[0], rect[1], sw, sh, 0, 0, w, h);
 82         }
 83     },
 84 
 85     /**
 86      * @private
 87      * @see Renderer#endDraw
 88      */
 89     endDraw: function(target) {
 90         this.context.restore();
 91     },
 92 
 93     /**
 94      * @private
 95      * @see Renderer#transform
 96      */
 97     transform: function(target) {
 98         var drawable = target.drawable;
 99         if (drawable && drawable.domElement) {
100             Hilo.setElementStyleByView(target);
101             return;
102         }
103 
104         var ctx = this.context,
105             scaleX = target.scaleX,
106             scaleY = target.scaleY;
107 
108         if (target === this.stage) {
109             var style = this.canvas.style,
110                 oldScaleX = target._scaleX,
111                 oldScaleY = target._scaleY,
112                 isStyleChange = false;
113 
114             if ((!oldScaleX && scaleX != 1) || (oldScaleX && oldScaleX != scaleX)) {
115                 target._scaleX = scaleX;
116                 style.width = scaleX * target.width + "px";
117                 isStyleChange = true;
118             }
119             if ((!oldScaleY && scaleY != 1) || (oldScaleY && oldScaleY != scaleY)) {
120                 target._scaleY = scaleY;
121                 style.height = scaleY * target.height + "px";
122                 isStyleChange = true;
123             }
124             if (isStyleChange) {
125                 target.updateViewport();
126             }
127         } else {
128             var x = target.x,
129                 y = target.y,
130                 pivotX = target.pivotX,
131                 pivotY = target.pivotY,
132                 rotation = target.rotation % 360,
133                 transform = target.transform,
134                 mask = target.mask;
135 
136             if (mask) {
137                 mask._render(this);
138                 ctx.clip();
139             }
140 
141             //alignment
142             var align = target.align;
143             if (align) {
144                 var pos = target.getAlignPosition();
145                 x = pos.x;
146                 y = pos.y;
147             }
148             
149             if (transform) {
150                 ctx.transform(transform.a, transform.b, transform.c, transform.d, transform.tx, transform.ty);
151             } else {
152                 if (x != 0 || y != 0) ctx.translate(x, y);
153                 if (rotation != 0) ctx.rotate(rotation * Math.PI / 180);
154                 if (scaleX != 1 || scaleY != 1) ctx.scale(scaleX, scaleY);
155                 if (pivotX != 0 || pivotY != 0) ctx.translate(-pivotX, -pivotY);
156             }
157 
158         }
159 
160         if (target.alpha > 0) ctx.globalAlpha *= target.alpha;
161     },
162 
163     /**
164      * @private
165      * @see Renderer#remove
166      */
167     remove: function(target) {
168         var drawable = target.drawable;
169         var elem = drawable && drawable.domElement;
170 
171         if (elem) {
172             var parentElem = elem.parentNode;
173             if (parentElem) {
174                 parentElem.removeChild(elem);
175             }
176         }
177     },
178 
179     /**
180      * @private
181      * @see Renderer#clear
182      */
183     clear: function(x, y, width, height) {
184         this.context.clearRect(x, y, width, height);
185     },
186 
187     /**
188      * @private
189      * @see Renderer#resize
190      */
191     resize: function(width, height) {
192         var canvas = this.canvas;
193         var stage = this.stage;
194         var style = canvas.style;
195 
196         canvas.width = width;
197         canvas.height = height;
198 
199         style.width = stage.width * stage.scaleX + 'px';
200         style.height = stage.height * stage.scaleY + 'px';
201     }
202 
203 });