html5 css3 particle wave
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Particle Wave</title> <style> html, body { height: 100%; background:#000; } body { margin: 0; } canvas { display: block; } .waves { position:absolute; left:0; top:0; right:0; bottom:0; } </style> </head> <body> <div class="waves"></div> <script> class ShaderProgram { constructor( holder, options = {} ) { options = Object.assign({ antialias: false, depthTest: false, mousemove: false, autosize: true, side: 'front', vertex:` precision highp float; attribute vec4 a_position; attribute vec4 a_color; uniform float u_time; uniform vec2 u_resolution; uniform vec2 u_mousemove; uniform mat4 u_projection; varying vec4 v_color; void main() { gl_Position = u_projection * a_position; gl_PointSize = (10.0 / gl_Position.w) * 100.0; v_color = a_color; }`, fragment:` precision highp float; uniform sampler2D u_texture; uniform int u_hasTexture; varying vec4 v_color; void main() { if ( u_hasTexture == 1 ) { gl_FragColor = v_color * texture2D(u_texture, gl_PointCoord); } else { gl_FragColor = v_color; } }`, uniforms: {}, buffers: {}, camera: {}, texture: null, onUpdate: ( () => {} ), onResize: ( () => {} ), }, options ) const uniforms = Object. assign({ time: { type: 'float', value: 0 }, hasTexture: { type: 'int', value: 0 }, resolution: { type: 'vec2', value: [ 0, 0 ] }, mousemove: { type: 'vec2', value: [ 0, 0 ] }, projection: { type: 'mat4', value: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ] }, }, options. uniforms ) const buffers = Object. assign({ position: { size: 3, data: [] }, color: { size: 4, data: [] }, }, options. buffers ) const camera = Object. assign({ fov: 60, near: 1, far: 10000, aspect: 1, z: 100, perspective: true, }, options. camera ) const canvas = document. createElement( 'canvas' ) const gl = canvas. getContext( 'webgl', { antialias: options. antialias } ) if ( ! gl ) return false this.count = 0 this.gl = gl this. canvas = canvas this. camera = camera this.holder = holder this.onUpdate = options.onUpdate this.onResize = options.onResize this.data = {} holder. appendChild( canvas ) this.createProgram( options.vertex, options.fragment ) this. createBuffers( buffers ) this. createUniforms( uniforms ) this. updateBuffers() this. updateUniforms() this. createTexture( options. texture ) gl. enable( gl. BLEND ) gl. enable( gl. CULL_FACE ) gl.blendFunc( gl.SRC_ALPHA, gl.ONE ) gl[ options. depthTest ? 'enable' : 'disable' ]( gl.DEPTH_TEST ) if ( options. autosize ) window.addEventListener( 'resize', e => this.resize( e ), false ) if ( options. mousemove ) window.addEventListener( 'mousemove', e => this.mousemove( e ), false ) this. resize() this. update = this. update. bind( this ) this.time = { start: performance.now(), old: performance.now() } this. update() } mousemove( e ) { let x = e.pageX / this.width * 2 - 1 let y = e.pageY / this.height * 2 - 1 this.uniforms.mousemove = [ x, y ] } resize( e ) { const holder = this.holder const canvas = this. canvas const gl = this.gl const width = this.width = holder.offsetWidth const height = this.height = holder.offsetHeight const aspect = this. aspect = width / height const dpi = this.dpi = devicePixelRatio canvas.width = width * dpi canvas.height = height * dpi canvas.style.width = width + 'px' canvas.style.height = height + 'px' gl. viewport( 0, 0, width * dpi, height * dpi ) gl. clearColor( 0, 0, 0, 0 ) this.uniforms.resolution = [ width, height ] this.uniforms.projection = this.setProjection( aspect ) this.onResize(width, height, dpi) } setProjection( aspect ) { const camera = this. camera if (camera. perspective) { camera.aspect = aspect const fovRad = camera.fov * ( Math.PI / 180 ) const f = Math.tan( Math.PI * 0.5 - 0.5 * fovRad ) const rangeInv = 1.0 / ( camera. near - camera. far ) const matrix = [ f / camera. aspect, 0, 0, 0, 0, f, 0, 0, 0, 0, (camera. near + camera. far) * rangeInv, -1, 0, 0, camera. near * camera. far * rangeInv * 2, 0 ] matrix[ 14 ] + = camera.z matrix[ 15 ] + = camera.z return matrix } else { return [ 2 / this. width, 0, 0, 0, 0, -2 / this.height, 0, 0, 0, 0, 1, 0, -1, 1, 0, 1, ] } } createShader( type, source ) { const gl = this.gl const shader = gl. createShader( type ) gl. shaderSource( shader, source ) gl. compileShader( shader ) if ( gl. getShaderParameter (shader, gl. COMPILE_STATUS ) ) { return shader } else { console. log( gl. getShaderInfoLog( shader ) ) gl. deleteShader( shader ) } } createProgram( vertex, fragment ) { const gl = this.gl const vertexShader = this. createShader( gl. VERTEX_SHADER, vertex ) const fragmentShader = this. createShader( gl. FRAGMENT_SHADER, fragment ) const program = gl. createProgram() gl. attachShader( program, vertexShader ) gl. attachShader( program, fragmentShader ) gl. linkProgram( program ) if ( gl. getProgramParameter( program, gl. LINK_STATUS ) ) { gl. useProgram( program ) this.program = program } else { console. log( gl. getProgramInfoLog( program ) ) gl. deleteProgram( program ) } } createUniforms( data ) { const gl = this.gl const uniforms = this.data.uniforms = data const values = this. uniforms = {} Object.keys( uniforms ).forEach( name => { const uniform = uniforms[name] uniform.location = gl.getUniformLocation( this.program, 'u_' + name ) Object.defineProperty( values, name, { set: value => { uniforms[name].value = value this.setUniform( name, value ) }, get: () => uniforms[name].value } ) } ) } setUniform( name, value ) { const gl = this.gl const uniform = this.data.uniforms[name] uniform. value = value switch ( uniform. type ) { case 'int': { gl. uniform1i( uniform. location, value ) break } case 'float': { gl. uniform1f( uniform. location, value ) break } case 'vec2': { gl. uniform2f( uniform. location, ... value ) break } case 'vec3': { gl. uniform3f( uniform. location, ... value ) break } case 'vec4': { gl. uniform4f( uniform. location, ... value ) break } case 'mat2': { gl. uniformMatrix2fv( uniform. location, false, value ) break } case 'mat3': { gl. uniformMatrix3fv( uniform. location, false, value ) break } case 'mat4': { gl. uniformMatrix4fv( uniform. location, false, value ) break } } // ivec2 : uniform2i, // ivec3 : uniform3i, // ivec4 : uniform4i, // sampler2D : uniform1i, // samplerCube : uniform1i, // bool : uniform1i, // bvec2 : uniform2i, // bvec3 : uniform3i, // bvec4 : uniform4i, } updateUniforms() { const gl = this.gl const uniforms = this.data.uniforms Object.keys( uniforms ).forEach( name => { const uniform = uniforms[name] this.uniforms[name] = uniform.value } ) } createBuffers( data ) { const gl = this.gl const buffers = this. data. buffers = data const values = this. buffers = {} Object.keys( buffers ).forEach( name => { const buffer = buffers[name] buffer.buffer = this.createBuffer( 'a_' + name, buffer.size ) Object.defineProperty( values, name, { set: data => { buffers[name].data = data this. setBuffer( name, data ) if ( name == 'position' ) this.count = buffers.position.data.length / 3 }, get: () => buffers[name].data } ) } ) } createBuffer( name, size ) { const gl = this.gl const program = this. program const index = gl. getAttribLocation( program, name ) const buffer = gl. createBuffer() gl. bindBuffer( gl. ARRAY_BUFFER, buffer ) gl. enableVertexAttribArray( index ) gl.vertexAttribPointer( index, size, gl.FLOAT, false, 0, 0 ) return buffer } setBuffer( name, data ) { const gl = this.gl const buffers = this.data.buffers if ( name == null & amp; & amp; ! gl. bindBuffer( gl. ARRAY_BUFFER, null ) ) return gl.bindBuffer( gl.ARRAY_BUFFER, buffers[ name ].buffer ) gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( data ), gl.STATIC_DRAW ) } updateBuffers() { const gl = this.gl const buffers = this.buffers Object.keys( buffers ).forEach( name => buffers[name] = buffer.data ) this. setBuffer( null ) } createTexture( src ) { const gl = this.gl const texture = gl. createTexture() gl.bindTexture( gl.TEXTURE_2D, texture ) gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array( [ 0, 0, 0, 0 ] ) ) this.texture = texture if ( src ) { this.uniforms.hasTexture = 1 this. loadTexture( src ) } } loadTexture( src ) { const gl = this.gl const texture = this.texture const textureImage = new Image() textureImage.onload = () => { gl.bindTexture( gl.TEXTURE_2D, texture ) gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textureImage ) gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR ) gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_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) // gl.generateMipmap( gl.TEXTURE_2D ) } textureImage.src = src } update() { const gl = this.gl const now = performance. now() const elapsed = ( now - this.time.start ) / 5000 const delta = now - this.time.old this.time.old = now this.uniforms.time = elapsed if ( this. count > 0 ) { gl.clear( gl.COLORBUFFERBIT ) gl. drawArrays( gl. POINTS, 0, this. count ) } this. onUpdate( delta ) requestAnimationFrame( this. update ) } } const pointSize = 2.5 const waves = new ShaderProgram( document. querySelector( '.waves' ), { texture: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAgCAMAAABEpIrGAAAAb1BMVEUAAAD///8v0wLRAAAAJHRSTlMAC/goGvDhmwcExrVjWzrm29TRqqSKenRXVklANSIUE8mRkGpv + HOfAAABCElEQV Q4y4VT13LDMAwLrUHteO + R9f/fWMfO6dLaPeKVEECRxOULWsEGpS9nULDwia2Y + ALqUNbAWeg775zv + sA4/FFRMxt8U2FZFCVWjR/YrH4/H9sarclSKdPMWKzb8VsEeHB3m0shkhVCyNzeXe AQ9Xl4opEieX2QCGnwGbj6GMyjw9t1K0fK9YZunPXeAGsfJtYjwzxaBnozGGorYz0ypK2HzQSYx1y8DgSRo2ewOiyh2QWOEk1Y9OrQV0a8TiBM1a8eMHWYnRMy7CZ4t1CmyRkhSUvP3 gRXyHOCLBxNoC3IJv//ZrJ/kxxUHPUB + 6jJZZHrpg6GOjnqaOmzp4NDR48OLxn/H27SRQ08S0ZJAAAAAAElFTkSuQmCC', uniforms: { size: { type: 'float', value: pointSize }, field: { type: 'vec3', value: [ 0, 0, 0 ] }, speed: { type: 'float', value: 5 }, }, vertex:` #define M_PI 3.1415926535897932384626433832795 precision highp float; attribute vec4 a_position; attribute vec4 a_color; uniform float u_time; uniform float u_size; uniform float u_speed; uniform vec3 u_field; uniform mat4 u_projection; varying vec4 v_color; void main() { vec3 pos = a_position. xyz; pos.y + = ( cos(pos.x / u_field.x * M_PI * 8.0 + u_time * u_speed) + sin(pos.z / u_field.z * M_PI * 8.0 + u_time * u_speed) ) * u_field.y; gl_Position = u_projection * vec4( pos.xyz, a_position.w ); gl_PointSize = ( u_size / gl_Position.w ) * 100.0; v_color = a_color; }`, fragment:` precision highp float; uniform sampler2D u_texture; varying vec4 v_color; void main() { gl_FragColor = v_color * texture2D(u_texture, gl_PointCoord); }`, onResize( w, h, dpi ) { const position = [], color = [] const width = 400 * ( w / h ) const depth = 400 const height = 3 const distance = 5 for ( let x = 0; x < width; x + = distance ) { for ( let z = 0; z < depth; z + = distance ) { position.push(-width / 2 + x, -30, -depth / 2 + z) color.push( 0, 1 - ( x / width ) * 1, 0.5 + x / width * 0.5, z / depth ) } } this.uniforms.field = [ width, height, depth ] this.buffers.position = position this.buffers.color = color this.uniforms.size = (h / 400) * pointSize * dpi }, } )</script> </body> </html>
Two: particle waves
<html><head> <meta charset="utf-8"> <title>Particle Wave</title> <style> html, body { padding: 0; margin:0 } canvas { display: block } </style> </head> <body> <canvas width="1277" height="930"></canvas> <script> 'use strict'; var FastRandom = function() { this. getNextFloat = function() { return Math. random(); }; }; var ParticleWave = function() { var me = this; var config = { colors: { background: 0x000000, particle : 0x477cc2 }, alpha : { particle : 1 }, particleCount: 30000 }; var TAU = Math.PI * 2; var random = new FastRandom(); var particle; var particleFillStyle; var particleColorRGB = new Float32Array(3); var smoothGradient; var waterGradient; var canvas; var engine; var width; var height; var particleWaveWalker = 0; var randomWalker = 0; var requestTick = function() { window.requestAnimationFrame(tick); }; var initParticle = function() { particle = new Float32Array(config. particleCount * 2); eachParticle(function(x, z) { particle[x] = random. getNextFloat(); particle[z] = random. getNextFloat(); }); }; var initCanvas = function() { var cs = document. getElementsByTagName('canvas'); canvas = cs[0]; engine = canvas. getContext('2d'); width = window. innerWidth; height = window. innerHeight; canvas.setAttribute('width', width); canvas.setAttribute('height', height); }; var initParticleColor = function() { particleColorRGB[0] = config.colors.particle >> 16 & 0xff; particleColorRGB[1] = config.colors.particle >> 8 & 0xff; particleColorRGB[2] = config.colors.particle & 0xff; particleFillStyle = 'rgb(' + particleColorRGB[0] + ',' + particleColorRGB[1] + ',' + particleColorRGB[2] + ')'; }; var initSmoothGradient = function() { smoothGradient = engine. createLinearGradient( width / 2, 0, width / 2, height ); smoothGradient.addColorStop(0.25, 'rgba(0, 0, 0, 0)'); smoothGradient.addColorStop(0.45, 'rgba(0, 0, 0, 0.9)'); smoothGradient.addColorStop(0.5 , 'rgba(0, 0, 0, 1)'); smoothGradient.addColorStop(0.55, 'rgba(0, 0, 0, 0.9)'); smoothGradient.addColorStop(0.75, 'rgba(0, 0, 0, 0)'); }; var initWaterGradient = function() { waterGradient = engine.createLinearGradient( width / 2, height / 2, width / 2, height ); waterGradient.addColorStop(0, 'rgba(0, 0, 30, 0)'); waterGradient.addColorStop(1, 'rgba(30, 0, 60, 0.5)'); }; var init = function() { initCanvas(); initParticle(); initParticleColor(); initSmoothGradient(); initWaterGradient(); }; var eachParticle = function(cb) { for (var i = 0; i < particle. length; i + = 2) { cb(i, i + 1); } }; var renderParticle = function() { randomWalker + = (Math. random() - 0.5) * 0.1; particleWaveWalker += 0.03; var radius = { min : 1, add : 5 }; var midY = height / 2; var midX = width / 2; var spreadX = 5; var spreadZ = 0.0; var modZ = 0.0; var addX = 0; var addY = 0; var p = { x : 0.0, y : 0.0, r : 0.0 }; engine.fillStyle = particleFillStyle; // engine.beginPath(); var waveControl = 10; for (var i = 0, xIndex, zIndex; i < particle. length; i + = 2) { xIndex = i; zIndex = i + 1; particle[zIndex] += 0.003; if (particle[zIndex] > 1) { particle[zIndex] = 0; particle[xIndex] = random. getNextFloat(); } if (particle[zIndex] < 0.3) { continue; } modZ = Math.pow(particle[zIndex], 2); spreadZ = 1 + (spreadX - 1) * modZ; //bottom addX = (0.5 - particle[xIndex]) * width * spreadZ; addY = midY * modZ * (1 + 3 / waveControl); p.x = midX + addX; p.y = midY + addY; p.r = radius.min + modZ * radius.add; p.y + = Math.sin(particle[xIndex] * 50 + particleWaveWalker) * addY / waveControl; p.y + = Math.cos(particle[zIndex] * 10 + particleWaveWalker) * addY / waveControl; p.y -= Math.cos(particle[zIndex] + particle[xIndex] * 10 + particleWaveWalker) * addY / waveControl; p.y -= Math.cos(particle[xIndex] * 50 + particleWaveWalker) * addY / waveControl; p.y -= Math.sin(particle[zIndex] * 10 + particleWaveWalker) * addY / waveControl; if (p.x < 0 || p.x > width) { continue; } engine.fillRect(p.x, p.y, p.r, p.r); // engine. moveTo(p.x, p.y); // engine.arc(p.x, p.y, p.r, 0, TAU); //top // p.y = height - p.y; // // engine. moveTo(p.x, p.y); // engine.arc(p.x, p.y, p.r, 0, TAU); } engine.fillStyle = particleFillStyle; // engine. closePath(); // engine. fill(); }; var colorIntToHexString = function(color) { var s = color.toString(16); return '0'.repeat(6 - s.length) + s; }; var clear = function() { engine.fillStyle = '#' + colorIntToHexString(config.colors.background); engine. fillRect(0, 0, width, height); }; var drawSmooth = function() { engine. fillStyle = smoothGradient; engine. fillRect(0, 0, width, height); }; var drawWater = function() { engine.fillStyle = waterGradient; engine. fillRect(0, height / 2, width, height / 2); }; var tick = function() { clear(); drawWater(); renderParticle(); drawSmooth(); requestTick(); }; this. run = function() { init(); tick(); }; }; var pw = new ParticleWave(); pw. run();</script> </body></html>