Toggle navigation
在线编辑器
在线代码
文本比较
jQuery下载
前端库
在线手册
登录/注册
下载代码
html
css
js
分享到微信朋友圈
X
html
css
body { margin:0; } canvas { position: fixed; }
JavaScript
class WTCGL { constructor(el, vertexShaderSource, fragmentShaderSource, width, height, pxratio, styleElement, webgl2) { this.run = this.run.bind(this); this._onRun = () => {}; // Destructure if an object is aprovided instead a series of parameters if (el instanceof Object && el.el) { ({ el, vertexShaderSource, fragmentShaderSource, width, height, pxratio, webgl2, styleElement } = el); } // If the HTML element isn't a canvas, return null if (!el instanceof HTMLElement || el.nodeName.toLowerCase() !== 'canvas') { console.log('Provided element should be a canvas element'); return null; } this._el = el; // The context should be either webgl2, webgl or experimental-webgl if (webgl2 === true) { this.isWebgl2 = true; this._ctx = this._el.getContext("webgl2", this.webgl_params) || this._el.getContext("webgl", this.webgl_params) || this._el.getContext("experimental-webgl", this.webgl_params); } else { this.isWebgl2 = false; this._ctx = this._el.getContext("webgl", this.webgl_params) || this._el.getContext("experimental-webgl", this.webgl_params); } // Set up the extensions this._ctx.getExtension('OES_standard_derivatives'); this._ctx.getExtension('EXT_shader_texture_lod'); this._ctx.getExtension('OES_texture_float'); this._ctx.getExtension('WEBGL_color_buffer_float'); this._ctx.getExtension('OES_texture_float_linear'); this._ctx.getExtension('EXT_color_buffer_float'); // We can't make the context so return an error if (!this._ctx) { console.log('Browser doesn\'t support WebGL '); return null; } // Create the shaders this._vertexShader = WTCGL.createShaderOfType(this._ctx, this._ctx.VERTEX_SHADER, vertexShaderSource); this._fragmentShader = WTCGL.createShaderOfType(this._ctx, this._ctx.FRAGMENT_SHADER, fragmentShaderSource); // Create the program and link the shaders this._program = this._ctx.createProgram(); this._ctx.attachShader(this._program, this._vertexShader); this._ctx.attachShader(this._program, this._fragmentShader); this._ctx.linkProgram(this._program); // If we can't set up the params, this means the shaders have failed for some reason if (!this._ctx.getProgramParameter(this._program, this._ctx.LINK_STATUS)) { console.log('Unable to initialize the shader program: ' + this._ctx.getProgramInfoLog(this._program)); return null; } // Initialise the vertex buffers this.initBuffers([ -1.0, 1.0, -1., 1.0, 1.0, -1., -1.0, -1.0, -1., 1.0, -1.0, -1.]); // Initialise the frame buffers this.frameBuffers = []; // The program information object. This is essentially a state machine for the webGL instance this._programInfo = { attribs: { vertexPosition: this._ctx.getAttribLocation(this._program, 'a_position') }, uniforms: { projectionMatrix: this._ctx.getUniformLocation(this._program, 'u_projectionMatrix'), modelViewMatrix: this._ctx.getUniformLocation(this._program, 'u_modelViewMatrix'), resolution: this._ctx.getUniformLocation(this._program, 'u_resolution'), time: this._ctx.getUniformLocation(this._program, 'u_time') } }; // Tell WebGL to use our program when drawing this._ctx.useProgram(this._program); this.pxratio = pxratio; this.styleElement = styleElement !== true; this.resize(width, height); } /** * Public methods */ addFrameBuffer(w, h, tiling = 0, buffertype = 0) { // create to render to const gl = this._ctx; const targetTextureWidth = w * this.pxratio; const targetTextureHeight = h * this.pxratio; const targetTexture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, targetTexture); { // define size and format of level 0 const level = 0; let internalFormat = gl.RGBA; const border = 0; let format = gl.RGBA; let t; if (buffertype & WTCGL.TEXTYPE_FLOAT) { const e = gl.getExtension('OES_texture_float'); window.extension = e; t = e.FLOAT; // internalFormat = gl.RGBA32F; } else if (buffertype & WTCGL.TEXTYPE_HALF_FLOAT_OES) { // t = gl.renderer.isWebgl2 ? e.HALF_FLOAT : e.HALF_FLOAT_OES; // gl.renderer.extensions['OES_texture_half_float'] ? gl.renderer.extensions['OES_texture_half_float'].HALF_FLOAT_OES : // gl.UNSIGNED_BYTE; const e = gl.getExtension('OES_texture_half_float'); t = this.isWebgl2 ? gl.HALF_FLOAT : e.HALF_FLOAT_OES; // format = gl.RGBA; if (this.isWebgl2) { internalFormat = gl.RGBA16F; } // internalFormat = gl.RGB32F; // format = gl.RGB32F; // window.gl = gl // t = e.HALF_FLOAT_OES; } else { t = gl.UNSIGNED_BYTE; } const type = t; const data = null; gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, targetTextureWidth, targetTextureHeight, border, format, type, data); // gl.generateMipmap(gl.TEXTURE_2D); // set the filtering so we don't need mips gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); // Set the parameters based on the passed type if (tiling === WTCGL.IMAGETYPE_TILE) { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } else if (tiling === WTCGL.IMAGETYPE_MIRROR) { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.MIRRORED_REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT); } else if (tiling === WTCGL.IMAGETYPE_REGULAR) { 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); } } // Create and bind the framebuffer const fb = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, fb); // attach the texture as the first color attachment const attachmentPoint = gl.COLOR_ATTACHMENT0; const level = 0; gl.framebufferTexture2D(gl.FRAMEBUFFER, attachmentPoint, gl.TEXTURE_2D, targetTexture, level); return { w: w * this.pxratio, h: h * this.pxratio, fb: fb, frameTexture: targetTexture }; } /** * Resizes the canvas to a specified width and height, respecting the pixel ratio * * @param {number} w The width of the canvas * @param {number} h The height of the canvas * @return {Void} */ resize(w, h) { this.width = w; this.height = h; this._el.width = w * this.pxratio; this._el.height = h * this.pxratio; this._size = [w * this.pxratio, h * this.pxratio]; if (this.styleElement) { this._el.style.width = w + 'px'; this._el.style.height = h + 'px'; } this._ctx.viewportWidth = w * this.pxratio; this._ctx.viewportHeight = h * this.pxratio; this._ctx.uniform2fv(this._programInfo.uniforms.resolution, this._size); this.initBuffers(this._positions); } /** * Initialise a provided vertex buffer * * @param {array} positions The vertex positions to initialise * @return {Void} */ initBuffers(positions) { this._positions = positions; this._positionBuffer = this._ctx.createBuffer(); this._ctx.bindBuffer(this._ctx.ARRAY_BUFFER, this._positionBuffer); this._ctx.bufferData(this._ctx.ARRAY_BUFFER, new Float32Array(positions), this._ctx.STATIC_DRAW); } /** * Add a uniform to the program. At this time the following types are supported: * - Float - WTCGL.TYPE_FLOAT * - Vector 2 - WTCGL.TYPE_V2 * - Vector 3 - WTCGL.TYPE_V3 * - Vector 4 - WTCGL.TYPE_V4 * * @param {string} name The name of the uniform. N.B. your name will be prepended with a `u_` in your shaders. So providing a name of `foo` here will result in a uniform named `u_foo` * @param {WTCGL.UNIFORM_TYPE} type The unfiform type * @param {number|array} value The unfiform value. The type depends on the uniform type being created * @return {WebGLUniformLocation} The uniform location for later reference */ addUniform(name, type, value) { let uniform = this._programInfo.uniforms[name]; uniform = this._ctx.getUniformLocation(this._program, `u_${name}`); switch (type) { case WTCGL.TYPE_INT: if (!isNaN(value)) this._ctx.uniform1i(uniform, value); break; case WTCGL.TYPE_FLOAT: if (!isNaN(value)) this._ctx.uniform1f(uniform, value); break; case WTCGL.TYPE_V2: if (value instanceof Array && value.length === 2.) this._ctx.uniform2fv(uniform, value); break; case WTCGL.TYPE_V3: if (value instanceof Array && value.length === 3.) this._ctx.uniform3fv(uniform, value); break; case WTCGL.TYPE_V4: if (value instanceof Array && value.length === 4.) this._ctx.uniform4fv(uniform, value); break; case WTCGL.TYPE_BOOL: if (!isNaN(value)) this._ctx.uniform1i(uniform, value); break;} this._programInfo.uniforms[name] = uniform; return uniform; } /** * Adds a texture to the program and links it to a named uniform. Providing the type changes the tiling properties of the texture. Possible values for type: * - WTCGL.IMAGETYPE_REGULAR - No tiling, clamp to edges and doesn't need to be power of 2. * - WTCGL.IMAGETYPE_TILE - full x and y tiling, needs to be power of 2. * - WTCGL.IMAGETYPE_MIRROR - mirror tiling, needs to be power of 2. * * @public * @param {string} name The name of the uniform. N.B. your name will be prepended with a `u_` in your shaders. So providing a name of `foo` here will result in a uniform named `u_foo` * @param {WTCGL.TYPE_IMAGETYPE} type The type of texture to create. This is basically the tiling behaviour of the texture as described above * @param {Image} image The image object to add to the texture * @return {WebGLTexture} The texture object */ addTexture(name, type, image, liveUpdate = false) { var texture = this._ctx.createTexture(); this._ctx.pixelStorei(this._ctx.UNPACK_FLIP_Y_WEBGL, true); this._ctx.bindTexture(this._ctx.TEXTURE_2D, texture); // this._ctx.generateMipmap(this._ctx.TEXTURE_2D); // Set the parameters based on the passed type if (type === WTCGL.IMAGETYPE_MIRROR) { this._ctx.texParameteri(this._ctx.TEXTURE_2D, this._ctx.TEXTURE_WRAP_S, this._ctx.MIRRORED_REPEAT); this._ctx.texParameteri(this._ctx.TEXTURE_2D, this._ctx.TEXTURE_WRAP_T, this._ctx.MIRRORED_REPEAT); } else if (type === WTCGL.IMAGETYPE_REGULAR) { this._ctx.texParameteri(this._ctx.TEXTURE_2D, this._ctx.TEXTURE_WRAP_S, this._ctx.CLAMP_TO_EDGE); this._ctx.texParameteri(this._ctx.TEXTURE_2D, this._ctx.TEXTURE_WRAP_T, this._ctx.CLAMP_TO_EDGE); } this._ctx.texParameteri(this._ctx.TEXTURE_2D, this._ctx.TEXTURE_MIN_FILTER, this._ctx.LINEAR); // this._ctx.texParameteri(this._ctx.TEXTURE_2D, this._ctx.TEXTURE_MAG_FILTER, this._ctx.LINEAR); // Upload the image into the texture. this._ctx.texImage2D(this._ctx.TEXTURE_2D, 0, this._ctx.RGBA, this._ctx.RGBA, this._ctx.UNSIGNED_BYTE, image); // add the texture to the array of textures. this.pushTexture(name, texture, image, this._ctx.TEXTURE_2D, liveUpdate); return texture; } pushTexture(name, texture, image, target, liveUpdate = false) { let textures = this.textures; textures.push({ name: name, tex: texture, liveUpdate: liveUpdate, image: image, target: target }); // Finally set the this.textures (this is just to get around the funnyness of default getters) this.textures = textures; } /** * Updates a texture location for a given WebGLTexture with an image * * @param {WebGLTexture} texture The texture location to update * @param {Image} image The image object to add to the texture * @return {Void} */ updateTexture(texture, image, name) { let uniform = this._ctx.getUniformLocation(this._program, `u_${name}`); // Set the texture unit to the uniform this._ctx.uniform1i(uniform, 0); this._ctx.activeTexture(this._ctx.TEXTURE0); this._ctx.bindTexture(this._ctx.TEXTURE_2D, texture); // Upload the image into the texture. this._ctx.texImage2D(this._ctx.TEXTURE_2D, 0, this._ctx.RGBA, this._ctx.RGBA, this._ctx.UNSIGNED_BYTE, image); } /** * Initialise texture locations in the program * * @return {Void} */ initTextures() { for (let i = 0; i < this.textures.length; i++) { let name = this.textures[i].name; let uniform = this._programInfo.uniforms[name]; uniform = this._ctx.getUniformLocation(this._program, `u_${name}`); // Set the texture unit to the uniform this._ctx.uniform1i(uniform, i); // find the active texture based on the index this._ctx.activeTexture(this._ctx[`TEXTURE${i}`]); // Finally, bind the texture this._ctx.bindTexture(this.textures[i].target, this.textures[i].tex); } } /** * The run loop. This function is run as a part of a RaF and updates the internal * time uniform (`u_time`). * * @param {number} delta The delta time provided by the RaF loop * @return {Void} */ run(delta) { this.running && requestAnimationFrame(this.run); const runFunction = () => { this.time = this.startTime + delta * .0002; this.onRun(delta); this.render(); }; if (this.frameRate) { let now = Date.now(); let elapsed = now - this._then; if (elapsed > this.frameRate) { this._then = now - elapsed % this.frameRate; runFunction(); } } else { runFunction(); } } /** * Render the program * * @return {Void} */ render(buffer = {}) { this._ctx.bindFramebuffer(this._ctx.FRAMEBUFFER, buffer.fb || null); // Update the time uniform this._ctx.uniform1f(this._programInfo.uniforms.time, this.time); this.textures.forEach(textureInfo => { if (textureInfo.liveUpdate === true) { this.updateTexture(textureInfo.tex, textureInfo.image, textureInfo.name); } }); this._ctx.viewport(0, 0, buffer.w || this._ctx.viewportWidth, buffer.h || this._ctx.viewportHeight); if (this.clearing) { this._ctx.clearColor(1.0, 0.0, 0.0, 0.0); // this._ctx.clearDepth(1.0); // this._ctx.enable(this._ctx.DEPTH_TEST); // this._ctx.depthFunc(this._ctx.LEQUAL); this._ctx.blendFunc(this._ctx.SRC_ALPHA, this._ctx.ONE_MINUS_SRC_ALPHA); this._ctx.clear(this._ctx.COLOR_BUFFER_BIT); } this._ctx.bindBuffer(this._ctx.ARRAY_BUFFER, this._positionBuffer); this._ctx.vertexAttribPointer( this._programInfo.attribs.vertexPosition, 3, this._ctx.FLOAT, false, 0, 0); this._ctx.enableVertexAttribArray(this._programInfo.attribs.vertexPosition); // Set the shader uniforms this.includePerspectiveMatrix && this._ctx.uniformMatrix4fv(this._programInfo.uniforms.projectionMatrix, false, this.perspectiveMatrix); this.includeModelViewMatrix && this._ctx.uniformMatrix4fv(this._programInfo.uniforms.modelViewMatrix, false, this.modelViewMatrix); this._ctx.drawArrays(this._ctx.TRIANGLE_STRIP, 0, 4); } /** * Getters and setters */ /** * The default webGL parameters to be used for the program. * This is read only and should only be overridden as a part of a subclass. * * @readonly * @type {object} * @default { alpha: true } */ get webgl_params() { return { alpha: true }; } /** * (getter/setter) Whether the element should include styling as a part of * its rendition. * * @type {boolean} * @default true */ set styleElement(value) { this._styleElement = value === true; if (this._styleElement === false && this._el) { this._el.style.width = ''; this._el.style.height = ''; } } get styleElement() { return this._styleElement !== false; } /** * (getter/setter) startTime. This is a value to begin the `u_time` * unform at. This is here in case you want `u_time` to begin at a * specific value other than 0. * * @type {number} * @default 0 */ set startTime(value) { if (!isNaN(value)) { this._startTime = value; } } get startTime() { return this._startTime || 0; } /** * (getter/setter) time. This is the time that the program currently * sits at. By default this value is set as a part of the run loop * however this is a public property so that we can specify time * for rendition outside of the run loop. * * @type {number} * @default 0 */ set time(value) { if (!isNaN(value)) { this._time = value; } } get time() { return this._time || 0; } /** * (getter/setter) includePerspectiveMatrix. This determines whether the * perspecive matrix is included in the program. This doesn't really make * a difference right now, but this is here to provide future interoperability. * * @type {boolean} * @default false */ set includePerspectiveMatrix(value) { this._includePerspectiveMatrix = value === true; } get includePerspectiveMatrix() { return this._includePerspectiveMatrix === true; } /** * (getter/setter) includeModelViewMatrix. This determines whether the * model view matrix is included in the program. This doesn't really make * a difference right now, but this is here to provide future interoperability. * * @type {boolean} * @default false */ set includeModelViewMatrix(value) { this._includeModelViewMatrix = value === true; } get includeModelViewMatrix() { return this._includeModelViewMatrix === true; } /** * (getter/setter) textures. The array of textures to initialise into the program. * * @private * @type {array} * @default [] */ set textures(value) { if (value instanceof Array) { this._textures = value; } } get textures() { return this._textures || []; } /** * (getter/setter) clearing. Specifies whether the program should clear the screen * before drawing anew. * * @type {boolean} * @default false */ set clearing(value) { this._clearing = value === true; } get clearing() { return this._clearing === true; } /** * (getter/setter) running. Specifies whether the programming is running. Setting * this to true will create a RaF loop which will call the run function. * * @type {boolean} * @default false */ set running(value) { if (!this.running && value === true) { this._then = Date.now(); requestAnimationFrame(this.run); } this._running = value === true; } get running() { return this._running === true; } set frameRate(value) { if (!isNaN(value)) this._frameRate = 1000 / value; } get frameRate() { return this._frameRate || null; } /** * (getter/setter) pxratio. The 1-dimensional pixel ratio of the application. * This should be used either for making a program look good on high density * screens or for raming down pixel density for performance. * * @type {number} * @default 1 */ set pxratio(value) { if (value > 0) this._pxratio = value; } get pxratio() { return this._pxratio || 1; } /** * (getter/setter) perspectiveMatrix. Calculate a perspective matrix, a * special matrix that is used to simulate the distortion of perspective in * a camera. Our field of view is 45 degrees, with a width/height ratio * that matches the display size of the canvas and we only want to see * objects between 0.1 units and 100 units away from the camera. * * @readonly * @type {mat4} */ get perspectiveMatrix() { const fieldOfView = 45 * Math.PI / 180; // in radians const aspect = this._size.w / this._size.h; const zNear = 0.1; const zFar = 100.0; const projectionMatrix = mat4.create(); // note: glmatrix.js always has the first argument // as the destination to receive the result. mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar); return projectionMatrix; } /** * (getter/setter) perspectiveMatrix. Calculate a model view matrix. * * @readonly * @type {mat4} */ get modelViewMatrix() { // Set the drawing position to the "identity" point, which is // the center of the scene. const modelViewMatrix = mat4.create(); // Now move the drawing position a bit to where we want to // start drawing the square. mat4.translate(modelViewMatrix, // destination matrix modelViewMatrix, // matrix to translate [-0.0, 0.0, -1.]); // amount to translate return modelViewMatrix; } set onRun(runMethod) { if (typeof runMethod == 'function') { this._onRun = runMethod.bind(this); } } get onRun() { return this._onRun; } get context() { return this._ctx || null; } /** * Static Methods */ /** * Create a shader of a given type given a context, type and source. * * @static * @param {WebGLContext} ctx The context under which to create the shader * @param {WebGLShaderType} type The shader type, vertex or fragment * @param {string} source The shader source. * @return {WebGLShader} The created shader */ static createShaderOfType(ctx, type, source) { const shader = ctx.createShader(type); ctx.shaderSource(shader, source); ctx.compileShader(shader); if (!ctx.getShaderParameter(shader, ctx.COMPILE_STATUS)) { console.log('An error occurred compiling the shaders: ' + ctx.getShaderInfoLog(shader)); ctx.deleteShader(shader); return null; } return shader; }} WTCGL.TYPE_INT = 0; WTCGL.TYPE_FLOAT = 1; WTCGL.TYPE_V2 = 2; WTCGL.TYPE_V3 = 3; WTCGL.TYPE_V4 = 4; WTCGL.TYPE_BOOL = 5; WTCGL.IMAGETYPE_REGULAR = 0; WTCGL.IMAGETYPE_TILE = 1; WTCGL.IMAGETYPE_MIRROR = 2; WTCGL.TEXTYPE_FLOAT = 0; WTCGL.TEXTYPE_UNSIGNED_BYTE = 1; WTCGL.TEXTYPE_HALF_FLOAT_OES = 2; console.clear(); const twodWebGL = new WTCGL( document.querySelector('canvas#webgl'), document.querySelector('script#vertexShader').textContent, document.querySelector('script#fragmentShader').textContent, window.innerWidth, window.innerHeight, window.devicePixelRatio); twodWebGL.startTime = -100 + Math.random() * 50; window.addEventListener('resize', () => { twodWebGL.resize(window.innerWidth, window.innerHeight); }); // track mouse move let mousepos = [0, 0]; const u_mousepos = twodWebGL.addUniform('mouse', WTCGL.TYPE_V2, mousepos); window.addEventListener('pointermove', e => { let ratio = window.innerHeight / window.innerWidth; if (window.innerHeight > window.innerWidth) { mousepos[0] = (e.pageX - window.innerWidth / 2) / window.innerWidth; mousepos[1] = (e.pageY - window.innerHeight / 2) / window.innerHeight * -1 * ratio; } else { mousepos[0] = (e.pageX - window.innerWidth / 2) / window.innerWidth / ratio; mousepos[1] = (e.pageY - window.innerHeight / 2) / window.innerHeight * -1; } twodWebGL.addUniform('mouse', WTCGL.TYPE_V2, mousepos); }); // Load all our textures. We only initiate the instance once all images are loaded. const textures = [ { name: 'noise', url: '', type: WTCGL.IMAGETYPE_TILE, img: null }, { name: 'bricks', url: 'data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAABAAD/4QMdaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzE0NSA3OS4xNjM0OTksIDIwMTgvMDgvMTMtMTY6NDA6MjIgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjY3MjY2OUQ0MjQ4NTExRUI4MjhDRkI4NDdFRjlBQkU5IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjY3MjY2OUQzMjQ4NTExRUI4MjhDRkI4NDdFRjlBQkU5IiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IFdpbmRvd3MiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0iNjc2MTQwODhGMjY2OTY2NTY2RTYwNEREODg3N0IzQ0UiIHN0UmVmOmRvY3VtZW50SUQ9IjY3NjE0MDg4RjI2Njk2NjU2NkU2MDRERDg4NzdCM0NFIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+/+4ADkFkb2JlAGTAAAAAAf/bAIQAGhkZJxwnPiUlPkIvLy9CRz07Oz1HR0dHR0dHR0dHR0dH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', type: WTCGL.IMAGETYPE_TILE, img: null }]; const loadImage = function (imageObject) { let img = document.createElement('img'); img.crossOrigin = "anonymous"; return new Promise((resolve, reject) => { img.addEventListener('load', e => { imageObject.img = img; resolve(imageObject); }); img.addEventListener('error', e => { reject(e); }); img.src = imageObject.url; }); }; const loadTextures = function (textures) { return new Promise((resolve, reject) => { const loadTexture = pointer => { if (pointer >= textures.length || pointer > 10) { resolve(textures); return; }; const imageObject = textures[pointer]; const p = loadImage(imageObject); p.then( result => { twodWebGL.addTexture(result.name, result.type, result.img); }, error => { console.log('error', error); }).finally(e => { loadTexture(pointer + 1); }); }; loadTexture(0); }); }; loadTextures(textures).then( result => { twodWebGL.initTextures(); // twodWebGL.render(); twodWebGL.running = true; }, error => { console.log('error'); });
粒子
时间
文字
hover
canvas
3d
游戏
音乐
火焰
水波
轮播图
鼠标跟随
动画
css
加载动画
导航
菜单
按钮
滑块
tab
弹出层
统计图
svg
×
Close
在线代码下载提示
开通在线代码永久免费下载,需支付20jQ币
开通后,在线代码模块中所有代码可终身免费下!
您已开通在线代码永久免费下载,关闭提示框后,点下载代码可直接下载!
您已经开通过在线代码永久免费下载
对不起,您的jQ币不足!可通过发布资源 或
直接充值获取jQ币
取消
开通下载
<!doctype html> <html> <head> <meta charset="utf-8"> <title>canvas舞台水波背景动画-jq22.com</title> <script src="https://www.jq22.com/jquery/jquery-1.10.2.js"></script> <style>
</style> </head> <body>
<script>
</script>
</body> </html>
2012-2021 jQuery插件库版权所有
jquery插件
|
jq22工具库
|
网页技术
|
广告合作
|
在线反馈
|
版权声明
沪ICP备13043785号-1
浙公网安备 33041102000314号