Engine.PanZoomContainer = class extends Engine.Drawable { constructor() { super(); this.panLayer = new Engine.Drawable(); this.maskBox = new Engine.Drawable(); this.panPosition = new Vector2(0,0); this.panBounds = new Vector2(); this.onZoomChanged = new Action; this._enabled = false; this.rubberBanding = new Vector2(); this.requestedMinZoom = 0.25; this.requestedMaxZoom = 1.5; this.xTween = new Engine.SimpleTween(); this.yTween = new Engine.SimpleTween(); this.zoom = 1; this.minZoom = 0.25; this.maxZoom = 1.5; this.actualMinZoom = 0.5; this.orthoZoomSpeed = 0.005; this.mouseBlockers = [];//new List<Drawable>(); this.dragging = false; this.mouseDownPos = new Vector2(0,0); this.panPosOnMouseDown = new Vector2(0,0); this.startMouseX = 0; this.startMouseY = 0; this.containerWidth = 500; this.containerHeight = 500; this.contents = null; this.boundsInnerPadding = 30; this.moveTween = new Engine.SimpleTween(); this.addChild(this.maskBox); this.addChild(this.panLayer); this.panLayer.mask = this.maskBox.graphics; this.enabled = true; this.scrollWheelEnabled = true; } set enabled(value) { if (this._enabled == value) return; this._enabled = value; if (this._enabled) { Engine.Mouse.addEventListener("mousedown", this, this.mouseDown); Engine.Mouse.addEventListener("mousewheel", this, this.mouseWheel); } else { Engine.Mouse.removeEventListener("mousedown", this); Engine.Mouse.removeEventListener("mousewheel", this); } } mouseMove(e /*Vector2*/) { if (this.dragging) { e = Engine.Mouse.position; var deltaX = this.mouseDownPos.x - e.x; var deltaY = this.mouseDownPos.y - e.y; this.panPosition.x = this.panPosOnMouseDown.x - deltaX; this.panPosition.y = this.panPosOnMouseDown.y - deltaY; if (this.panPosition.x < this.panBounds.x && deltaX > 0) { this.rubberBanding.x = this.rubberBandingFunc(this.panBounds.x - this.panPosition.x); this.panPosition.x = this.panBounds.x - this.rubberBanding.x; } if (this.panPosition.x > -this.boundsInnerPadding && deltaX < 0) { this.rubberBanding.x = this.rubberBandingFunc(this.panPosition.x - this.boundsInnerPadding); this.panPosition.x = -this.boundsInnerPadding + this.rubberBanding.x; } if (this.panPosition.y < this.panBounds.y) { this.rubberBanding.y = this.rubberBandingFunc(this.panBounds.y - this.panPosition.y); this.panPosition.y = this.panBounds.y - this.rubberBanding.y; } if (this.panPosition.y > -this.boundsInnerPadding) { this.rubberBanding.y = this.rubberBandingFunc(this.panPosition.y - this.boundsInnerPadding); this.panPosition.y = -this.boundsInnerPadding + this.rubberBanding.y; } this.panLayer.x = this.panPosition.x; this.panLayer.y = this.panPosition.y; } } setZoomLevels(min /*float*/, max /*int*/) { this.requestedMinZoom = min; this.requestedMaxZoom = max; this.capZoomLevels(); } rubberBandingFunc(distance /*float*/) { if (distance <= 0) return 0; return (1 + Math.log10(distance / 15)) * 15 * this.zoom; } mouseUp(e /*Vector2*/) { Engine.Mouse.removeEventListener("mouseup", this); Engine.Mouse.removeEventListener("mousemove", this); this.dragging = false; this.panLayer.x = this.panPosition.x; this.panLayer.y = this.panPosition.y; if (this.panPosition.x < this.panBounds.x) { this.xTween.init((x) => this.panPosition.x = x, Engine.SimpleTween.quadEaseOut, this.panPosition.x, this.panBounds.x, 0.5); } else if (this.panPosition.x > -this.boundsInnerPadding) { this.xTween.init((x) => this.panPosition.x = x, Engine.SimpleTween.quadEaseOut, this.panPosition.x, -this.boundsInnerPadding, 0.5); } if (this.panPosition.y < this.panBounds.y) { this.yTween.init((x) => this.panPosition.y = x, Engine.SimpleTween.quadEaseOut, this.panPosition.y, this.panBounds.y, 0.5); } else if (this.panPosition.y > -this.boundsInnerPadding) { this.yTween.init((x) => this.panPosition.y = x, Engine.SimpleTween.quadEaseOut, this.panPosition.y, -this.boundsInnerPadding, 0.5); } } setZoom(zoom /*float*/) { if (this.zoom == zoom) { return; } this.zoom = zoom; if (this.zoom > maxZoom) this.zoom = maxZoom; if (this.zoom < minZoom) this.zoom = minZoom; if (this.onZoomChanged != null) this.onZoomChanged.call(this.zoom); } mouseWheel(velocity) { this.panLayer.x = this.panPosition.x; this.panLayer.y = this.panPosition.y; var startZoom = this.zoom; var mouseWheel = -velocity * 0.1; if (this.scrollWheelEnabled && mouseWheel != 0) // forward { var mouseLocation = new Vector2(Engine.Mouse.position.x, Engine.Mouse.position.y); mouseLocation.x -= this.screenX; mouseLocation.y -= this.screenY; var globalToLocal = new Vector2((1 / this.zoom) * mouseLocation.x + (-1) * this.panLayer.x * (1 / this.zoom), (1 / this.zoom) * mouseLocation.y + (-1) * this.panLayer.y * (1 / this.zoom)); this.zoom += mouseWheel * 1; if (this.zoom > this.maxZoom) this.zoom = this.maxZoom; if (this.zoom < this.minZoom) this.zoom = this.minZoom; var pannedX = this.panPosition.x / this.panBounds.x; var pannedY = this.panPosition.y / this.panBounds.y; this.scaleToFit(false); var globalToLocalZoomed = new Vector2((1 / this.zoom) * mouseLocation.x + (-1) * this.panLayer.x * (1 / this.zoom),(1 / this.zoom) * mouseLocation.y + (-1) * this.panLayer.y * (1 / this.zoom)); var scaledDiff = new Vector2((globalToLocalZoomed.x - globalToLocal.x) * this.zoom, (globalToLocalZoomed.y - globalToLocal.y) * this.zoom) ; this.panPosition.x += scaledDiff.x; this.panPosition.y += scaledDiff.y; if (this.panPosition.x < this.panBounds.x) this.panPosition.x = this.panBounds.x; if (this.panPosition.x > -this.boundsInnerPadding) this.panPosition.x = -this.boundsInnerPadding; if (this.panPosition.y < this.panBounds.y) this.panPosition.y = this.panBounds.y; if (this.panPosition.y > -this.boundsInnerPadding) this.panPosition.y = -this.boundsInnerPadding; this.panLayer.x = this.panPosition.x; this.panLayer.y = this.panPosition.y; this.boundsInnerPadding = 30 * this.zoom; this.yTween.stop(); this.xTween.stop(); } if (startZoom != this.zoom && this.onZoomChanged != null) this.onZoomChanged.call(this.zoom); } update(timeElapsed /*float*/) { this.panLayer.x = this.panPosition.x; this.panLayer.y = this.panPosition.y; var startZoom = this.zoom; // If there are two touches on the device... /* if (Input.touchCount == 2) { this.dragging = false; // Store both touches. var touchZero = Input.GetTouch(0); var touchOne = Input.GetTouch(1); // Find the position in the previous frame of each touch. var touchZeroPrevPos = touchZero.position - touchZero.deltaPosition; var touchOnePrevPos = touchOne.position - touchOne.deltaPosition; // Find the magnitude of the vector (the distance) between the touches in each frame. var prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude; var touchDeltaMag = (touchZero.position - touchOne.position).magnitude; // Find the difference in the distances between each frame. float deltaMagnitudeDiff = prevTouchDeltaMag - touchDeltaMag; // ... change the orthographic size based on the change in distance between the touches. zoom += -deltaMagnitudeDiff * orthoZoomSpeed; if (zoom > maxZoom) zoom = maxZoom; if (zoom < minZoom) zoom = minZoom; var pannedX = panPosition.x / panBounds.x; var pannedY = panPosition.y / panBounds.y; this.scaleToFit(false); var * = zoom; var * = zoom; this.panPosition.x = panBounds.x * pannedX; this.panPosition.y = panBounds.y * pannedY; this.panLayer.x = panPosition.x; this.panLayer.y = panPosition.y; }*/ if (startZoom != this.zoom && this.onZoomChanged != null) this.onZoomChanged.call(this.zoom); } mouseDown(v /*Vector2*/) { var v = Engine.Mouse.position; //Make sure the mouse isn't over something that is suppose to block mouse events into this container. for (var i = 0; i < this.mouseBlockers.length; i++) { if (this.mouseBlockers[i].screenRect.contains(v.x, v.y)) return; } if (this.maskBox.graphics.getBounds() != null && this.maskBox.graphics.getBounds().contains(v.x, v.y)) { this.startMouseX = Engine.Mouse.position.x; this.startMouseY = Engine.Mouse.position.y; Engine.Mouse.addEventListener("mouseup", this, this.mouseUp); Engine.Mouse.addEventListener("mousemove", this, this.mouseMove); this.mouseDownPos.x = this.startMouseX; this.mouseDownPos.y = this.startMouseY; this.panPosOnMouseDown.x = this.panPosition.x; this.panPosOnMouseDown.y = this.panPosition.y; this.dragging = true; this.yTween.stop(); this.xTween.stop(); } } setSize(width /*int*/, height /*int*/) { this.containerWidth = width; this.containerHeight = height; this.maskBox.setAsBox(this.maskBox.graphics, width, height, Colors.red, 1); this.capZoomLevels(); this.scaleToFit(false); } capZoomLevels() { if (this.contents != null) { //if the min zoom is too small, make sure the item fits inside the bounds. this.minZoom = Math.max(this.containerWidth / (this.contents.width - this.boundsInnerPadding * 2), this.containerHeight / (this.contents.height - this.boundsInnerPadding * 2)); if (this.requestedMinZoom > this.minZoom) this.minZoom = this.requestedMinZoom; //if the max zoom is too small... make the max zoom the min allowable zoom. if (this.requestedMaxZoom < this.minZoom) this.maxZoom = this.minZoom; this.zoom = Math.max(this.minZoom, Math.min(this.maxZoom, this.zoom)); } } setContents(contents /*Drawable*/) { this.contents = contents; this.panLayer.addChild(contents); this.capZoomLevels(); this.scaleToFit(); if (this.onZoomChanged != null) this.onZoomChanged.call(this.zoom); } scaleToFit(center /*bool*/) { if (center === undefined) center = true; this.scale.x = 1; this.scale.y = 1; var scale = 1; this.panLayer.additionalMouseRect = new Rect(0, 0, this.panLayer.width, this.panLayer.height); scale = this.zoom; this.panLayer.scale.x = scale; this.panLayer.scale.y = scale; if(this.panLayer.width < EngineSettings.screenWidth || this.panLayer.height < EngineSettings.screenHeight) { var scaleX = EngineSettings.screenWidth / this.panLayer.width; var scaleY = EngineSettings.screenHeight / this.panLayer.height; var scaleDiff = Math.max(scaleX, scaleY); this.panLayer.scale.x *= scaleDiff; this.panLayer.scale.y *= scaleDiff; } var backgroundHeight = this.panLayer.height; var backgroundWidth = this.panLayer.width; this.panBounds.x = (this.containerWidth - backgroundWidth) + this.boundsInnerPadding; this.panBounds.y = (this.containerHeight - backgroundHeight) + this.boundsInnerPadding; if (this.xTween != null) this.xTween.stop(); if (this.yTween != null) this.yTween.stop(); if (center) this.centerCamera(); } centerCameraOnPosition(x /*float*/, y /*float*/) { this.panPosition.x = -(x * this.zoom - this.containerWidth / 2); this.panPosition.y = -(y * this.zoom - this.containerHeight / 2); if (this.panPosition.x < this.panBounds.x) { this.panPosition.x = this.panBounds.x; } if (this.panPosition.x > -this.boundsInnerPadding) { this.panPosition.x = -this.boundsInnerPadding; } if (this.panPosition.y < this.panBounds.y) { this.panPosition.y = this.panBounds.y; } if (this.panPosition.y > -this.boundsInnerPadding) { this.panPosition.y = -this.boundsInnerPadding; } this.panLayer.x = this.panPosition.x; this.panLayer.y = this.panPosition.y; } centerCamera() { this.panPosition.x = this.panBounds.x / 2; this.panPosition.y = this.panBounds.y / 2; this.panLayer.x = this.panPosition.x; this.panLayer.y = this.panPosition.y; } // new center is previous center + vector moveByDistance(v /*Vector2*/) { var center = new Vector2((-this.panPosition.x + this.containerWidth / 2) / this.zoom,(-this.panPosition.y + this.containerHeight / 2) / this.zoom); this.centerCameraOnPosition(center.x + v.x/this.zoom, center.y + v.y/this.zoom); } tweenByDistance(v /*Vector2*/) { var center = new Vector2((-this.panPosition.x + this.containerWidth / 2) / this.zoom,(-this.panPosition.y + this.containerHeight / 2) / this.zoom); this.moveTween.init((p) => this.centerCameraOnPosition(center.x + this.p * v.x / this.zoom, center.y + this.p * v.y / this.zoom), Engine.SimpleTween.easeLinear, 0, 1, 0.25); } }