1 2 /** 3 * @class 舞台是可视对象树的根,可视对象只有添加到舞台或其子对象后才会被渲染出来。 4 * @augments Container 5 * @param {Object} properties 创建对象的属性参数。可包含此类所有可写属性。 6 * @module hilo/view/Stage 7 * @requires hilo/core/Hilo 8 * @requires hilo/core/Class 9 * @requires hilo/view/Container 10 * @requires hilo/renderer/CanvasRenderer 11 * @property {HTMLElement} canvas 舞台所对应的画布。它可以是一个canvas或一个普通的div。只读属性。 12 * @property {Renderer} renderer 舞台渲染器。只读属性。 13 * @property {Boolean} paused 指示舞台是否暂停刷新渲染。 14 * @property {Object} viewport 舞台内容在页面中的渲染区域。包含的属性有:left、top、width、height。只读属性。 15 */ 16 var Stage = Class.create(/** @lends Stage.prototype */{ 17 Extends: Container, 18 constructor: function Stage(properties){ 19 properties = properties || {}; 20 this.id = this.id || properties.id || Hilo.getUid('Stage'); 21 Stage.superclass.constructor.call(this, properties); 22 23 //init canvas 24 var canvas = this.canvas; 25 if(typeof canvas === 'string') canvas = Hilo.getElement(canvas); 26 if(!canvas){ 27 canvas = Hilo.createElement('canvas', { 28 style: { 29 position: 'absolute' 30 } 31 }); 32 } 33 this.canvas = canvas; 34 35 if(canvas.parentNode) this.updateViewport(); 36 37 if(!properties.width) this.width = this.viewport.width || 320; 38 if(!properties.height) this.height = this.viewport.height || 480; 39 40 var isCanvas = canvas.getContext; 41 if(isCanvas){ 42 canvas.width = this.width; 43 canvas.height = this.height; 44 } 45 46 //init renderer 47 var props = {canvas:canvas, stage:this}; 48 this.renderer = isCanvas ? new CanvasRenderer(props) : new DOMRenderer(props); 49 }, 50 51 canvas: null, 52 renderer: null, 53 paused: false, 54 viewport: null, 55 56 /** 57 * 调用tick会触发舞台的更新和渲染。 58 */ 59 tick: function(delta){ 60 if(!this.paused){ 61 this._render(this.renderer, delta); 62 } 63 }, 64 65 /** 66 * 开启/关闭DOM事件功能。 67 */ 68 enableDOMEvent: function(type, enable){ 69 var me = this, 70 canvas = me.canvas, 71 types = typeof type === 'string' ? [type] : type, 72 handler = me._domListener || (me._domListener = function(e){me._onDOMEvent(e)}); 73 74 for(var i = 0; i < types.length; i++){ 75 var type = types[i]; 76 if(enable === false){ 77 canvas.removeEventListener(type, handler); 78 }else{ 79 canvas.addEventListener(type, handler, false); 80 } 81 } 82 }, 83 84 /** 85 * DOM事件处理函数。此方法会把事件调度到事件的坐标点所对应的可视对象。 86 * @private 87 */ 88 _onDOMEvent: function(e){ 89 var type = e.type, event = e, isTouch = type.indexOf('touch') == 0; 90 91 //calculate stageX/stageY 92 var posObj = e; 93 if(isTouch){ 94 var touches = e.touches, changedTouches = e.changedTouches; 95 posObj = (touches && touches.length) ? touches[0] : 96 (changedTouches && changedTouches.length) ? changedTouches[0] : null; 97 } 98 99 var x = posObj.pageX || posObj.clientX, y = posObj.pageY || posObj.clientY, 100 viewport = this.viewport || this.updateViewport(); 101 102 event.stageX = x = (x - viewport.left) / this.scaleX; 103 event.stageY = y = (y - viewport.top) / this.scaleY; 104 105 var obj = this.getViewAtPoint(x, y, true, false, true), 106 canvas = this.canvas, target = this._eventTarget; 107 108 //fire mouseout/touchout event for last event target 109 var leave = type === 'mouseout' && !canvas.contains(e.relatedTarget); 110 if(target && (target != obj || leave)){ 111 var out = (type === 'touchmove') ? 'touchout' : 112 (type === 'mousemove' || leave || !obj) ? 'mouseout' : null; 113 if(out) target.fire(out); 114 event.lastEventTarget = target; 115 this._eventTarget = null; 116 } 117 118 //fire event for current view 119 if(obj && obj.pointerEnabled && type !== 'mouseout'){ 120 event.eventTarget = this._eventTarget = obj; 121 obj.fire(event); 122 } 123 124 //set cursor for current view 125 if(!isTouch){ 126 var cursor = (obj && obj.pointerEnabled && obj.useHandCursor) ? 'pointer' : ''; 127 canvas.style.cursor = cursor; 128 } 129 130 //fire event for stage 131 if(leave || type !== "mouseout") this.fire(event); 132 133 //fix android: `touchmove` fires only once 134 if(Hilo.browser.android && type === 'touchmove'){ 135 e.preventDefault(); 136 } 137 }, 138 139 /** 140 * 更新舞台在页面中的渲染区域。当舞台canvas的样式border、margin、padding等属性更改后,需要调用此方法更新舞台渲染区域。 141 */ 142 updateViewport: function(){ 143 return this.viewport = Hilo.getElementRect(this.canvas); 144 } 145 146 });