UNPKG

gl2d

Version:

2D graphics package for WebGL

338 lines 13.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var point_1 = require("../struct/point"); /** * A rendering surface linked to an HTMLCanvasElement (the drawing buffer). */ var Surface = (function () { /** * Creates a new rendering surface. * @param drawingBuffer the canvas that serves as the drawing buffer for this surface. * @param renderer the renderer responsible for rendering the surface. */ function Surface(drawingBuffer, renderer) { /** * True if a request has been made to re-render this surface. */ this.hasRenderRequest = false; this.drawingBuffer = drawingBuffer; this.clientRect = drawingBuffer.getBoundingClientRect(); this.renderer = renderer; this.renderer.onSurfaceCreated(); } /** * Requests that this surface be re-rendered. */ Surface.prototype.requestRender = function () { this.hasRenderRequest = true; }; /** * Re-renders this surface if it has a render request. */ Surface.prototype.onAnimationFrame = function () { if (this.hasRenderRequest) { this.renderer.onDrawFrame(); this.hasRenderRequest = false; } }; /** * Resizes this surface if the specified width and height differ from the current width and height. * @param width the width to set on the surface. Defaults to the client width of the drawing buffer. * @param height the height to set on the surface. Defaults to the client height of the drawing buffer. */ Surface.prototype.resize = function (width, height) { if (width === void 0) { width = this.drawingBuffer.clientWidth; } if (height === void 0) { height = this.drawingBuffer.clientHeight; } // If width or height has changed if (this.drawingBuffer.width !== width || this.drawingBuffer.height !== height) { // Resize canvas to specified dimensions this.drawingBuffer.width = width; this.drawingBuffer.height = height; // Get new bounding box this.clientRect = this.drawingBuffer.getBoundingClientRect(); // Notify renderer of surface change this.renderer.onSurfaceChanged(width, height); // Request render to show changes this.requestRender(); } }; /** * Sends a request to offset the image displayed by this surface. * @param desiredOffset the desired offset, which is automatically adjusted according to the renderer's camera settings. * @returns the actual offset. */ Surface.prototype.offset = function (desiredOffset) { var camera = this.renderer.camera; var actual = camera.offset(desiredOffset); if (!actual.equalsScalar(0)) { camera.updateMatrix(); this.requestRender(); } return actual; }; /** * Sends a request to zoom into the image displayed by this surface. * @param desiredScaleFactor the desired scale factor, which is automatically adjusted according to the renderer's camera settings. * @returns the actual scale factor. */ Surface.prototype.zoomIn = function (desiredScaleFactor) { var camera = this.renderer.camera; var actual = camera.zoomIn(desiredScaleFactor); if (actual !== 1) { camera.updateMatrix(); this.requestRender(); } return actual; }; /** * Sends a request to zoom out of the image displayed by this surface. * @param desiredScaleFactor the desired scale factor, which is automatically adjusted according to the renderer's camera settings. * @returns the actual scale factor. */ Surface.prototype.zoomOut = function (desiredScaleFactor) { return this.zoomIn(1 / desiredScaleFactor); }; /** * Sends a request to zoom into the image displayed by this surface while fixing the specified focus point. * @param desiredScaleFactor the desired scale factor, which is automatically adjusted according to the renderer's camera settings. * @param focus the focus point. * @returns the actual scale factor and camera offset. */ Surface.prototype.zoomToPoint = function (desiredScaleFactor, focus) { var camera = this.renderer.camera; var actual = camera.zoomToPoint(desiredScaleFactor, focus); if (actual.scaleFactor !== 1 || !actual.offset.equalsScalar(0)) { camera.updateMatrix(); this.requestRender(); } return actual; }; /** * Maps a screen cordinate to canvas space. * @param screen the screen coordinate. * @param dst where to store the result. */ Surface.prototype.screenToCanvas = function (screenPoint, dst) { if (dst === void 0) { dst = new point_1.Point(); } dst.x = screenPoint.clientX - this.clientRect.left; dst.y = screenPoint.clientY - this.clientRect.top; return dst; }; /** * Maps a screen coordinate to NDC space [0,1]. * @param screen the screen coordinate. * @param dst where to store the result. */ Surface.prototype.screenToNdc = function (screenPoint, dst) { if (dst === void 0) { dst = new point_1.Point(); } this.screenToCanvas(screenPoint, dst); this.canvasToNdc(dst, dst); return dst; }; /** * Maps a screen coordinate to clip space. * @param screen the screen coordinate. * @param dst where to store the result. */ Surface.prototype.screenToWorld = function (screenPoint, dst) { if (dst === void 0) { dst = new point_1.Point(); } this.screenToCanvas(screenPoint, dst); this.canvasToNdc(dst, dst); this.ndcToWorld(dst, dst); return dst; }; /** * Maps a canvas coordinate to NDC space [0,1]. * @param p the canvas coordinate. * @param dst where to store the result. */ Surface.prototype.canvasToNdc = function (canvasPoint, dst) { if (dst === void 0) { dst = new point_1.Point(); } // Normalize the coordinate by width and height of canvas var width = this.clientRect.width; var height = this.clientRect.height; dst.x = canvasPoint.x / width; dst.y = (height - canvasPoint.y) / height; // Flip in y axis return dst; }; /** * Maps a normalized device coordinate (NDC) to world space. * @param p the normalized device coordinate. * @param dst where to store the result. */ Surface.prototype.ndcToWorld = function (ndc, dst) { if (dst === void 0) { dst = new point_1.Point(); } // Depends on what is currently in view var view = this.renderer.camera.view; dst.x = view.left + (ndc.x * view.width()); dst.y = view.bottom + (ndc.y * view.height()); return dst; }; /** * Invokes the specified callback whenever a mouse action occurs on this surface. */ Surface.prototype.onMouseEvent = function (callback) { var _this = this; /** * Whether or not a mouse button is currently pressed. */ var isPressed = false; // Listen for mouse events window.addEventListener("mousedown", function (e) { // Return if not inside element if (e.target !== _this.drawingBuffer) return; // A mouse button is pressed isPressed = true; // Tell the browser we're handling this event e.preventDefault(); e.stopPropagation(); // Get action and pass to callback callback(_this.getSurfaceMouseEvent(e, 0 /* Start */)); }, false); window.addEventListener("mousemove", function (e) { // If mouse is pressed if (isPressed) { // Tell browser we're handling this event e.preventDefault(); e.stopPropagation(); // Process as a drag action callback(_this.getSurfaceMouseEvent(e, 2 /* Drag */)); } else { // Otherwise process as a move action callback(_this.getSurfaceMouseEvent(e, 1 /* Move */)); } }, false); window.addEventListener("mouseout", function (e) { // Ignore if mouse not pressed if (!isPressed) { return; } // Tell browser we're handling this event e.preventDefault(); e.stopPropagation(); // Get action and pass to callback callback(_this.getSurfaceMouseEvent(e, 3 /* Leave */)); }, false); window.addEventListener("mouseup", function (e) { // Ignore if mouse not pressed if (!isPressed) { return; } // Mouse is no longer pressed isPressed = false; // Tell browser we're handling this event e.preventDefault(); e.stopPropagation(); // Get action and pass to callback callback(_this.getSurfaceMouseEvent(e, 4 /* End */)); }, false); }; Surface.prototype.getSurfaceMouseEvent = function (event, status) { return { target: this, src: event, status: status, cursor: this.screenToWorld(event) }; }; /** * Invokes the specified callback whenever a touch action occurs on this surface. */ Surface.prototype.onTouchEvent = function (callback) { var _this = this; /** * Whether or no a touch event is currently active. */ var isActive = false; // Listen for touch events window.addEventListener("touchstart", function (e) { // Return if not inside element if (e.target !== _this.drawingBuffer) return; // Mark touch event active isActive = true; // Tell the browser we're handling this event e.preventDefault(); e.stopPropagation(); // Get action and pass to callback callback(_this.getSurfaceTouchEvent(e, 0 /* Start */)); }, false); window.addEventListener("touchmove", function (e) { // Don't send callback if touch action did not originate inside the element if (!isActive) return; // Tell the browser we're handling this event e.preventDefault(); e.stopPropagation(); // Get action and pass to callback callback(_this.getSurfaceTouchEvent(e, 2 /* Drag */)); }, false); window.addEventListener("touchleave", function (e) { // Don't send callback if touch action did not originate inside the element if (!isActive) return; // Tell the browser we're handling this event e.preventDefault(); e.stopPropagation(); // Get action and pass to callback callback(_this.getSurfaceTouchEvent(e, 3 /* Leave */)); }, false); window.addEventListener("touchend", function (e) { // Don't send callback if touch action did not originate inside the element if (!isActive) return; // Touch event is no longer active isActive = false; // Tell the browser we're handling this event e.preventDefault(); e.stopPropagation(); // Get action and pass to callback callback(_this.getSurfaceTouchEvent(e, 4 /* End */)); }, false); }; Surface.prototype.getSurfaceTouchEvent = function (event, status) { return { target: this, src: event, status: status, pointers: this.getPointers(event) }; }; Surface.prototype.getPointers = function (event) { var pointers = []; var touches = event.touches; // Convert each of the touches to world space and add to array for (var i = 0; i < touches.length; i++) { pointers[i] = this.screenToWorld(touches[i]); } return pointers; }; /** * Invokes the specified callback whenever a scroll action occurs on this surface. */ Surface.prototype.onWheelEvent = function (callback) { var _this = this; document.addEventListener(this.getScrollSupport(), function (e) { if (e.target === _this.drawingBuffer) { e.preventDefault(); e.stopPropagation(); var isUpward = e.deltaY < 0 || e.detail < 0 || e.wheelDelta > 0; callback({ isUpward: isUpward, cursor: _this.screenToWorld(e), target: _this, src: e }); } }); }; /** * From https://developer.mozilla.org/en-US/docs/Web/Events/wheel */ Surface.prototype.getScrollSupport = function () { return "onwheel" in document.createElement("div") ? "wheel" : document.onmousewheel !== undefined ? "mousewheel" : "DOMMouseScroll"; // let's assume that remaining browsers are older Firefox }; return Surface; }()); exports.Surface = Surface; ; //# sourceMappingURL=surface.js.map