/** * @author Inateno / http://inateno.com / http://dreamirl.com */ /** * @constructor Render * @class this create the canvas, buffers, and manage resizing * @example Game.render = new DE.Render( "render", { fullScreen: "ratioStretch" } ); * Game.render.init(); */ define( [ 'DE.CONFIG', 'DE.Sizes', 'DE.Time', 'DE.MainLoop', 'DE.CollisionSystem', 'DE.Inputs', 'DE.CanvasBuffer' ], function( CONFIG, Sizes, Time, MainLoop, CollisionSystem, Inputs, CanvasBuffer ) { function Render( divId, params ) { params = params || {}; this.id = 0; this.divId = divId || undefined; this.canvas = null; this.ctx = null; this.sizes = new Sizes( params.width || CONFIG.defaultRenderWidth || 100, params.height || CONFIG.defaultRenderHeight || 100, params.scaleX || 1, params.scaleY || 1 ); this.savedSizes = new Sizes( params.width || CONFIG.defaultRenderWidth || 100, params.height || CONFIG.defaultRenderHeight || 100, params.scaleX || 1, params.scaleY || 1 ); var _drawRatio = 1; this.physicRatio = 1; this.conserveSizes = params.conserveSizes || false; this.fullScreenMode = params.fullScreen || null; this.cameras = new Array(); this.maxCameras = 0; this.freeze = false; /**** * init@void create canvas render, buffer, init ctx, and append in the dom bind Inputs, then call updateSizes */ this.init = function() { if ( this.inited ) return; MainLoop.addRender( this ); if ( !this.divId ) { this.div = window.document.body; CONFIG.debug.log( "%cWARN: you not specified a DOM Object where push the render, it will push in the body *hiiik*", 1, "color:orange" ); } else { this.div = document.getElementById( this.divId ); if ( !this.div ) { throw new Error( "Can't found the div by the given id" ); return false; } } this.canvas = document.createElement( "canvas" ); this.div.appendChild( this.canvas ); Inputs.addRender( this ); this.ctx = this.canvas.getContext( '2d' ); this.buffer = new CanvasBuffer( this.sizes.width, this.sizes.height ); this.inited = true; this.updateSizes(); } /**** * updateSizes@void change size, used for init and when change quality */ this.updateSizes = function() { if ( !this.inited ) return; if ( this.conserveSizes ) { this.sizes.width = this.div.offsetWidth.valueOf(); this.sizes.height = this.div.offsetHeight.valueOf(); } else { this.div.style.width = this.sizes.width + "px"; this.div.style.height = this.sizes.height + "px"; } this.canvas.width = this.sizes.width.valueOf(); this.canvas.height= this.sizes.height.valueOf(); this.canvas.id = this.id; if ( this.fullScreenMode ) { this.fullScreen( this.fullScreenMode ); this.resizeOnEventResize(); } this.buffer.resize( this.sizes.width, this.sizes.height ); } /*** * screenChangedSizeIndex@void when Screen class change index size, update sizes and ratios */ this.screenChangedSizeIndex = function( physicRatio, newSizes ) { this.savedSizes.width = newSizes.w; this.savedSizes.height = newSizes.h; this.physicRatio = physicRatio; this.updateSizes(); for ( var i = 0, c; c = this.cameras[ i ]; ++i ) { c.screenChangedSizeIndex( physicRatio, newSizes ); } } /**** * resize@void */ this.resize = function( w, h, stretch ) { if ( this.div != window.document.body ) { this.div.style.width = w + "px"; this.div.style.height = h + "px"; } this.canvas.width = w; this.canvas.height = h; this.sizes.width = w; this.sizes.height = h; /*if ( !stretch ) { }*/ } /**** * resizeOnEventResize@void bind window resize event if wanted */ this.resizeOnEventResize = function() { if ( !this.fullScreenMode ) return; var o = this; var lastResize = undefined; var recallMethod = function() { o.fullScreenMethod.call( o ); }; if ( window.addEventListener ) { window.addEventListener( "resize", function() { lastResize && window.clearTimeout( lastResize ); lastResize = window.setTimeout( recallMethod, 200 ); }, false ); } else if ( window.attachEvent ) { window.attachEvent( "onresize", function() { lastResize && window.clearTimeout( lastResize ); lastResize = window.setTimeout( recallMethod, 200 ); } ); } } /*** * resizeRatio@void resize with ratio, stretched or not */ this.resizeRatio = function( w, h , stretch ) { var baseW = this.savedSizes.width; var baseH = this.savedSizes.height; var calcRatio = w / baseW; if ( calcRatio * baseH > h ) { calcRatio = h / baseH; } var newW = calcRatio * baseW >> 0; var newH = calcRatio * baseH >> 0; this.resize( newW, newH, stretch ); this.div.style.marginLeft = ( ( w - newW ) / 2 ) + "px"; this.div.style.marginTop = ( ( h - newH ) / 2 ) + "px"; this.canvas.style.top = 0; _drawRatio = newW / this.savedSizes.width; } var _fullScreenMethod; /**** * changeFullScreenMode@void change current resize */ this.changeFullScreenMode = function( mode ) { this.fullScreenMode = mode; switch( mode ) { case "ratioStretch" : _fullScreenMethod = function( screenW, screenH ) { this.resizeRatio(screenW, screenH, true); }; break; case "fullStretch" : _fullScreenMethod = function( screenW, screenH ) { this.resize(screenW, screenH, true); }; break; case "ratio": _fullScreenMethod = function( screenW, screenH ) { this.resizeRatio(screenW, screenH, false); }; break; // default = nothing default: _fullScreenMethod = function( screenW, screenH ) { //this.resize(screenW, screenH, false); }; break; } } /**** * fullScreenMethod@void */ this.fullScreenMethod = function() { var screenW = ( window.innerWidth || document.documentElement.clientWidth ); var screenH = ( window.innerHeight || document.documentElement.clientHeight ); var divParentH = window.getComputedStyle( this.div.parentElement, null ).getPropertyValue( 'height' ); if ( this.div.parentElement != null && divParentH && screenH < document.body.clientHeight ) { var divW = this.div.parentElement.innerWidth || this.div.parentElement.clientWidth; var divH = this.div.parentElement.innerHeight || this.div.parentElement.clientHeight; if ( divH < screenH ) screenH = divH; if ( divW < screenW ) screenW = divW; } if ( !_fullScreenMethod ) { throw ("Render.js : fullScreenMethod need a fullScreenMode, maybe you never used changeFullScreenMode") } _fullScreenMethod.call( this, screenW, screenH ); } /**** * fullScreen@void TODO - WIP */ this.fullScreen = function( mode, automatism ) { if ( mode ) { this.changeFullScreenMode( mode ); } if ( automatism ) { // add on listener Resize } this.fullScreenMethod(); } /**** * render@void renderise all cameras binded on this Render */ this.render = function() { if ( this.freeze ) { return; } this.ctx.fillStyle = "black"; this.ctx.fillRect( 0, 0, this.sizes.width, this.sizes.height ); for ( var i = 0, camera; camera = this.cameras[ i ]; ++i ) { camera.render( this.ctx, _drawRatio, this.physicRatio ); } if ( CONFIG.DEBUG_LEVEL > 0 ) { this.ctx.font = "24px Arial"; this.ctx.fillStyle = "white"; this.ctx.fillText( "DeltaTime: " + Time.deltaTime , ( this.sizes.width >> 0 ) - 220 , 30 ); this.ctx.fillText( "MissedFrame: " + Time.missedFrame , ( this.sizes.width >> 0 ) - 220 , 60 ); this.ctx.fillText( "FPS: " + Time.fps , ( this.sizes.width >> 0 ) - 220 , 90 ); } } /**** * add@void( camera@Camera ) add a camera on this render */ this.add = function( camera ) { this.cameras.push( camera ); this.maxScenes++; camera.screenChangedSizeIndex( this.physicRatio, this.savedSizes ); } /**** * remove@void( camera@Camera ) remove a camera on this render ( not deleted ! ) */ this.remove = function( camera ) { var pos = this.cameras.indexOf( camera ); if ( pos == -1 ) { CONFIG.debug.log( "Remove camera not found ", 1, camera ); return; } this.cameras.splice( pos, 1 ); this.maxScenes--; } /**** * camerasMousCollide@void( eventName@string, x@Int, y@Int, index@Int ) trigger mouses/touches events in cameras */ this.camerasMouseCollide = function( eventName, x, y, index ) { var mouse = _getMouseCoords.call( this, x, y, index ); // custom events ? if return true stop propagation now if ( this[ 'on' + eventName ]( mouse ) || mouse.stopPropagation ) return mouse; for ( var i = 0, cam, camPos; i < this.cameras.length; i++ ) { cam = this.cameras[ i ]; if ( cam.sleep ) continue; camPos = { 'x' : cam.renderPosition.x - ( cam.renderSizes.width * 0.5 * cam.renderSizes.scaleX ) ,'y' : cam.renderPosition.y - ( cam.renderSizes.height * 0.5 * cam.renderSizes.scaleY ) ,'width' : cam.renderSizes.width * cam.renderSizes.scaleX ,'height': cam.renderSizes.height * cam.renderSizes.scaleY }; // console.log( 'camPos', mouse ); if ( CollisionSystem.pointFixedBoxCollision( mouse, camPos ) ) { if ( !cam.indexMouseOver[ index ] && cam.oOnMouseEnter( mouse, this.physicRatio ) ) break; if ( cam[ 'oOn' + eventName ]( mouse, this.physicRatio ) ) break; } else if ( cam.indexMouseOver[ index ] && cam.oOnMouseLeave( mouse, this.physicRatio ) ) { break; } } if ( !mouse.stopPropagation ) this[ 'lastOn' + eventName ]( mouse ); return mouse; } /* Custom Events return true to stop current event */ this.onMouseMove = function(){}; this.onMouseDown = function(){}; this.onMouseUp = function(){}; /* last event, called after all if no stopPropagation */ this.lastOnMouseMove = function(){}; this.lastOnMouseDown = function(){}; this.lastOnMouseUp = function(){}; this.oOnMouseDown = function( mouse ) { this.camerasMouseCollide( "MouseDown", mouse.x, mouse.y, mouse.index ); } this.oOnMouseUp = function( mouse ) { this.camerasMouseCollide( "MouseUp", mouse.x, mouse.y, mouse.index ); } this.oOnMouseMove = function( mouse ) { this.camerasMouseCollide( "MouseMove", mouse.x, mouse.y, mouse.index ); } /**** * @private */ /**** * _scrollPosition@Vector2 return the scollPostion of the window **/ function _scrollPosition() { return { x: document.scrollLeft || window.pageXOffset, y: document.scrollTop || window.pageYOffset }; } /**** * _getMouseCoords@Mouse */ function _getMouseCoords( x, y, index ) { var pos = { "x": x, "y": y }; var elem = this.canvas; var offsetLeft = 0, offsetTop = 0; while( elem ) { offsetLeft += elem.offsetLeft; offsetTop += elem.offsetTop; elem = elem.parentElement; } pos.x -= offsetLeft; pos.y -= offsetTop; return { 'x': pos.x / _drawRatio + _scrollPosition().x >> 0 , 'y': pos.y / _drawRatio + _scrollPosition().y >> 0 , 'index': index }; } /*** -private- ***/ } Render.prototype.DEName = "Render"; CONFIG.debug.log( "Render loaded", 3 ); return Render; } );