UNPKG

xl-pano

Version:

一个基于 Typescript 的,同时支持立方体和球体场景的轻量开源库。

1,452 lines (1,327 loc) 116 kB
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define([], factory); else if(typeof exports === 'object') exports["XLPano"] = factory(); else root["XLPano"] = factory(); })(self, function() { return /******/ (() => { // webpackBootstrap /******/ "use strict"; /******/ var __webpack_modules__ = ({ /***/ "./src/shader/cube/index.frag": /*!************************************!*\ !*** ./src/shader/cube/index.frag ***! \************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ("precision mediump float;\n\nuniform sampler2D f_Sampler;\nuniform sampler2D r_Sampler;\nuniform sampler2D u_Sampler;\nuniform sampler2D l_Sampler;\nuniform sampler2D d_Sampler;\nuniform sampler2D b_Sampler;\nvarying vec2 v_TexCoord;\n// 标记每个面 0-f 1-r 2-u 3-l 4-d 5-b\nuniform int u_Face;\n\nvoid main() {\n if (u_Face == 0) {\n gl_FragColor = texture2D(f_Sampler, v_TexCoord);\n } else if (u_Face == 1) {\n gl_FragColor = texture2D(r_Sampler, v_TexCoord);\n } else if (u_Face == 2) {\n gl_FragColor = texture2D(u_Sampler, v_TexCoord);\n } else if (u_Face == 3) {\n gl_FragColor = texture2D(l_Sampler, v_TexCoord);\n } else if (u_Face == 4) {\n gl_FragColor = texture2D(d_Sampler, v_TexCoord);\n } else {\n gl_FragColor = texture2D(b_Sampler, v_TexCoord);\n }\n}\n"); /***/ }), /***/ "./src/shader/cube/index.vert": /*!************************************!*\ !*** ./src/shader/cube/index.vert ***! \************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ("attribute vec4 a_Position;\nattribute vec2 a_TexCoord;\nvarying vec2 v_TexCoord;\nuniform mat4 u_MvpMatrix;\n\nvoid main() {\n gl_Position = u_MvpMatrix * a_Position;\n v_TexCoord = a_TexCoord;\n}\n"); /***/ }), /***/ "./src/shader/sphere/index.frag": /*!**************************************!*\ !*** ./src/shader/sphere/index.frag ***! \**************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ("precision mediump float;\n\nuniform sampler2D u_Sampler;\nvarying vec2 v_TexCoord;\n\nvoid main() {\n gl_FragColor = texture2D(u_Sampler, v_TexCoord);\n}\n"); /***/ }), /***/ "./src/shader/sphere/index.vert": /*!**************************************!*\ !*** ./src/shader/sphere/index.vert ***! \**************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ("attribute vec4 a_Position;\nattribute vec2 a_TexCoord;\nvarying vec2 v_TexCoord;\nuniform mat4 u_MvpMatrix;\n\nvoid main() {\n gl_Position = u_MvpMatrix * a_Position;\n v_TexCoord = a_TexCoord;\n}\n"); /***/ }), /***/ "./src/utils/webgl-debug.js": /*!**********************************!*\ !*** ./src/utils/webgl-debug.js ***! \**********************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); const WebGLDebugUtils = function() { /** * Wrapped logging function. * @param {string} msg Message to log. */ var log = function(msg) { if (window.console && window.console.log) { window.console.log(msg); } }; /** * Which arguements are enums. * @type {!Object.<number, string>} */ var glValidEnumContexts = { // Generic setters and getters 'enable': { 0:true }, 'disable': { 0:true }, 'getParameter': { 0:true }, // Rendering 'drawArrays': { 0:true }, 'drawElements': { 0:true, 2:true }, // Shaders 'createShader': { 0:true }, 'getShaderParameter': { 1:true }, 'getProgramParameter': { 1:true }, // Vertex attributes 'getVertexAttrib': { 1:true }, 'vertexAttribPointer': { 2:true }, // Textures 'bindTexture': { 0:true }, 'activeTexture': { 0:true }, 'getTexParameter': { 0:true, 1:true }, 'texParameterf': { 0:true, 1:true }, 'texParameteri': { 0:true, 1:true, 2:true }, 'texImage2D': { 0:true, 2:true, 6:true, 7:true }, 'texSubImage2D': { 0:true, 6:true, 7:true }, 'copyTexImage2D': { 0:true, 2:true }, 'copyTexSubImage2D': { 0:true }, 'generateMipmap': { 0:true }, // Buffer objects 'bindBuffer': { 0:true }, 'bufferData': { 0:true, 2:true }, 'bufferSubData': { 0:true }, 'getBufferParameter': { 0:true, 1:true }, // Renderbuffers and framebuffers 'pixelStorei': { 0:true, 1:true }, 'readPixels': { 4:true, 5:true }, 'bindRenderbuffer': { 0:true }, 'bindFramebuffer': { 0:true }, 'checkFramebufferStatus': { 0:true }, 'framebufferRenderbuffer': { 0:true, 1:true, 2:true }, 'framebufferTexture2D': { 0:true, 1:true, 2:true }, 'getFramebufferAttachmentParameter': { 0:true, 1:true, 2:true }, 'getRenderbufferParameter': { 0:true, 1:true }, 'renderbufferStorage': { 0:true, 1:true }, // Frame buffer operations (clear, blend, depth test, stencil) 'clear': { 0:true }, 'depthFunc': { 0:true }, 'blendFunc': { 0:true, 1:true }, 'blendFuncSeparate': { 0:true, 1:true, 2:true, 3:true }, 'blendEquation': { 0:true }, 'blendEquationSeparate': { 0:true, 1:true }, 'stencilFunc': { 0:true }, 'stencilFuncSeparate': { 0:true, 1:true }, 'stencilMaskSeparate': { 0:true }, 'stencilOp': { 0:true, 1:true, 2:true }, 'stencilOpSeparate': { 0:true, 1:true, 2:true, 3:true }, // Culling 'cullFace': { 0:true }, 'frontFace': { 0:true }, }; /** * Map of numbers to names. * @type {Object} */ var glEnums = null; /** * Initializes this module. Safe to call more than once. * @param {!WebGLRenderingContext} ctx A WebGL context. If * you have more than one context it doesn't matter which one * you pass in, it is only used to pull out constants. */ function init(ctx) { if (glEnums == null) { glEnums = { }; for (var propertyName in ctx) { if (typeof ctx[propertyName] == 'number') { glEnums[ctx[propertyName]] = propertyName; } } } } /** * Checks the utils have been initialized. */ function checkInit() { if (glEnums == null) { throw 'WebGLDebugUtils.init(ctx) not called'; } } /** * Returns true or false if value matches any WebGL enum * @param {*} value Value to check if it might be an enum. * @return {boolean} True if value matches one of the WebGL defined enums */ function mightBeEnum(value) { checkInit(); return (glEnums[value] !== undefined); } /** * Gets an string version of an WebGL enum. * * Example: * var str = WebGLDebugUtil.glEnumToString(ctx.getError()); * * @param {number} value Value to return an enum for * @return {string} The string version of the enum. */ function glEnumToString(value) { checkInit(); var name = glEnums[value]; return (name !== undefined) ? name : ("*UNKNOWN WebGL ENUM (0x" + value.toString(16) + ")"); } /** * Returns the string version of a WebGL argument. * Attempts to convert enum arguments to strings. * @param {string} functionName the name of the WebGL function. * @param {number} argumentIndx the index of the argument. * @param {*} value The value of the argument. * @return {string} The value as a string. */ function glFunctionArgToString(functionName, argumentIndex, value) { var funcInfo = glValidEnumContexts[functionName]; if (funcInfo !== undefined) { if (funcInfo[argumentIndex]) { return glEnumToString(value); } } return value.toString(); } /** * Given a WebGL context returns a wrapped context that calls * gl.getError after every command and calls a function if the * result is not gl.NO_ERROR. * * @param {!WebGLRenderingContext} ctx The webgl context to * wrap. * @param {!function(err, funcName, args): void} opt_onErrorFunc * The function to call when gl.getError returns an * error. If not specified the default function calls * console.log with a message. */ function makeDebugContext(ctx, opt_onErrorFunc) { init(ctx); opt_onErrorFunc = opt_onErrorFunc || function(err, functionName, args) { // apparently we can't do args.join(","); var argStr = ""; for (var ii = 0; ii < args.length; ++ii) { argStr += ((ii == 0) ? '' : ', ') + glFunctionArgToString(functionName, ii, args[ii]); } log("WebGL error "+ glEnumToString(err) + " in "+ functionName + "(" + argStr + ")"); }; // Holds booleans for each GL error so after we get the error ourselves // we can still return it to the client app. var glErrorShadow = { }; // Makes a function that calls a WebGL function and then calls getError. function makeErrorWrapper(ctx, functionName) { return function() { var result = ctx[functionName].apply(ctx, arguments); var err = ctx.getError(); if (err != 0) { glErrorShadow[err] = true; opt_onErrorFunc(err, functionName, arguments); } return result; }; } // Make a an object that has a copy of every property of the WebGL context // but wraps all functions. var wrapper = {}; for (var propertyName in ctx) { if (typeof ctx[propertyName] == 'function') { wrapper[propertyName] = makeErrorWrapper(ctx, propertyName); } else { wrapper[propertyName] = ctx[propertyName]; } } // Override the getError function with one that returns our saved results. wrapper.getError = function() { for (var err in glErrorShadow) { if (glErrorShadow[err]) { glErrorShadow[err] = false; return err; } } return ctx.NO_ERROR; }; return wrapper; } function resetToInitialState(ctx) { var numAttribs = ctx.getParameter(ctx.MAX_VERTEX_ATTRIBS); var tmp = ctx.createBuffer(); ctx.bindBuffer(ctx.ARRAY_BUFFER, tmp); for (var ii = 0; ii < numAttribs; ++ii) { ctx.disableVertexAttribArray(ii); ctx.vertexAttribPointer(ii, 4, ctx.FLOAT, false, 0, 0); ctx.vertexAttrib1f(ii, 0); } ctx.deleteBuffer(tmp); var numTextureUnits = ctx.getParameter(ctx.MAX_TEXTURE_IMAGE_UNITS); for (var ii = 0; ii < numTextureUnits; ++ii) { ctx.activeTexture(ctx.TEXTURE0 + ii); ctx.bindTexture(ctx.TEXTURE_CUBE_MAP, null); ctx.bindTexture(ctx.TEXTURE_2D, null); } ctx.activeTexture(ctx.TEXTURE0); ctx.useProgram(null); ctx.bindBuffer(ctx.ARRAY_BUFFER, null); ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, null); ctx.bindFramebuffer(ctx.FRAMEBUFFER, null); ctx.bindRenderbuffer(ctx.RENDERBUFFER, null); ctx.disable(ctx.BLEND); ctx.disable(ctx.CULL_FACE); ctx.disable(ctx.DEPTH_TEST); ctx.disable(ctx.DITHER); ctx.disable(ctx.SCISSOR_TEST); ctx.blendColor(0, 0, 0, 0); ctx.blendEquation(ctx.FUNC_ADD); ctx.blendFunc(ctx.ONE, ctx.ZERO); ctx.clearColor(0, 0, 0, 0); ctx.clearDepth(1); ctx.clearStencil(-1); ctx.colorMask(true, true, true, true); ctx.cullFace(ctx.BACK); ctx.depthFunc(ctx.LESS); ctx.depthMask(true); ctx.depthRange(0, 1); ctx.frontFace(ctx.CCW); ctx.hint(ctx.GENERATE_MIPMAP_HINT, ctx.DONT_CARE); ctx.lineWidth(1); ctx.pixelStorei(ctx.PACK_ALIGNMENT, 4); ctx.pixelStorei(ctx.UNPACK_ALIGNMENT, 4); ctx.pixelStorei(ctx.UNPACK_FLIP_Y_WEBGL, false); ctx.pixelStorei(ctx.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); // TODO: Delete this IF. if (ctx.UNPACK_COLORSPACE_CONVERSION_WEBGL) { ctx.pixelStorei(ctx.UNPACK_COLORSPACE_CONVERSION_WEBGL, ctx.BROWSER_DEFAULT_WEBGL); } ctx.polygonOffset(0, 0); ctx.sampleCoverage(1, false); ctx.scissor(0, 0, ctx.canvas.width, ctx.canvas.height); ctx.stencilFunc(ctx.ALWAYS, 0, 0xFFFFFFFF); ctx.stencilMask(0xFFFFFFFF); ctx.stencilOp(ctx.KEEP, ctx.KEEP, ctx.KEEP); ctx.viewport(0, 0, ctx.canvas.clientWidth, ctx.canvas.clientHeight); ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT | ctx.STENCIL_BUFFER_BIT); // TODO: This should NOT be needed but Firefox fails with 'hint' while(ctx.getError()); } function makeLostContextSimulatingContext(ctx) { var wrapper_ = {}; var contextId_ = 1; var contextLost_ = false; var resourceId_ = 0; var resourceDb_ = []; var onLost_ = undefined; var onRestored_ = undefined; var nextOnRestored_ = undefined; // Holds booleans for each GL error so can simulate errors. var glErrorShadow_ = { }; function isWebGLObject(obj) { //return false; return (obj instanceof WebGLBuffer || obj instanceof WebGLFramebuffer || obj instanceof WebGLProgram || obj instanceof WebGLRenderbuffer || obj instanceof WebGLShader || obj instanceof WebGLTexture); } function checkResources(args) { for (var ii = 0; ii < args.length; ++ii) { var arg = args[ii]; if (isWebGLObject(arg)) { return arg.__webglDebugContextLostId__ == contextId_; } } return true; } function clearErrors() { var k = Object.keys(glErrorShadow_); for (var ii = 0; ii < k.length; ++ii) { delete glErrorShdow_[k]; } } // Makes a function that simulates WebGL when out of context. function makeLostContextWrapper(ctx, functionName) { var f = ctx[functionName]; return function() { // Only call the functions if the context is not lost. if (!contextLost_) { if (!checkResources(arguments)) { glErrorShadow_[ctx.INVALID_OPERATION] = true; return; } var result = f.apply(ctx, arguments); return result; } }; } for (var propertyName in ctx) { if (typeof ctx[propertyName] == 'function') { wrapper_[propertyName] = makeLostContextWrapper(ctx, propertyName); } else { wrapper_[propertyName] = ctx[propertyName]; } } function makeWebGLContextEvent(statusMessage) { return {statusMessage: statusMessage}; } function freeResources() { for (var ii = 0; ii < resourceDb_.length; ++ii) { var resource = resourceDb_[ii]; if (resource instanceof WebGLBuffer) { ctx.deleteBuffer(resource); } else if (resource instanceof WebctxFramebuffer) { ctx.deleteFramebuffer(resource); } else if (resource instanceof WebctxProgram) { ctx.deleteProgram(resource); } else if (resource instanceof WebctxRenderbuffer) { ctx.deleteRenderbuffer(resource); } else if (resource instanceof WebctxShader) { ctx.deleteShader(resource); } else if (resource instanceof WebctxTexture) { ctx.deleteTexture(resource); } } } wrapper_.loseContext = function() { if (!contextLost_) { contextLost_ = true; ++contextId_; while (ctx.getError()); clearErrors(); glErrorShadow_[ctx.CONTEXT_LOST_WEBGL] = true; setTimeout(function() { if (onLost_) { onLost_(makeWebGLContextEvent("context lost")); } }, 0); } }; wrapper_.restoreContext = function() { if (contextLost_) { if (onRestored_) { setTimeout(function() { freeResources(); resetToInitialState(ctx); contextLost_ = false; if (onRestored_) { var callback = onRestored_; onRestored_ = nextOnRestored_; nextOnRestored_ = undefined; callback(makeWebGLContextEvent("context restored")); } }, 0); } else { throw "You can not restore the context without a listener" } } }; // Wrap a few functions specially. wrapper_.getError = function() { if (!contextLost_) { var err; while (err = ctx.getError()) { glErrorShadow_[err] = true; } } for (var err in glErrorShadow_) { if (glErrorShadow_[err]) { delete glErrorShadow_[err]; return err; } } return ctx.NO_ERROR; }; var creationFunctions = [ "createBuffer", "createFramebuffer", "createProgram", "createRenderbuffer", "createShader", "createTexture" ]; for (var ii = 0; ii < creationFunctions.length; ++ii) { var functionName = creationFunctions[ii]; wrapper_[functionName] = function(f) { return function() { if (contextLost_) { return null; } var obj = f.apply(ctx, arguments); obj.__webglDebugContextLostId__ = contextId_; resourceDb_.push(obj); return obj; }; }(ctx[functionName]); } var functionsThatShouldReturnNull = [ "getActiveAttrib", "getActiveUniform", "getBufferParameter", "getContextAttributes", "getAttachedShaders", "getFramebufferAttachmentParameter", "getParameter", "getProgramParameter", "getProgramInfoLog", "getRenderbufferParameter", "getShaderParameter", "getShaderInfoLog", "getShaderSource", "getTexParameter", "getUniform", "getUniformLocation", "getVertexAttrib" ]; for (var ii = 0; ii < functionsThatShouldReturnNull.length; ++ii) { var functionName = functionsThatShouldReturnNull[ii]; wrapper_[functionName] = function(f) { return function() { if (contextLost_) { return null; } return f.apply(ctx, arguments); } }(wrapper_[functionName]); } var isFunctions = [ "isBuffer", "isEnabled", "isFramebuffer", "isProgram", "isRenderbuffer", "isShader", "isTexture" ]; for (var ii = 0; ii < isFunctions.length; ++ii) { var functionName = isFunctions[ii]; wrapper_[functionName] = function(f) { return function() { if (contextLost_) { return false; } return f.apply(ctx, arguments); } }(wrapper_[functionName]); } wrapper_.checkFramebufferStatus = function(f) { return function() { if (contextLost_) { return ctx.FRAMEBUFFER_UNSUPPORTED; } return f.apply(ctx, arguments); }; }(wrapper_.checkFramebufferStatus); wrapper_.getAttribLocation = function(f) { return function() { if (contextLost_) { return -1; } return f.apply(ctx, arguments); }; }(wrapper_.getAttribLocation); wrapper_.getVertexAttribOffset = function(f) { return function() { if (contextLost_) { return 0; } return f.apply(ctx, arguments); }; }(wrapper_.getVertexAttribOffset); wrapper_.isContextLost = function() { return contextLost_; }; function wrapEvent(listener) { if (typeof(listener) == "function") { return listener; } else { return function(info) { listener.handleEvent(info); } } } wrapper_.registerOnContextLostListener = function(listener) { onLost_ = wrapEvent(listener); }; wrapper_.registerOnContextRestoredListener = function(listener) { if (contextLost_) { nextOnRestored_ = wrapEvent(listener); } else { onRestored_ = wrapEvent(listener); } } return wrapper_; } return { /** * Initializes this module. Safe to call more than once. * @param {!WebGLRenderingContext} ctx A WebGL context. If * you have more than one context it doesn't matter which one * you pass in, it is only used to pull out constants. */ 'init': init, /** * Returns true or false if value matches any WebGL enum * @param {*} value Value to check if it might be an enum. * @return {boolean} True if value matches one of the WebGL defined enums */ 'mightBeEnum': mightBeEnum, /** * Gets an string version of an WebGL enum. * * Example: * WebGLDebugUtil.init(ctx); * var str = WebGLDebugUtil.glEnumToString(ctx.getError()); * * @param {number} value Value to return an enum for * @return {string} The string version of the enum. */ 'glEnumToString': glEnumToString, /** * Converts the argument of a WebGL function to a string. * Attempts to convert enum arguments to strings. * * Example: * WebGLDebugUtil.init(ctx); * var str = WebGLDebugUtil.glFunctionArgToString('bindTexture', 0, gl.TEXTURE_2D); * * would return 'TEXTURE_2D' * * @param {string} functionName the name of the WebGL function. * @param {number} argumentIndx the index of the argument. * @param {*} value The value of the argument. * @return {string} The value as a string. */ 'glFunctionArgToString': glFunctionArgToString, /** * Given a WebGL context returns a wrapped context that calls * gl.getError after every command and calls a function if the * result is not NO_ERROR. * * You can supply your own function if you want. For example, if you'd like * an exception thrown on any GL error you could do this * * function throwOnGLError(err, funcName, args) { * throw WebGLDebugUtils.glEnumToString(err) + " was caused by call to" + * funcName; * }; * * ctx = WebGLDebugUtils.makeDebugContext( * canvas.getContext("webgl"), throwOnGLError); * * @param {!WebGLRenderingContext} ctx The webgl context to wrap. * @param {!function(err, funcName, args): void} opt_onErrorFunc The function * to call when gl.getError returns an error. If not specified the default * function calls console.log with a message. */ 'makeDebugContext': makeDebugContext, /** * Given a WebGL context returns a wrapped context that adds 4 * functions. * * ctx.loseContext: * simulates a lost context event. * * ctx.restoreContext: * simulates the context being restored. * * ctx.registerOnContextLostListener(listener): * lets you register a listener for context lost. Use instead * of addEventListener('webglcontextlostevent', listener); * * ctx.registerOnContextRestoredListener(listener): * lets you register a listener for context restored. Use * instead of addEventListener('webglcontextrestored', * listener); * * @param {!WebGLRenderingContext} ctx The webgl context to wrap. */ 'makeLostContextSimulatingContext': makeLostContextSimulatingContext, /** * Resets a context to the initial state. * @param {!WebGLRenderingContext} ctx The webgl context to * reset. */ 'resetToInitialState': resetToInitialState }; }(); /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (WebGLDebugUtils); /***/ }), /***/ "./src/utils/webgl-utils.js": /*!**********************************!*\ !*** ./src/utils/webgl-utils.js ***! \**********************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); const WebGLUtils = function() { /** * Creates the HTLM for a failure message * @param {string} canvasContainerId id of container of th * canvas. * @return {string} The html. */ var makeFailHTML = function(msg) { return '' + '<div style="margin: auto; width:500px;z-index:10000;margin-top:20em;text-align:center;">' + msg + '</div>'; return '' + '<table style="background-color: #8CE; width: 100%; height: 100%;"><tr>' + '<td align="center">' + '<div style="display: table-cell; vertical-align: middle;">' + '<div style="">' + msg + '</div>' + '</div>' + '</td></tr></table>'; }; /** * Mesasge for getting a webgl browser * @type {string} */ var GET_A_WEBGL_BROWSER = '' + 'This page requires a browser that supports WebGL.<br/>' + '<a href="http://get.webgl.org">Click here to upgrade your browser.</a>'; /** * Mesasge for need better hardware * @type {string} */ var OTHER_PROBLEM = '' + "It doesn't appear your computer can support WebGL.<br/>" + '<a href="http://get.webgl.org">Click here for more information.</a>'; /** * Creates a webgl context. If creation fails it will * change the contents of the container of the <canvas> * tag to an error message with the correct links for WebGL. * @param {Element} canvas. The canvas element to create a * context from. * @param {WebGLContextCreationAttirbutes} opt_attribs Any * creation attributes you want to pass in. * @param {function:(msg)} opt_onError An function to call * if there is an error during creation. * @return {WebGLRenderingContext} The created context. */ var setupWebGL = function(canvas, opt_attribs, opt_onError) { function handleCreationError(msg) { var container = document.getElementsByTagName("body")[0]; //var container = canvas.parentNode; if (container) { var str = window.WebGLRenderingContext ? OTHER_PROBLEM : GET_A_WEBGL_BROWSER; if (msg) { str += "<br/><br/>Status: " + msg; } container.innerHTML = makeFailHTML(str); } }; opt_onError = opt_onError || handleCreationError; if (canvas.addEventListener) { canvas.addEventListener("webglcontextcreationerror", function(event) { opt_onError(event.statusMessage); }, false); } var context = create3DContext(canvas, opt_attribs); if (!context) { if (!window.WebGLRenderingContext) { opt_onError(""); } else { opt_onError(""); } } return context; }; /** * Creates a webgl context. * @param {!Canvas} canvas The canvas tag to get context * from. If one is not passed in one will be created. * @return {!WebGLContext} The created context. */ var create3DContext = function(canvas, opt_attribs) { var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"]; var context = null; for (var ii = 0; ii < names.length; ++ii) { try { context = canvas.getContext(names[ii], opt_attribs); } catch(e) {} if (context) { break; } } return context; } return { create3DContext: create3DContext, setupWebGL: setupWebGL }; }(); /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (WebGLUtils); /***/ }), /***/ "./src/config/index.ts": /*!*****************************!*\ !*** ./src/config/index.ts ***! \*****************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "DefaultYRange": () => (/* binding */ DefaultYRange), /* harmony export */ "DefaultFovy": () => (/* binding */ DefaultFovy), /* harmony export */ "DefaultAnimDuration": () => (/* binding */ DefaultAnimDuration), /* harmony export */ "DefaultMovingRate": () => (/* binding */ DefaultMovingRate) /* harmony export */ }); // 默认俯仰可视范围角度 var DefaultYRange = [-86, 86]; // 默认静态可视范围角度 var DefaultFovy = 60; // 所有动画默认执行时间 var DefaultAnimDuration = 500; // 移动灵敏度 var DefaultMovingRate = 1.5; /***/ }), /***/ "./src/lib/CubeScene.ts": /*!******************************!*\ !*** ./src/lib/CubeScene.ts ***! \******************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); /* harmony import */ var _utils_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../utils/process */ "./src/utils/process.ts"); /* harmony import */ var _resource__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./resource */ "./src/lib/resource.ts"); /* harmony import */ var _interface_BaseScene__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./interface/BaseScene */ "./src/lib/interface/BaseScene.ts"); var __extends = (undefined && undefined.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); /** * 场景类,提供全景画面 * 立方体 * v6----- v5 * /| /| * v1------v0| * | | | | * | |v7---|-|v4 * |/ |/ * v2------v3 * */ var CubeScene = /** @class */ (function (_super) { __extends(CubeScene, _super); /** * @constructor 构造函数 * @param {string[]} textures 六个面的纹理图片,按照 f r u l d b 的顺序 * @param {SceneAngle} defaultAngle 默认展示角度 * */ function CubeScene(textures, defaultAngle) { var _this = _super.call(this) || this; /** * @property {TextureSource[]} textures 六个面的纹理图片,按照 f r u l d b 的顺序 * */ _this.textures = []; _this.textures = textures; if (defaultAngle) { var pitch = defaultAngle.pitch, yaw = defaultAngle.yaw; if (pitch) _this.pitch = pitch; if (yaw) _this.yaw = yaw; } return _this; } /** * 将每个面的纹理贴图赋值给每个面的 sampler * @param {WebGLRenderingContextWithProgram} gl WebGL 上下文 * @param {TexImageSource} image 图像资源 * @param {number} unit 每个面的编号 0-f 1-r 2-u 3-l 4-d 5-b * */ CubeScene.initTexture = function (gl, image, unit) { var texture = gl.createTexture(); if (!texture) { throw new Error('创建纹理对象失败'); } var texUnit = gl.TEXTURE0; var samplerPrefix = 'f'; switch (unit) { case 0: texUnit = gl.TEXTURE0; samplerPrefix = 'f'; break; case 1: texUnit = gl.TEXTURE1; samplerPrefix = 'r'; break; case 2: texUnit = gl.TEXTURE2; samplerPrefix = 'u'; break; case 3: texUnit = gl.TEXTURE3; samplerPrefix = 'l'; break; case 4: texUnit = gl.TEXTURE4; samplerPrefix = 'd'; break; case 5: texUnit = gl.TEXTURE5; samplerPrefix = 'b'; break; default: break; } gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); gl.activeTexture(texUnit); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); var u_Sampler = gl.getUniformLocation(gl.program, samplerPrefix + "_Sampler"); if (!u_Sampler) { throw new Error("\u83B7\u53D6 " + u_Sampler + " \u5730\u5740\u5931\u8D25"); } gl.uniform1i(u_Sampler, unit); }; /** * 获取 u_Face 缓存 * @param {WebGLRenderingContextWithProgram} gl WebGL 上下文 * @return {WebGLUniformLocation} u_Face 地址 * */ CubeScene.getUFaceLocation = function (gl) { if (!CubeScene.u_Face) { var u_Face = gl.getUniformLocation(gl.program, 'u_Face'); if (!u_Face) { throw new Error('获取 u_Face 地址失败'); } CubeScene.u_Face = u_Face; } return CubeScene.u_Face; }; /** * 渲染立方体 * */ CubeScene.prototype.drawModel = function () { var _this = this; CubeScene.allFacesIndices.forEach(function (faceIndices, index) { _this.renderFace(faceIndices, index); }); }; /** * 加载纹理贴图 * @return {Promise<TexImageSource[]>} 图像资源 * */ CubeScene.prototype.loadTextures = function () { // 交换前后,左右 var _a = this.textures, f = _a[0], r = _a[1], u = _a[2], l = _a[3], d = _a[4], b = _a[5]; var sourceOrder = [b, r, u, l, d, f]; // 删选出资源地址类型的,然后加载图片 var srcSources = []; sourceOrder.forEach(function (source, index) { if (typeof source === 'string') { srcSources.push({ src: source, index: index, }); } }); return (0,_resource__WEBPACK_IMPORTED_MODULE_1__.getTexImageSource)(srcSources.map(function (item) { return item.src; })).then(function (images) { // 拼接返回所有的 TexImageSource 类型 return sourceOrder.map(function (source) { if (typeof source === 'string') { return images.shift(); } else { return source; } }); }); }; /** * 渲染立方体的其中一面 * @param {Uint8Array} indices 该面的顶点索引 * @param {Unit} unit 每个面的编号 * */ CubeScene.prototype.renderFace = function (indices, unit) { var gl = this.pano.gl; var indexBuffer = gl.createBuffer(); if (!indexBuffer) { throw new Error('创建索引缓冲区对象失败'); } gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); gl.uniform1i(CubeScene.getUFaceLocation(gl), unit); gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_BYTE, 0); }; /** * 更改纹理重新渲染 * @param {TextureSource[]} textures 纹理资源集合,按照 f r u l d b 的顺序 * */ CubeScene.prototype.replaceTextures = function (textures) { this.textures = textures; this.render(this.pano); }; /** * 渲染到 pano * @param {Pano} pano 父容器 * */ CubeScene.prototype.render = function (pano) { var _this = this; _super.prototype.render.call(this, pano); var gl = this.pano.gl; (0,_utils_process__WEBPACK_IMPORTED_MODULE_0__.initArrayBuffer)(gl, CubeScene.vertices, 3, gl.FLOAT, 'a_Position'); (0,_utils_process__WEBPACK_IMPORTED_MODULE_0__.initArrayBuffer)(gl, CubeScene.texs, 2, gl.FLOAT, 'a_TexCoord'); this.loadTextures().then(function (images) { // [imgF, imgR, imgU, imgL, imgD, imgB] images.forEach(function (img, index) { if (index <= 5) CubeScene.initTexture(gl, img, index); }); _this.draw(); }); }; /** * @static {Float32Array} vertices 立方体顶点坐标 * */ CubeScene.vertices = new Float32Array([ 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0 // v4-v7-v6-v5 back ]); /** * @static {Float32Array} texs 顶点对应的纹理坐标 * */ CubeScene.texs = new Float32Array([ 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, ]); /** * @static {Uint8Array[]} allFacesIndices 六个面的顶点索引数组 * */ CubeScene.allFacesIndices = [ new Uint8Array([0, 1, 2, 0, 2, 3]), new Uint8Array([4, 5, 6, 4, 6, 7]), new Uint8Array([8, 9, 10, 8, 10, 11]), new Uint8Array([12, 13, 14, 12, 14, 15]), new Uint8Array([16, 17, 18, 16, 18, 19]), new Uint8Array([20, 21, 22, 20, 22, 23]), // b ]; return CubeScene; }(_interface_BaseScene__WEBPACK_IMPORTED_MODULE_2__["default"])); /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (CubeScene); /***/ }), /***/ "./src/lib/HotSpot.ts": /*!****************************!*\ !*** ./src/lib/HotSpot.ts ***! \****************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); /* harmony import */ var _utils_math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../utils/math */ "./src/utils/math.ts"); /* harmony import */ var _config_index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../config/index */ "./src/config/index.ts"); /** * 热点 * */ var HotSpot = /** @class */ (function () { /** * @constructor * @param {HTMLElement} dom 热点元素 * @param {{pitch?: number, yaw?: number, target?: number}} options 可选参数:pitch 位置-俯仰角-角度,范围是 (-90, 90);yaw 位置-偏航角-角度;target 转场目标场景索引 * */ function HotSpot(dom, options) { if (options === void 0) { options = {}; } var _a = options.pitch, pitch = _a === void 0 ? 0 : _a, _b = options.yaw, yaw = _b === void 0 ? 0 : _b, target = options.target; // 强制 dom 绝对定位 dom.style.position = 'absolute'; dom.style.cursor = 'pointer'; this.dom = dom; this.pitch = pitch; this.yaw = yaw; this.target = target; this.originOnClick = dom.onclick; } /** * 把热点图像插入到容器内 * @param deltaPitch {number} 场景的俯仰角偏移值 * @param deltaYaw {number} 场景的偏航角偏移值 * @param {Pano} pano 爷爷容器 * @param {Scene} scene 父容器 * */ HotSpot.prototype.render = function (deltaPitch, deltaYaw, pano, scene) { var _this = this; this.pano = pano; this.scene = scene; var container = this.pano.container; // target 有值的时候,给 click 添加默认场景切换操作 if (this.target || this.target === 0) { this.dom.onclick = function (ev) { _this.scene.onHotSpotClick(_this); if (_this.originOnClick) _this.originOnClick(ev); }; } container.append(this.dom); var _a = this.dom.getBoundingClientRect(), width = _a.width, height = _a.height; var centerX = (container.offsetWidth - width) / 2; var centerY = (container.offsetHeight - height) / 2; var offsetWidth = container.offsetWidth, offsetHeight = container.offsetHeight; this.pitch += deltaPitch; this.yaw -= deltaYaw; /** * tan(|pitch|) = deltaTop / 1 * pitch 的绝对值的正切:y 方向偏移量 / 深度(1) * pitch 的绝对值不超过配置的范围 * deltaTop 的计算结果是归一化的,换算成像素值:* canvas.height / 2 * ------ 上面的计算是基于 fovy 是 90 度的情况,当 fovy 非 90 时,需要乘以比例 -------- * */ var deltaTop = Math.tan((0,_utils_math__WEBPACK_IMPORTED_MODULE_0__.angle2PI)(Math.abs(this.pitch))) * (container.offsetHeight / 2) * (90 / _config_index__WEBPACK_IMPORTED_MODULE_1__.DefaultFovy); var top = this.pitch > 0 ? centerY - deltaTop : centerY + deltaTop; this.dom.style.top = top + "px"; /** * yaw 先转换到 [0, 360),再区分 [0, 90, 180, 270, 360) 各个区间计算 * 只要处理 [0, 90) 和 (270, 360) * 90 和 270 是无穷大,(90, 270) 范围在视线背面 * * [0, 90): tan(yaw) = deltaLeft / 1 * (270, 360): tan(360 - yaw) = deltaLeft / 1 * * 横向偏移量还和 canvas 的宽高比还有关系,但是垂直方向的偏移量和宽高比没关系??? * ------ 上面的计算是基于 fovy 是 90 度的情况,当 fovy 非 90 时,需要乘以比例 -------- * */ this.yaw = (0,_utils_math__WEBPACK_IMPORTED_MODULE_0__.angleIn360)(this.yaw); var deltaLeft; if (this.yaw >= 0 && this.yaw < 90) { deltaLeft = Math.tan((0,_utils_math__WEBPACK_IMPORTED_MODULE_0__.angle2PI)(this.yaw)); } else if (this.yaw > 270) { deltaLeft = -Math.tan((0,_utils_math__WEBPACK_IMPORTED_MODULE_0__.angle2PI)(360 - this.yaw)); } else { // 直接给一个画布的宽度值,偏移到外面不显示 deltaLeft = 4; } deltaLeft *= ((container.offsetWidth / 2) * (offsetHeight / offsetWidth) * (90 / _config_index__WEBPACK_IMPORTED_MODULE_1__.DefaultFovy)); this.dom.style.left = centerX + deltaLeft + "px"; }; /** * 销毁 * */ HotSpot.prototype.destroy = function () { // 移除图片 if (this.pano.container) this.pano.container.removeChild(this.dom); }; return HotSpot; }()); /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (HotSpot); /***/ }), /***/ "./src/lib/Pano.ts": /*!*************************!*\ !*** ./src/lib/Pano.ts ***! \*************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); /* harmony import */ var _utils_cuon_utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../utils/cuon-utils */ "./src/utils/cuon-utils.ts"); /* harmony import */ var _shader_cube_index_vert__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../shader/cube/index.vert */ "./src/shader/cube/index.vert"); /* harmony import */ var _shader_cube_index_frag__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../shader/cube/index.frag */ "./src/shader/cube/index.frag"); /* harmony import */ var _shader_sphere_index_vert__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../shader/sphere/index.vert */ "./src/shader/sphere/index.vert"); /* harmony import */ var _shader_sphere_index_frag__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../shader/sphere/index.frag */ "./src/shader/sphere/index.frag"); /* harmony import */ var _resource__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./resource */ "./src/lib/resource.ts"); /* harmony import */ var _CubeScene__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./CubeScene */ "./src/lib/CubeScene.ts"); /* harmony import */ var _SphereScene__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./SphereScene */ "./src/lib/SphereScene.ts"); /** * 容器 * */ var Pano = /** @class */ (function () { /** * @constructor * @param {string} containerId 容器节点id * @param {boolean} debug 是否开启debug * */ function Pano(containerId, debug) { var _this = this; /** * @property {Scene[]} scenes 场景列表 * */ this.scenes = []; /** * @property {number} sceneIndex 当前场景 * */ this.sceneIndex = 0; /** * 是否已经渲染 * */ this.rendered = false; /** * 场景变化的回调函数 * */ this.sceneChangeCallbacks = []; /** * 切换场景 * @param {number} sceneIndex 切换后的场景 * */ this.switchScene = function (sceneIndex) { if (sceneIndex === _this.sceneIndex) return; var currentScene = _this.scenes[_this.sceneIndex]; if (currentScene) currentScene.destroy(); _this.sceneIndex = sceneIndex; _this.render(); // 执行回调 _this.sceneChangeCallbacks.forEach(function (callback) { callback(_this.scenes[_this.sceneIndex], _this.sceneIndex); }); }; var container = document.getElementById(containerId); if (!container) { throw new Error('找不到容器,请确认 id 是否正确'); } this.container = container; var canvas = document.createElement('canvas'); this.canvas = canvas; container.append(canvas); this.gl = (0,_utils_cuon_utils__WEBPACK_IMPORTED_MODULE_0__.getWebGLContext)(canvas, debug); this.setStyle(); this.onContainerResize(); } /** * 初始化着色器程序 * */ Pano.prototype.initShader = function () { var scene = this.scenes[this.sceneIndex]; if (scene instanceof _CubeScene__WEBPACK_IMPORTED_MODULE_6__["default"]) { if (!Pano.CubeGLProgram) Pano.CubeGLProgram = (0,_utils_cuon_utils__WEBPACK_IMPORTED_MODULE_0__.createProgram)(this.gl, _shader_cube_index_vert__WEBPACK_IMPORTED_MODULE_1__["default"], _shader_cube_index_frag__WEBPACK_IMPORTED_MODULE_2__["default"]); (0,_utils_cuon_utils__WEBPACK_IMPORTED_MODULE_0__.useProgram)(this.gl, Pano.CubeGLProgram); } else if (scene instanceof _SphereScene__WEBPACK_IMPORTED_MODULE_7__["default"]) { if (!Pano.SphereGLProgram) Pano.SphereGLProgram = (0,_utils_cuon_utils__WEBPACK_IMPORTED_MODULE_0__.createProgram)(this.gl, _shader_sphere_index_vert__WEBPACK_IMPORTED_MODULE_3__["default"], _shader_sphere_index_frag__WEBPACK_IMPORTED_MODULE_4__["default"]); (0,_utils_cuon_utils__WEBPACK_IMPORTED_MODULE_0__.useProgram)(this.gl, Pano.SphereGLProgram); } else { throw new Error('当前场景非法'); } }; /** * 设置样式 * */ Pano.prototype.setStyle = function () { var _a = this, container = _a.container, canvas = _a.canvas; container.style.overflow = 'hidden'; // 如果 container 的 position 不是 absolute 和 fixed,则统一设置成 relative var containerPosition = container.style.position; if (containerPosition !== 'absolute' && containerPosition !== 'fixed') { container.style.position = 'relative'; } // 开启 HiDPI var desiredCSSWidth = container.offsetWidth; var desiredCSSHeight = container.offsetHeight; var devicePixelRatio = window.devicePixelRatio || 1; canvas.width = desiredCSSWidth * devicePixelRatio; canvas.height = desiredCSSHeight * devicePixelRatio; canvas.style.width = desiredCSSWidth + "px"; canvas.style.height = desiredCSSHeight + "px"; // canvas 大小变化后,即时改变 gl 的 viewport this.gl.viewport(0, 0, canvas.width, canvas.height); canvas.style.cursor = 'grab'; }; /** * container 尺寸变化监听,重新渲染 * */ Pano.prototype.onContainerResize = function () { var _this = this; if (ResizeObserver) { var observer = new ResizeObserver(function () { if (_this.rendered) { _this.setStyle(); _this.render(); } }); observer.observe(this.container); } }; /** * 添加回调函数 * @param {ListenerType} type 监听类型,预定义好的 * @param {ListenerCallback} callback 回调函数 * */ Pano.prototype.addListener = function (type, callback) { switch (type) { case "sceneChange": this.sceneChangeCallbacks.push(callback); break; default: break; } }; /** * 添加回调函数 * @param {ListenerType} type 监听类型,预定义好的 * @param {ListenerCallback} callback 回调函数 * */ Pano.prototype.removeListener = function (type, callback) { switch (type) { case "sceneChange": this.sceneChangeCallbacks = this.sceneChangeCallbacks.filter(function (item) { return item !== callback; }); break; default: break; } }; /** * 移除所有监听 * */ Pano.prototype.removeAllListeners = function () { this.sceneChangeCallbacks = []; }; /** * 添加场景 * @param {Scene} scene 场景 * */ Pano.prototype.addScene = function (scene) { this.scene