UNPKG

stripe-gradient

Version:

Stripe WebGl Gradient Animation

350 lines (347 loc) 15.9 kB
//Essential functionality of WebGl //t = width //n = height class MiniGl { constructor(canvas, width, height, debug = false) { const _miniGl = this, debug_output = -1 !== document.location.search.toLowerCase().indexOf('debug=webgl') ;(_miniGl.canvas = canvas), (_miniGl.gl = _miniGl.canvas.getContext('webgl', { antialias: true })), (_miniGl.meshes = []) const context = _miniGl.gl width && height && this.setSize(width, height), _miniGl.lastDebugMsg, (_miniGl.debug = debug && debug_output ? function (e) { const t = new Date() t - _miniGl.lastDebugMsg > 1e3 && console.log('---'), console.log( t.toLocaleTimeString() + Array(Math.max(0, 32 - e.length)).join(' ') + e + ': ', ...Array.from(arguments).slice(1) ), (_miniGl.lastDebugMsg = t) } : () => {}), Object.defineProperties(_miniGl, { Material: { enumerable: false, value: class { constructor(vertexShaders, fragments, uniforms = {}) { const material = this function getShaderByType(type, source) { const shader = context.createShader(type) return ( context.shaderSource(shader, source), context.compileShader(shader), context.getShaderParameter(shader, context.COMPILE_STATUS) || console.error(context.getShaderInfoLog(shader)), _miniGl.debug('Material.compileShaderSource', { source: source }), shader ) } function getUniformVariableDeclarations(uniforms, type) { return Object.entries(uniforms) .map(([uniform, value]) => value.getDeclaration(uniform, type)) .join('\n') } ;(material.uniforms = uniforms), (material.uniformInstances = []) const prefix = '\n precision highp float;\n ' ;(material.vertexSource = `\n ${prefix}\n attribute vec4 position;\n attribute vec2 uv;\n attribute vec2 uvNorm;\n ${getUniformVariableDeclarations( _miniGl.commonUniforms, 'vertex' )}\n ${getUniformVariableDeclarations(uniforms, 'vertex')}\n ${vertexShaders}\n `), (material.Source = `\n ${prefix}\n ${getUniformVariableDeclarations( _miniGl.commonUniforms, 'fragment' )}\n ${getUniformVariableDeclarations(uniforms, 'fragment')}\n ${fragments}\n `), (material.vertexShader = getShaderByType(context.VERTEX_SHADER, material.vertexSource)), (material.fragmentShader = getShaderByType(context.FRAGMENT_SHADER, material.Source)), (material.program = context.createProgram()), context.attachShader(material.program, material.vertexShader), context.attachShader(material.program, material.fragmentShader), context.linkProgram(material.program), context.getProgramParameter(material.program, context.LINK_STATUS) || console.error(context.getProgramInfoLog(material.program)), context.useProgram(material.program), material.attachUniforms(void 0, _miniGl.commonUniforms), material.attachUniforms(void 0, material.uniforms) } //t = uniform attachUniforms(name, uniforms) { //n = material const material = this void 0 === name ? Object.entries(uniforms).forEach(([name, uniform]) => { material.attachUniforms(name, uniform) }) : 'array' == uniforms.type ? uniforms.value.forEach((uniform, i) => material.attachUniforms(`${name}[${i}]`, uniform)) : 'struct' == uniforms.type ? Object.entries(uniforms.value).forEach(([uniform, i]) => material.attachUniforms(`${name}.${uniform}`, i)) : (_miniGl.debug('Material.attachUniforms', { name: name, uniform: uniforms }), material.uniformInstances.push({ uniform: uniforms, location: context.getUniformLocation(material.program, name) })) } } }, Uniform: { enumerable: !1, value: class { constructor(e) { ;(this.type = 'float'), Object.assign(this, e) ;(this.typeFn = { float: '1f', int: '1i', vec2: '2fv', vec3: '3fv', vec4: '4fv', mat4: 'Matrix4fv' }[this.type] || '1f'), this.update() } update(value) { void 0 !== this.value && context[`uniform${this.typeFn}`]( value, 0 === this.typeFn.indexOf('Matrix') ? this.transpose : this.value, 0 === this.typeFn.indexOf('Matrix') ? this.value : null ) } //e - name //t - type //n - length getDeclaration(name, type, length) { const uniform = this if (uniform.excludeFrom !== type) { if ('array' === uniform.type) return ( uniform.value[0].getDeclaration(name, type, uniform.value.length) + `\nconst int ${name}_length = ${uniform.value.length};` ) if ('struct' === uniform.type) { let name_no_prefix = name.replace('u_', '') return ( (name_no_prefix = name_no_prefix.charAt(0).toUpperCase() + name_no_prefix.slice(1)), `uniform struct ${name_no_prefix} {\n` + Object.entries(uniform.value) .map(([name, uniform]) => uniform.getDeclaration(name, type).replace(/^uniform/, '')) .join('') + `\n} ${name}${length > 0 ? `[${length}]` : ''};` ) } return `uniform ${uniform.type} ${name}${length > 0 ? `[${length}]` : ''};` } } } }, PlaneGeometry: { enumerable: !1, value: class { constructor(width, height, n, i, orientation) { context.createBuffer(), (this.attributes = { position: new _miniGl.Attribute({ target: context.ARRAY_BUFFER, size: 3 }), uv: new _miniGl.Attribute({ target: context.ARRAY_BUFFER, size: 2 }), uvNorm: new _miniGl.Attribute({ target: context.ARRAY_BUFFER, size: 2 }), index: new _miniGl.Attribute({ target: context.ELEMENT_ARRAY_BUFFER, size: 3, type: context.UNSIGNED_SHORT }) }), this.setTopology(n, i), this.setSize(width, height, orientation) } setTopology(e = 1, t = 1) { const n = this ;(n.xSegCount = e), (n.ySegCount = t), (n.vertexCount = (n.xSegCount + 1) * (n.ySegCount + 1)), (n.quadCount = n.xSegCount * n.ySegCount * 2), (n.attributes.uv.values = new Float32Array(2 * n.vertexCount)), (n.attributes.uvNorm.values = new Float32Array(2 * n.vertexCount)), (n.attributes.index.values = new Uint16Array(3 * n.quadCount)) for (let e = 0; e <= n.ySegCount; e++) for (let t = 0; t <= n.xSegCount; t++) { const i = e * (n.xSegCount + 1) + t if ( ((n.attributes.uv.values[2 * i] = t / n.xSegCount), (n.attributes.uv.values[2 * i + 1] = 1 - e / n.ySegCount), (n.attributes.uvNorm.values[2 * i] = (t / n.xSegCount) * 2 - 1), (n.attributes.uvNorm.values[2 * i + 1] = 1 - (e / n.ySegCount) * 2), t < n.xSegCount && e < n.ySegCount) ) { const s = e * n.xSegCount + t ;(n.attributes.index.values[6 * s] = i), (n.attributes.index.values[6 * s + 1] = i + 1 + n.xSegCount), (n.attributes.index.values[6 * s + 2] = i + 1), (n.attributes.index.values[6 * s + 3] = i + 1), (n.attributes.index.values[6 * s + 4] = i + 1 + n.xSegCount), (n.attributes.index.values[6 * s + 5] = i + 2 + n.xSegCount) } } n.attributes.uv.update(), n.attributes.uvNorm.update(), n.attributes.index.update(), _miniGl.debug('Geometry.setTopology', { uv: n.attributes.uv, uvNorm: n.attributes.uvNorm, index: n.attributes.index }) } setSize(width = 1, height = 1, orientation = 'xz') { const geometry = this ;(geometry.width = width), (geometry.height = height), (geometry.orientation = orientation), (geometry.attributes.position.values && geometry.attributes.position.values.length === 3 * geometry.vertexCount) || (geometry.attributes.position.values = new Float32Array(3 * geometry.vertexCount)) const o = width / -2, r = height / -2, segment_width = width / geometry.xSegCount, segment_height = height / geometry.ySegCount for (let yIndex = 0; yIndex <= geometry.ySegCount; yIndex++) { const t = r + yIndex * segment_height for (let xIndex = 0; xIndex <= geometry.xSegCount; xIndex++) { const r = o + xIndex * segment_width, l = yIndex * (geometry.xSegCount + 1) + xIndex ;(geometry.attributes.position.values[3 * l + 'xyz'.indexOf(orientation[0])] = r), (geometry.attributes.position.values[3 * l + 'xyz'.indexOf(orientation[1])] = -t) } } geometry.attributes.position.update(), _miniGl.debug('Geometry.setSize', { position: geometry.attributes.position }) } } }, Mesh: { enumerable: !1, value: class { constructor(geometry, material) { const mesh = this ;(mesh.geometry = geometry), (mesh.material = material), (mesh.wireframe = !1), (mesh.attributeInstances = []), Object.entries(mesh.geometry.attributes).forEach(([e, attribute]) => { mesh.attributeInstances.push({ attribute: attribute, location: attribute.attach(e, mesh.material.program) }) }), _miniGl.meshes.push(mesh), _miniGl.debug('Mesh.constructor', { mesh: mesh }) } draw() { context.useProgram(this.material.program), this.material.uniformInstances.forEach(({ uniform: e, location: t }) => e.update(t)), this.attributeInstances.forEach(({ attribute: e, location: t }) => e.use(t)), context.drawElements( this.wireframe ? context.LINES : context.TRIANGLES, this.geometry.attributes.index.values.length, context.UNSIGNED_SHORT, 0 ) } remove() { _miniGl.meshes = _miniGl.meshes.filter((e) => e != this) } } }, Attribute: { enumerable: !1, value: class { constructor(e) { ;(this.type = context.FLOAT), (this.normalized = !1), (this.buffer = context.createBuffer()), Object.assign(this, e), this.update() } update() { void 0 !== this.values && (context.bindBuffer(this.target, this.buffer), context.bufferData(this.target, this.values, context.STATIC_DRAW)) } attach(e, t) { const n = context.getAttribLocation(t, e) return ( this.target === context.ARRAY_BUFFER && (context.enableVertexAttribArray(n), context.vertexAttribPointer(n, this.size, this.type, this.normalized, 0, 0)), n ) } use(e) { context.bindBuffer(this.target, this.buffer), this.target === context.ARRAY_BUFFER && (context.enableVertexAttribArray(e), context.vertexAttribPointer(e, this.size, this.type, this.normalized, 0, 0)) } } } }) const a = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] _miniGl.commonUniforms = { projectionMatrix: new _miniGl.Uniform({ type: 'mat4', value: a }), modelViewMatrix: new _miniGl.Uniform({ type: 'mat4', value: a }), resolution: new _miniGl.Uniform({ type: 'vec2', value: [1, 1] }), aspectRatio: new _miniGl.Uniform({ type: 'float', value: 1 }) } } setSize(e = 640, t = 480) { ;(this.width = e), (this.height = t), (this.canvas.width = e), (this.canvas.height = t), this.gl.viewport(0, 0, e, t), (this.commonUniforms.resolution.value = [e, t]), (this.commonUniforms.aspectRatio.value = e / t), this.debug('MiniGL.setSize', { width: e, height: t }) } //left, right, top, bottom, near, far setOrthographicCamera(e = 0, t = 0, n = 0, i = -2e3, s = 2e3) { ;(this.commonUniforms.projectionMatrix.value = [2 / this.width, 0, 0, 0, 0, 2 / this.height, 0, 0, 0, 0, 2 / (i - s), 0, e, t, n, 1]), this.debug('setOrthographicCamera', this.commonUniforms.projectionMatrix.value) } render() { this.gl.clearColor(0, 0, 0, 0), this.gl.clearDepth(1), this.meshes.forEach((e) => e.draw()) } } export default MiniGl