1 var jaws = (function(jaws) { 2 3 /** 4 * 5 * @class A window (Rect) into a bigger canvas/image. Viewport is always contained within that given image (called the game world). "Field Summary" contains options for the Viewport()-constructor. 6 * 7 * @property {int} width Width of viewport, defaults to canvas width 8 * @property {int} height Height of viewport, defaults to canvas height 9 * @property {int} max_x Maximum x-position for viewport, defaults to canvas width 10 * @property {int} max_y Maximum y-position for viewport, defaults to canvas height 11 * @property {int} x X-position for the upper left corner of the viewport 12 * @property {int} y Y-position for the upper left corner of the viewport 13 * 14 * @example 15 * // Center viewport around players position (player needs to have x/y attributes) 16 * // Usefull for sidescrollers 17 * viewport.centerAround(player) 18 * 19 * // Common viewport usage. max_x/max_y could be said to set the "game world size" 20 * viewport = viewport = new jaws.Viewport({max_x: 400, max_y: 3000}) 21 * player = new jaws.Sprite({x:100, y:400}) 22 * viewport.centerAround(player) 23 * 24 * // Draw player relative to the viewport. If viewport is way off, player won't even show up. 25 * viewport.apply( function() { 26 * player.draw() 27 * }); 28 * 29 */ 30 31 32 jaws.Viewport = function ViewPort(options) { 33 if( !(this instanceof arguments.callee) ) return new arguments.callee( options ); 34 35 jaws.parseOptions(this, options, this.default_options) 36 37 /* This is needed cause default_options is set loadtime, we need to get width etc runtime */ 38 if(!this.context) this.context = jaws.context; 39 if(!this.width) this.width = jaws.width; 40 if(!this.height) this.height = jaws.height; 41 if(!this.max_x) this.max_x = jaws.width; 42 if(!this.max_y) this.max_y = jaws.height; 43 44 var that = this 45 46 /** Move viewport x pixels horizontally and y pixels vertically */ 47 this.move = function(x, y) { 48 x && (this.x += x) 49 y && (this.y += y) 50 this.verifyPosition() 51 }; 52 53 /** Move viewport to given x/y */ 54 this.moveTo = function(x, y) { 55 if(!(x==undefined)) { this.x = x } 56 if(!(y==undefined)) { this.y = y } 57 this.verifyPosition() 58 }; 59 60 /** 61 * Returns true if item is outside viewport 62 * @example 63 * 64 * if( viewport.isOutside(player)) player.die(); 65 * 66 * // ... or the more advanced: 67 * bullets = new SpriteList() 68 * bullets.push( bullet ) 69 * bullets.removeIf( viewport.isOutside ) 70 * 71 */ 72 this.isOutside = function(item) { 73 return(!that.isInside(item)) 74 }; 75 76 /** Returns true if item is inside viewport */ 77 this.isInside = function(item) { 78 return( item.x >= that.x && item.x <= (that.x + that.width) && item.y >= that.y && item.y <= (that.y + that.height) ) 79 }; 80 81 /** Returns true if item is partly (down to 1 pixel) inside viewport */ 82 this.isPartlyInside = function(item) { 83 var rect = item.rect() 84 return( rect.right >= that.x && rect.x <= (that.x + that.width) && rect.bottom >= that.y && item.y <= (that.y + that.height) ) 85 }; 86 87 /** Returns true of item is left of viewport */ 88 this.isLeftOf = function(item) { return(item.x < that.x) } 89 90 /** Returns true of item is right of viewport */ 91 this.isRightOf = function(item) { return(item.x > (that.x + that.width) ) } 92 93 /** Returns true of item is above viewport */ 94 this.isAbove = function(item) { return(item.y < that.y) } 95 96 /** Returns true of item is above viewport */ 97 this.isBelow = function(item) { return(item.y > (that.y + that.height) ) } 98 99 100 /** 101 * center the viewport around item. item must respond to x and y for this to work. 102 * Usefull for sidescrollers when you wan't to keep the player in the center of the screen no matter how he moves. 103 */ 104 this.centerAround = function(item) { 105 this.x = Math.floor(item.x - this.width / 2); 106 this.y = Math.floor(item.y - this.height / 2); 107 this.verifyPosition(); 108 }; 109 110 /** 111 * force 'item' inside current viewports visible area 112 * using 'buffer' as indicator how close to the 'item' is allowed to go 113 * 114 * @example 115 * 116 * viewport.move(10,0) // scroll forward 117 * viewport.forceInsideVisibleArea(player, 20) // make sure player doesn't get left behind 118 */ 119 this.forceInsideVisibleArea = function(item, buffer) { 120 if(item.x < this.x+buffer) { item.x = this.x+buffer } 121 if(item.x > this.x+jaws.width-buffer) { item.x = this.x+jaws.width-buffer } 122 if(item.y < this.y+buffer) { item.y = this.y+buffer } 123 if(item.y > this.y+jaws.height-buffer) { item.y = this.y+jaws.height-buffer } 124 } 125 126 /** 127 * force 'item' inside the limits of the viewport 128 * using 'buffer' as indicator how close to the 'item' is allowed to go 129 * 130 * @example 131 * viewport.forceInside(player, 10) 132 */ 133 this.forceInside = function(item, buffer) { 134 if(item.x < buffer) { item.x = buffer } 135 if(item.x > this.max_x-buffer) { item.x = this.max_x-buffer } 136 if(item.y < buffer) { item.y = buffer } 137 if(item.y > this.max_y-buffer) { item.y = this.max_y-buffer } 138 } 139 140 141 /** 142 * executes given draw-callback with a translated canvas which will draw items relative to the viewport 143 * 144 * @example 145 * 146 * viewport.apply( function() { 147 * player.draw(); 148 * foo.draw(); 149 * }); 150 * 151 */ 152 this.apply = function(func) { 153 this.context.save() 154 this.context.translate(-this.x, -this.y) 155 func() 156 this.context.restore() 157 }; 158 159 /** 160 * if obj is an array-like object, iterate through it and call draw() on each item if it's partly inside the viewport 161 */ 162 this.draw = function( obj ) { 163 this.apply( function() { 164 if(obj.forEach) obj.forEach( that.drawIfPartlyInside ); 165 else if(obj.draw) that.drawIfPartlyInside(obj); 166 // else if(jaws.isFunction(obj) {}; // add apply()-functionally here? 167 }); 168 } 169 170 /** 171 * draws all items of 'tile_map' that's lies inside the viewport 172 * this is simular to viewport.draw( tile_map.all() ) but optmized for Huge game worlds (tile maps) 173 */ 174 this.drawTileMap = function( tile_map ) { 175 var sprites = tile_map.atRect({ x: this.x, y: this.y, right: this.x + this.width, bottom: this.y + this.height }) 176 this.apply( function() { 177 for(var i=0; i < sprites.length; i++) sprites[i].draw(); 178 }); 179 } 180 181 /** draws 'item' if it's partly inside the viewport */ 182 this.drawIfPartlyInside = function(item) { 183 if(that.isPartlyInside(item)) item.draw(); 184 } 185 186 /** @private */ 187 this.verifyPosition = function() { 188 var max = this.max_x - this.width 189 if(this.x < 0) { this.x = 0 } 190 if(this.x > max) { this.x = max } 191 192 var max = this.max_y - this.height 193 if(this.y < 0) { this.y = 0 } 194 if(this.y > max) { this.y = max } 195 }; 196 197 this.moveTo(options.x||0, options.y||0) 198 } 199 200 jaws.Viewport.prototype.default_options = { 201 context: null, 202 width: null, 203 height: null, 204 max_x: null, 205 max_y: null, 206 x: 0, 207 y: 0 208 }; 209 210 jaws.Viewport.prototype.toString = function() { return "[Viewport " + this.x.toFixed(2) + ", " + this.y.toFixed(2) + ", " + this.width + ", " + this.height + "]" } 211 212 return jaws; 213 })(jaws || {}); 214 215