UNPKG

pxt-common-packages

Version:
147 lines (130 loc) 4.61 kB
enum BackgroundAlignment { //% block="left" Left = 1, //% block="right" Right, //% block="top" Top, //% block="bottom" Bottom, //% block="center" Center } namespace scene { export class Background { color: number; _image: Image; camera: Camera; private _layers: BackgroundLayer[]; constructor(camera: Camera) { this.color = 0; this.camera = camera; this._layers = []; } public addLayer(pic: Image, distance: number, alignment: BackgroundAlignment) { const layer = new BackgroundLayer(distance, alignment, pic); this._layers.push(layer); this._layers.sort((a, b) => b.distance - a.distance); return layer; } get image() { if (!this._image) { this._image = image.create(screen.width, screen.height); } return this._image; } set image(image: Image) { this._image = image; } hasBackgroundImage(): boolean { return !!this._image; } draw() { screen.fill(this.color); if (this._image) screen.drawTransparentImage(this._image, 0, 0) if (this._layers) { this._layers.forEach(layer => { // compute displacement based on distance const ox = Math.round(this.camera.drawOffsetX / (1 + layer.distance)); const oy = Math.round(this.camera.drawOffsetY / (1 + layer.distance)); layer.draw(ox, oy); }); } } } export class BackgroundLayer { distance: number; img: Image; repeatX: boolean; repeatY: boolean; alignX: BackgroundAlignment; alignY: BackgroundAlignment; constructor(distance: number, alignment: BackgroundAlignment, img: Image) { this.distance = Math.max(1, distance); this.img = img; switch (alignment) { case BackgroundAlignment.Center: this.repeatX = true; this.repeatY = true; this.alignX = BackgroundAlignment.Center; this.alignY = BackgroundAlignment.Center; break; case BackgroundAlignment.Left: case BackgroundAlignment.Right: this.repeatX = false; this.repeatY = true; this.alignX = alignment; this.alignY = BackgroundAlignment.Center; break; case BackgroundAlignment.Top: case BackgroundAlignment.Bottom: this.repeatX = true; this.repeatY = false; this.alignX = BackgroundAlignment.Center; this.alignY = alignment; break; } } draw(offsetX: number, offsetY: number) { const w = screen.width; const h = screen.height; const pw = this.img.width; const ph = this.img.height; if (!pw || !ph) return; // empty image. // left, top aligned let rx = -offsetX; let ry = -offsetY; switch (this.alignX) { case BackgroundAlignment.Right: rx -= (w + pw); break; case BackgroundAlignment.Center: rx -= (w + pw) >> 1; break; } switch (this.alignY) { case BackgroundAlignment.Bottom: ry -= (h + ph); break; case BackgroundAlignment.Center: ry -= (h + ph) >> 1; break; } rx %= w; if (rx < 0) rx += w; ry %= h; if (ry < 0) ry += h; // avoid subpixel aliasing rx = Math.floor(rx); ry = Math.floor(ry); let y = 0; let py = 0; while (y < h) { py = y % ph; let dh = Math.min(ph - py, h - ry); let x = 0; let rxl = rx; while (x < w) { let px = x % pw; let dw = Math.min(pw - px, w - rxl); screen.drawImage(this.img, rxl, ry); rxl = (rxl + dw) % w; x += this.repeatX ? dw : w; } ry = (ry + dh) % h; y += this.repeatY ? dh : h; } } } }