<body id="masthead">
<canvas id="canvasHeader"></canvas>
</body>
#masthead
{
position: relative;
}
#canvasHeader
{
width: 100%;
height: 300px;
}
/*
* Constants...
*/
const N_PNT : number = 4;
const STEPS : number = 600.0;
const SPLIT : string = "|";
const COOKY : string = "colors";
/*
* RGB color of a vertex...
*/
class VColor
{
public R : number;
public G : number;
public B : number;
//
// Constructor...
//
constructor (red? : number, green? : number, blue? : number)
{
this.R = red || Math.random ();
this.G = green || Math.random ();
this.B = blue || Math.random ();
}
//
//
//
public addColor (color : VColor) : void
{
this.R += color.R;
this.G += color.G;
this.B += color.B;
}
//
//
//
public static createStep (start : VColor, finish : VColor, steps : number) : VColor
{
var step_R : number = (finish.R - start.R) / steps;
var step_G : number = (finish.G - start.G) / steps;
var step_B : number = (finish.B - start.B) / steps;
return new VColor (step_R, step_G, step_B);
}
}
/*
* Background animator...
*/
class AnimateBackground
{
// Shader attributes...
private program : WebGLProgram;
private uni_vertex_colors : WebGLUniformLocation;
private attr_vertex_coord : number;
// Buffers...
private vertex_buffer : WebGLBuffer;
// Colors stuff...
private colors : Float32Array;
private current_color : VColor[];
private target_color : VColor[];
private step_color : VColor[];
private current_step : number=0;
//
// Constructor...
//
constructor (private canvas : HTMLCanvasElement, private gl : WebGLRenderingContext)
{
// Create shaders program...
this.program = this.createProgram ();
this.gl.useProgram (this.program);
// Extract and enable the "vertexCoord" attribute...
this.attr_vertex_coord = this.gl.getAttribLocation (this.program, "vertexCoord");
this.gl.enableVertexAttribArray (this.attr_vertex_coord);
// Create vertices, color and indices buffers...
this.vertex_buffer = this.gl.createBuffer ();
// Initialize triangle vertices...
var vertices = [-1,+1, +1,+1, -1,-1, +1,-1];
this.gl.bindBuffer (this.gl.ARRAY_BUFFER, this.vertex_buffer);
this.gl.bufferData (this.gl.ARRAY_BUFFER, new Float32Array (vertices), this.gl.STATIC_DRAW);
// Initialize background colors...
this.current_color = new Array (N_PNT);
this.target_color = new Array (N_PNT);
this.step_color = new Array (N_PNT);
var cookie_loaded = this.loadCookie ();
for (var i = 0; i < this.current_color.length; ++i)
{
this.target_color[i] = new VColor ();
if (!cookie_loaded) this.current_color[i] = new VColor();
this.step_color[i] = VColor.createStep (this.current_color[i], this.target_color[i], STEPS);
}
this.colors = new Float32Array (this.current_color.length * 3);
}
//
// Get and decode the cookie with given name...
//
public static getCookie (name : string) : string
{
var cookies : string[] = document.cookie.split (";");
if (cookies)
for (var i = 0; i < cookies.length; ++i)
{
var keyvalue : string[] = cookies[i].split ("=");
if (keyvalue && (keyvalue.length == 2))
if (keyvalue[0].trim () == name)
{
return decodeURI (keyvalue[1]);
}
}
}
//
// Load colors from cookie...
//
public loadCookie () : boolean
{
var cookie : string = AnimateBackground.getCookie (COOKY);
if (cookie)
{
var array : string[] = cookie.split (SPLIT);
if (array && (array.length == N_PNT * 3))
{
for (var i = 0, j = 0; i < N_PNT; ++i)
{
var cR = parseFloat (array[j++]);
var cG = parseFloat (array[j++]);
var cB = parseFloat (array[j++]);
this.current_color[i] = new VColor (cR, cG, cB);
}
return true;
}
}
return false;
}
//
// Save colors to cookie...
//
public saveCookie () : void
{
var str : string = "";
for (var i = 0; i < this.current_color.length; ++i)
{
if (i > 0) str += SPLIT;
var col : VColor = this.current_color[i];
str += col.R + SPLIT + col.G + SPLIT + col.B;
}
document.cookie = COOKY + "=" + encodeURI (str) + "; path=/";
}
//
// Start the core animation infinite loop...
//
public startAnimation ()
{
this.animateLoop (0);
}
//
// Draw the background...
//
private drawBackground (timekey)
{
// Set colors buffer...
for (var i = 0, j = 0; i < this.current_color.length; ++i)
{
this.colors[j++] = this.current_color[i].R;
this.colors[j++] = this.current_color[i].G;
this.colors[j++] = this.current_color[i].B;
}
// Draw triangles...
this.gl.bindBuffer (this.gl.ARRAY_BUFFER, this.vertex_buffer);
this.gl.vertexAttribPointer (this.attr_vertex_coord, 2, this.gl.FLOAT, false, 0, 0);
this.gl.uniform3fv (this.uni_vertex_colors, this.colors);
this.gl.drawArrays (this.gl.TRIANGLE_STRIP, 0, N_PNT);
}
//
// Update colors for next frame...
//
private nextFrame ()
{
this.current_step += 1;
if (this.current_step >= STEPS)
{
this.current_step = 0;
for (var i = 0; i < this.current_color.length; ++i)
{
this.current_color[i] = this.target_color[i];
this.target_color[i] = new VColor ();
this.step_color[i] = VColor.createStep (this.current_color[i], this.target_color[i], STEPS);
}
}
else
{
for (var i = 0; i < this.current_color.length; ++i)
{
this.current_color[i].addColor (this.step_color[i]);
}
}
}
//
// Create shader program...
//
private createProgram () : WebGLProgram
{
// Vertex shader...
var vertex_shader_text : string =
"attribute vec2 vertexCoord; \
varying vec2 point2D; \
\
void main (void) \
{ \
point2D = vertexCoord * 0.5 + 0.5; \
gl_Position = vec4 (vertexCoord, 0.0, 1.0); \
}";
// Fragment shader...
var fragment_shader_text : string =
"#define TL 0\n \
#define TR 1\n \
#define BL 2\n \
#define BR 3\n \
\
precision mediump float; \
\
varying vec2 point2D; \
uniform vec3 vColors[4]; \
\
void main (void) \
{ \
gl_FragColor = vec4 (mix (mix (vColors[TL], vColors[TR], point2D.x), mix (vColors[BL], vColors[BR], point2D.x), point2D.y), 1); \
}";
// Get the shaders from <script> tag...
var vertex_shader : WebGLShader = this.createShader (this.gl.VERTEX_SHADER, vertex_shader_text);
var pixel_shader: WebGLShader = this.createShader (this.gl.FRAGMENT_SHADER, fragment_shader_text);
if (!vertex_shader || !pixel_shader) return null;
// Create the program...
var program : WebGLProgram = this.gl.createProgram ();
this.gl.attachShader (program, vertex_shader);
this.gl.attachShader (program, pixel_shader);
this.gl.linkProgram(program);
// Extract uniform location for the 4 corners vertex colors...
this.uni_vertex_colors = this.gl.getUniformLocation (program, "vColors");
// Finished!
return program;
}
//
// Create a shader of given type with given string...
//
private createShader (shaderType : number, shaderText : string) : WebGLShader
{
var shader : WebGLShader = this.gl.createShader (shaderType);
this.gl.shaderSource (shader, shaderText);
this.gl.compileShader (shader);
return this.gl.getShaderParameter (shader, this.gl.COMPILE_STATUS) ? shader : null;
}
//
// Make sure (CSS) width/height of canvas is equal to client width/height...
//
private applyResize () : boolean
{
var ret : boolean = false;
if (this.canvas.width != this.canvas.clientWidth)
{
this.canvas.width = this.canvas.clientWidth;
ret = true;
}
if (this.canvas.height != this.canvas.clientHeight)
{
this.canvas.height = this.canvas.clientHeight;
ret = true;
}
return ret;
}
//
// Main animation loop...
//
private animateLoop = (timekey) =>
{
// Process canvas resize...
this.applyResize ();
this.gl.viewport (0, 0, this.gl.drawingBufferWidth, this.gl.drawingBufferHeight);
// Draw frame...
this.drawBackground (timekey);
// Next frame...
this.nextFrame ();
requestAnimationFrame (this.animateLoop);
}
}
var animator : AnimateBackground = null;
window.onload = () =>
{
// Get main header element...
var header : HTMLElement = document.getElementById ('masthead');
if (header)
{
// Get the canvas element...
var canvas : HTMLCanvasElement = document.getElementById ('canvasHeader');
if (canvas)
{
// Make sure WebGL context can be obtained...
var context: WebGLRenderingContext = canvas.getContext ("experimental-webgl");
if (!context) context = canvas.getContext ("webgl");
// We have a context...
if (context)
{
// Start animation...
animator = new AnimateBackground (canvas, context);
animator.startAnimation ();
// Remove background from header...
header.style.background = "transparent";
}
}
}
};
window.onunload = () =>
{
if (animator) animator.saveCookie ();
};
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.