* draw this renderable (automatically called by melonJS) * @param {CanvasRenderer|WebGLRenderer} renderer - a renderer instance * @param {Camera2d} [viewport] - the viewport to (re)draw
(renderer, viewport)
| 1156 | * @param {Camera2d} [viewport] - the viewport to (re)draw |
| 1157 | */ |
| 1158 | draw(renderer, viewport) { |
| 1159 | const bounds = this.getBounds(); |
| 1160 | |
| 1161 | // adjust position before clipping so the renderer's currentTransform |
| 1162 | // sits at this container's local origin. `clipRect` then operates |
| 1163 | // in container-local coords and any scale/rotation accumulated in |
| 1164 | // the matrix is honored when the renderer converts to screen space. |
| 1165 | // (Issue #1349 — passing world-space `bounds.left/top` to the |
| 1166 | // previous local-space-expecting `clipRect` double-counted the |
| 1167 | // translation when the container was nested inside a translated |
| 1168 | // parent.) |
| 1169 | renderer.translate(this.pos.x, this.pos.y); |
| 1170 | |
| 1171 | // clip the container children to the container bounds. |
| 1172 | // Container's anchorPoint is forced to (0, 0), so the local rect |
| 1173 | // is (0, 0, width, height). `bounds.isFinite()` covers the |
| 1174 | // aggregate (self + children) — a container may have a finite |
| 1175 | // child bounds while its own `width/height` are `Infinity` |
| 1176 | // (e.g., the world container or any `enableChildBoundsUpdate` |
| 1177 | // container with a finite child but no intrinsic size). Passing |
| 1178 | // `Infinity` to `clipRect` would NaN-poison the WebGL scissor |
| 1179 | // math, so guard on the size we actually pass too. |
| 1180 | if ( |
| 1181 | this.root === false && |
| 1182 | this.clipping === true && |
| 1183 | bounds.isFinite() === true && |
| 1184 | Number.isFinite(this.width) === true && |
| 1185 | Number.isFinite(this.height) === true |
| 1186 | ) { |
| 1187 | renderer.clipRect(0, 0, this.width, this.height); |
| 1188 | } |
| 1189 | |
| 1190 | // color background if defined |
| 1191 | if (this.backgroundColor.alpha > 1 / 255) { |
| 1192 | renderer.clearColor(this.backgroundColor); |
| 1193 | } |
| 1194 | |
| 1195 | // viewport is documented optional — legacy subclass overrides call |
| 1196 | // `super.draw(renderer)` without one; treat that as the default camera |
| 1197 | const isNonDefaultCamera = viewport ? !viewport.isDefault : false; |
| 1198 | |
| 1199 | const children = this.getChildren(); |
| 1200 | for (let i = children.length, obj; i--, (obj = children[i]); ) { |
| 1201 | const isFloating = obj.floating === true; |
| 1202 | |
| 1203 | if (obj.inViewport || isFloating) { |
| 1204 | // skip UI-only floating elements on non-default cameras |
| 1205 | if (isFloating && isNonDefaultCamera && !obj.visibleInAllCameras) { |
| 1206 | continue; |
| 1207 | } |
| 1208 | |
| 1209 | if (isFloating) { |
| 1210 | renderer.save(); |
| 1211 | renderer.resetTransform(); |
| 1212 | // Floating renderables draw in screen space — swap to |
| 1213 | // the camera's flat screen ortho regardless of whether |
| 1214 | // we're on the default camera. Under Camera2d this is |
| 1215 | // a no-op (its `screenProjection` mirrors |
nothing calls this directly
no test coverage detected