Update some existing projects and as well adding new

This commit is contained in:
Michael Reber 2021-02-09 15:42:15 +01:00
parent 243a252e24
commit 0ef1c36a5e
67 changed files with 5539 additions and 1227 deletions

View File

@ -2,4 +2,4 @@
This repository contains a collection of carefully engineered html5 snippets, which can be used in your own projects.
![image](/attachments/1160e58c-99cd-436c-b77f-b543430e6d08)
![image](/attachments/a3c79d4f-ab7e-4260-9596-7ddb9fd972e5)

View File

@ -1,5 +1,7 @@
# [3D] Synth Canyon Animation
# 3D Synth Canyon Animation
I wanted to do some experimentation with manipulating geometries and layering several simple shader effects, and this is what I came up with.
The Soundcloud embed is just there for ambiance.
![image](/attachments/2c7bac61-a6a9-40bd-89a8-909d8118a38d)

View File

@ -1,3 +1,5 @@
# Basic Matrix Effect
Hello World in 92 Languages falling down in Matrix code rain
![image](/attachments/0bd4f9d5-0ebf-4c8c-8100-5464f00d72d5)

View File

@ -10,7 +10,8 @@
<!-- Hello World in 92 Languages falling down in Matrix code rain-->
<script src='https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js'></script><script src="./script.js"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js'></script>
<script src="./script.js"></script>
</body>
</html>

View File

@ -0,0 +1,6 @@
# Floating Words
The words comes from song "Red like Roses part II" - RWBY Theme
size and speed determine from its frequency in lyrics
![image](/attachments/c5fac2e6-e204-4177-b695-b86eeafc3f54)

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Floating Words</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<canvas id="c"></canvas>
<script src="./script.js"></script>
</body>
</html>

View File

@ -0,0 +1,87 @@
window.onload = function(argument) {
// var lyric = "i couldn't take it couldn't stand another minute couldn't bear another day without you in it";
var lyric = "i couldn't take it couldn't stand another minute couldn't bear another day without you in it all of the joy that I had known for my life was stripped away from me the minute that you died to have you in my life was all i ever wanted but now without you I'm a soul forever haunted can't help but feel that i had taken you for granted no way in hell that i can ever comprehend this i wasn't dreaming when they told me you were gone i was wide awake and feeling that they had to be wrong how could you leave me when you swore that you would stay now i'm trapped inside a nightmare every single fucking day it's like a movie but there's not a happy ending every scene fades black and there's no pretending this little fairy tale doesn't seem to end well theres no knight in shining armor who will wake me from the spell i know you didn't plan this you tried to do what's right but in the middle of this madness i'm the one you left to win this fight red like roses fills my head with dreams and finds me always closer to the emptiness and sadness that has come to take the place of you i know you're broken down by anger and by sadness you feel I left you in a world that's full of madness wish i could talk to you if only for a minute make you understand the reasons why i did it i wanna tell you that you're all that ever mattered want you to know that for eternity i'm shattered i tried so hard just to protect you but i failed to and in a prison of abandonment i've jailed you i never planned that i would leave you there alone i was sure that i would see you when i made it back home and all the times I swore that it would be okay now i'm nothing but a liar and you're thrown into the fray this bedtime story ends with misery ever after the pages are torn and there's no final chapter i didn't have a choice I did what I had to do i made a sacrifice but forced a bigger sacrifice on you i know you've lived a nightmare i caused you so much pain but baby please don't do what i did i don't want you to waste your life in vain red like roses fills my head with dreams and finds me always closer to the emptiness and sadness that has come to take the place of you you're not the only one who needed me i thought you understood you were the one i needed and you left me as I always feared you would would I change it if i could? it doesn't matter how the petals scatter now every nightmare just discloses it's your blood that's red like roses and no matter what I do nothing ever takes the place of you red like roses fills my head with dreams and finds me always closer to the emptiness and sadness that has come to take the place of you";
var words = {};
var words_attr = [];
string_handle(lyric);
var canvas = document.getElementById('c');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
if (canvas.getContext) {
var c = canvas.getContext('2d'),
w = canvas.width,
h = canvas.height;
c.strokeStyle = 'red';
c.fillStyle = 'white';
c.lineWidth = 5;
// constructor
Word = function(key) {
this.text = key;
this.x = Math.random() * w;
this.y = Math.random() * h;
this.font = words[key] * 10 + 'px arial'
this.speed = (words[key]);
}
for (key in words) {
words_attr.push(new Word(key));
}
console.log(words_attr.length);
function animation() {
for (var i = 0; i < words_attr.length; i++) {
c.font = words_attr[i].font;
c.fillText(words_attr[i].text, words_attr[i].x, words_attr[i].y);
words_attr[i].width = c.measureText(words_attr[i].text).width;
c.stroke();
}
move();
}
function move() {
for (var i = 0; i < words_attr.length; i++) {
if (words_attr[i].x > w) {
words_attr[i].x = -words_attr[i].width;
words_attr[i].y = Math.random()*h;
}else{
words_attr[i].x += words_attr[i].speed;
}
}
}
setInterval(function() {
c.clearRect(0,0,w,h);
animation();
},24);
}
function string_handle(str) {
var split_str = str.split(" ");
var word_array = [];
var word_count = [];
for (var i = 0; i < split_str.length; i++) {
check = true;
for (var j = 0; j <= word_array.length; j++) {
if (split_str[i] == word_array[j]) {
word_count[j]++;
check = false;
break;
}
}
if (check) {
word_array.push(split_str[i]);
word_count.push(1);
}
}
for (var i = 0; i < word_array.length; i++) {
words[word_array[i]] = word_count[i];
}
return words;
}
}

View File

@ -0,0 +1,4 @@
body{
background:#141414;
overflow:hidden;
}

View File

@ -0,0 +1,3 @@
# Inifinity Tunnel
![image](/attachments/49ed7d89-9d9c-44c3-bd7d-ec29bad4a48d)

View File

@ -0,0 +1,726 @@
/**
* A basic Web GL class. This provides a very basic setup for GLSL shader code.
* Currently it doesn't support anything except for clip-space 3d, but this was
* done so that we could start writing fragments right out of the gate. My
* Intention is to update it with particle and polygonal 3d support later on.
*
* @class WTCGL
* @author Liam Egan <liam@wethecollective.com>
* @version 0.0.8
* @created Jan 16, 2019
*/
class WTCGL {
/**
* The WTCGL Class constructor. If construction of the webGL context fails
* for any reason this will return null.
*
* @TODO make the dimension properties properly optional
* @TODO provide the ability to allow for programmable buffers
*
* @constructor
* @param {HTMLElement} el The canvas element to use as the root
* @param {string} vertexShaderSource The vertex shader source
* @param {string} fragmentShaderSource The fragment shader source
* @param {number} [width] The width of the webGL context. This will default to the canvas dimensions
* @param {number} [height] The height of the webGL context. This will default to the canvas dimensions
* @param {number} [pxratio=1] The pixel aspect ratio of the canvas
* @param {boolean} [styleElement] A boolean indicating whether to apply a style property to the canvas (resizing the canvas by the inverse of the pixel ratio)
* @param {boolean} [webgl2] A boolean indicating whether to try to create a webgl2 context instead of a regulart context
*/
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;

View File

@ -0,0 +1,286 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Control 2</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<canvas id="webgl" width="500" height="1758"></canvas>
<script id="vertexShader" type="x-shader/x-vertex">
attribute vec4 a_position;
uniform mat4 u_modelViewMatrix;
uniform mat4 u_projectionMatrix;
void main() {
gl_Position = a_position;
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
precision highp float;
precision highp int;
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
uniform sampler2D u_noise;
// movement variables
vec3 movement = vec3(.0);
const int maxIterations = 256;
const float stopThreshold = 0.002;
const float stepScale = .5;
const float eps = 0.002;
const vec3 clipColour = vec3(1.);
const vec3 fogColour = vec3(1.);
const vec3 light1_position = vec3(0, 1., -1.);
const vec3 light1_colour = vec3(.8, .8, .85);
struct Surface {
int object_id;
float distance;
vec3 position;
vec3 onsurface_position;
vec3 colour;
float steps;
float ambient;
float spec;
float fog;
};
// Distance function copyright Inigo Quilez
float opExtrusion( in vec3 p, in float primitive, in float h ) {
float d = primitive;
vec2 w = vec2( d, abs(p.z) - h );
return min(max(w.x,w.y),0.0) + length(max(w,0.0));
}
float sdBox( in vec2 p, in vec2 b ) {
vec2 d = abs(p)-b;
return length(max(d,0.0)) + min(max(d.x,d.y),0.0);
}
vec3 path(float z) {
// return vec3(0,0,0.);
return vec3(sin(z * .1) * 4., sin(z * .05) * 10., z);
}
float getBlock(vec3 position, inout int object_id, inout vec3 p) {
// position.z = fract(position.z) - .5;
// object_id = int(id);
float id = float(object_id);
float r = sin(id * .3 + u_time) * .5;
r = sin(texture2D(u_noise, vec2((id*2.)/255.)).x * 2. + u_time - id) * .25;
float rw = texture2D(u_noise, vec2((id*2.)/255.)).x - .5;
// // position.xy += r;
float s = sin(r);
float c = cos(r);
position.xy *= mat2(c, -s, s, c);
p = position;
float box = sdBox(position.xy, vec2(1.5 + rw, 1.)) * -1.;
float world = opExtrusion(position, box, .45) - .005;
// world += smoothstep(.04, 0., abs(sin(p.x * 4.))) * .01;
// world += smoothstep(.02, 0., abs(sin(p.y * 2.))) * .01;
return world;
}
// This function describes the world in distances from any given 3 dimensional point in space
float world(in vec3 position, inout int object_id, inout vec3 p) {
position.xy -= path(position.z).xy;
float id = floor(position.z);
position.z = fract(position.z) - .5;
int oid1 = int(id);
vec3 p1;
float block1 = getBlock(position, oid1, p1);
int oid2 = int(id+1.);
vec3 p2;
float block2 = getBlock(position + vec3(0,0,-1), oid2, p2);
object_id = oid1;
p = p1;
if(block2 < block1) {
block1 = block2;
object_id = oid2;
p = p2;
}
return block1;
}
float world(in vec3 position, inout int object_id) {
vec3 p;
return world(position, object_id, p);
}
float world(in vec3 position) {
int dummy = 0;
return world(position, dummy);
}
Surface getSurface(int object_id, float rayDepth, vec3 sp, float steps, vec3 onsurface_pos, float fog) {
return Surface(
object_id,
rayDepth,
sp,
onsurface_pos,
vec3(1.),
steps,
.5,
1000.,
fog);
}
// The raymarch loop
Surface rayMarch(vec3 ro, vec3 rd, float start, float end) {
float sceneDist = 1e4;
float rayDepth = start;
int object_id = 0;
float steps = 0.;
float fog = 0.;
vec3 p;
for(int i = 0; i < maxIterations; i++) {
sceneDist = world(ro + rd * rayDepth, object_id, p);
rd += (texture2D(u_noise, (p.xy)*255.).rgb-.5)*.0005;
steps++;
fog += max(sceneDist, 0.);
if(sceneDist < stopThreshold || rayDepth > end) {
break;
}
rayDepth += sceneDist * stepScale;
}
return getSurface(object_id, rayDepth, ro + rd * rayDepth, steps, p, fog);
}
// Calculated the normal of any given point in space. Intended to be cast from the point of a surface
vec3 calculate_normal(in vec3 position) {
vec3 grad = vec3(
world(vec3(position.x + eps, position.y, position.z)) - world(vec3(position.x - eps, position.y, position.z)),
world(vec3(position.x, position.y + eps, position.z)) - world(vec3(position.x, position.y - eps, position.z)),
world(vec3(position.x, position.y, position.z + eps)) - world(vec3(position.x, position.y, position.z - eps))
);
return normalize(grad);
}
vec3 lighting(Surface surface_object, vec3 cam) {
// start with black
vec3 sceneColour = vec3(0);
// Surface normal
vec3 normal = calculate_normal(surface_object.position);
normal += smoothstep(.02, 0., abs(sin(surface_object.onsurface_position.x * 4.))) * .5;
normal += smoothstep(.01, 0., abs(sin(surface_object.onsurface_position.y * 2.))) * .5;
// Light position
vec3 lp = path(u_time*3.+15.);
// Light direction
vec3 ld = lp - surface_object.position;
// light attenuation
// For brightly lit scenes or global illumination (like sunlit), this can be limited to just normalizing the ld
float len = length( ld );
ld = normalize(ld);
float lightAtten = min( 1.0 / ( 0.15*len ), 1.0 );
lightAtten = 1.;
// The surface's light reflection normal
vec3 reflection_normal = reflect(-ld, normal);
// Ambient Occlusion
float ao = (surface_object.steps*.01*(1./(surface_object.fog*.07)));
ao *= ao*2.;
ao = clamp(
((1.-ao)+.3)
, 0., 1.);
// ao -= surface_object.steps*.005;
// Object surface properties
float diffuse = max(0., dot(normal, ld));
float specular = max(0., dot( reflection_normal, normalize(cam - surface_object.position) ));
// Bringing all of the lighting components together
vec3 tp = surface_object.onsurface_position * 2.;
vec3 c = texture2D(u_noise, tp.xy + tp.zx).rrr + texture2D(u_noise, tp.zy + tp.yx).rrr;
tp = surface_object.onsurface_position * vec3(.8, .8, 1.);
c += texture2D(u_noise, tp.xy + tp.zx).rrr + texture2D(u_noise, tp.zy + tp.yx).rrr;
tp = surface_object.onsurface_position * vec3(1., .4, .4);
c += texture2D(u_noise, tp.xy + tp.zx).rrr + texture2D(u_noise, tp.zy + tp.yx).rrr;
c *= .125;
c *= .5 + .5;
c *= vec3(1,.98,.90);
sceneColour += ( c * (diffuse + specular )) * light1_colour * lightAtten * ao;
// adding fog
float fogl = surface_object.fog*.04;
fogl *= smoothstep(-.5, 1.8, fogl);
sceneColour = mix( sceneColour, fogColour, fogl );
return sceneColour;
}
void main() {
vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution.xy) / min(u_resolution.y, u_resolution.x);
float t = u_time * 3.;
// movement
movement = path(t);
// Camera and look-at
vec3 cam = vec3(0,0,-2);
vec3 lookAt = vec3(0,-.5,-2.);
// add movement
lookAt = path(t+3.);
cam = movement;
// cam.y += abs(sin(u_time*20.)*.02);
// Unit vectors
vec3 forward = normalize(lookAt - cam);
vec3 right = normalize(vec3(forward.z, 0., -forward.x));
vec3 up = normalize(cross(forward, right));
// FOV
float FOV = 1.4;
// Ray origin and ray direction
vec3 ro = cam;
vec3 rd = normalize(forward + FOV * uv.x * right + FOV * uv.y * up);
rd.y -= (movement.y - lookAt.y) * .04;
// Ray marching
const float clipNear = 0.;
const float clipFar = 20.;
Surface objectSurface = rayMarch(ro, rd, clipNear, clipFar);
if(objectSurface.distance > clipFar) {
gl_FragColor = vec4(clipColour, 1.);
return;
}
vec3 sceneColour = lighting(objectSurface, cam);
gl_FragColor = vec4(sceneColour, 1.);
// gl_FragColor = vec4(vec3(objectSurface.fog*.03), 1.);
// gl_FragColor.rgb *= objectSurface.fog*.05;
}
</script>
<script src="./script.js"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

View File

@ -0,0 +1,833 @@
/**
* A basic Web GL class. This provides a very basic setup for GLSL shader code.
* Currently it doesn't support anything except for clip-space 3d, but this was
* done so that we could start writing fragments right out of the gate. My
* Intention is to update it with particle and polygonal 3d support later on.
*
* @class WTCGL
* @author Liam Egan <liam@wethecollective.com>
* @version 0.0.8
* @created Jan 16, 2019
*/
class WTCGL {
/**
* The WTCGL Class constructor. If construction of the webGL context fails
* for any reason this will return null.
*
* @TODO make the dimension properties properly optional
* @TODO provide the ability to allow for programmable buffers
*
* @constructor
* @param {HTMLElement} el The canvas element to use as the root
* @param {string} vertexShaderSource The vertex shader source
* @param {string} fragmentShaderSource The fragment shader source
* @param {number} [width] The width of the webGL context. This will default to the canvas dimensions
* @param {number} [height] The height of the webGL context. This will default to the canvas dimensions
* @param {number} [pxratio=1] The pixel aspect ratio of the canvas
* @param {boolean} [styleElement] A boolean indicating whether to apply a style property to the canvas (resizing the canvas by the inverse of the pixel ratio)
* @param {boolean} [webgl2] A boolean indicating whether to try to create a webgl2 context instead of a regulart context
*/
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,
false);
twodWebGL.startTime = -10;
let debounce;
window.addEventListener('resize', () => {
clearInterval(debounce);
debounce = setInterval(() => {
twodWebGL.resize(window.innerWidth, window.innerHeight);
}, 100);
});
// 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: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/982762/noise.png',
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');
});

View File

@ -0,0 +1,9 @@
body {
margin:0;
}
canvas {
height: 100vh !important;
position: fixed;
width: 100vw !important;
}

View File

@ -0,0 +1,3 @@
# Modern Tunnel Vision
![image](/attachments/8dc80b64-8e2c-4df4-ad9c-f3ebf4f15374)

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Tunnel Vision</title>
<link rel="stylesheet" href="./style.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r124/three.min.js"></script>
</head>
<body>
<canvas></canvas>
<script src="./script.js"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 KiB

View File

@ -0,0 +1,85 @@
//===================================================== Create a WebGL renderer
var renderer = new THREE.WebGLRenderer({
canvas: document.querySelector("canvas"),
powerPreference: "high-performance"
});
renderer.setSize(window.innerWidth, window.innerHeight);
//===================================================== Create an empty scene
var scene = new THREE.Scene();
//===================================================== Create a perpsective camera
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.001, 1000);
camera.position.z = 400;
//===================================================== resize
window.addEventListener("resize", function() {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
});
//===================================================== Array of points
var points = [
[68.5,185.5],
[1,262.5],
[270.9,281.9],
[345.5,212.8],
[178,155.7],
[240.3,72.3],
[153.4,0.6],
[52.6,53.3],
[68.5,185.5]
];
//===================================================== Convert the array of points into vertices
for (var i = 0; i < points.length; i++) {
var x = points[i][0];
var y = 0;
var z = points[i][1];
points[i] = new THREE.Vector3(x, y, z);
}
//===================================================== Create a path from the points
var path = new THREE.CatmullRomCurve3(points);
//===================================================== Create the tube geometry from the path
var sides = 3;
var geometry = new THREE.TubeGeometry( path, 300, 4, sides, true );
//===================================================== Basic material
var material = new THREE.MeshBasicMaterial({
side : THREE.BackSide,
map: new THREE.TextureLoader().load('https://raw.githubusercontent.com/baronwatts/images/master/map5.png')
});
material.map.wrapS = THREE.RepeatWrapping;
material.map.wrapT= THREE.RepeatWrapping;
material.map.repeat.set(10, 1)
//===================================================== Create a mesh
var tube = new THREE.Mesh( geometry, material );
tube.matrixAutoUpdate = false;//wont be moving so no need to update
scene.add( tube );
//===================================================== Create a point light in our scene
var light = new THREE.PointLight(new THREE.Color("white"),1, 100);
scene.add(light);
//===================================================== Animate
var percentage = 0;
function animate() {
percentage += 0.0005;
var p1 = path.getPointAt(percentage%1);
var p2 = path.getPointAt((percentage + 0.03)%1);
camera.position.set(p1.x,p1.y,p1.z);
camera.lookAt(p2);
light.position.set(p2.x, p2.y, p2.z);
//Render the scene
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();

View File

@ -0,0 +1,8 @@
html,
body {
margin: 0;
padding: 0;
background: black;
overflow: hidden;
height: 100%;
}

View File

@ -0,0 +1,4 @@
# Time to Drink Coffee Animation
![image](/attachments/dfe21f29-12a9-484b-a194-017141825c74)

View File

@ -0,0 +1,72 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Time to Drink Coffee Animation</title>
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Barlow+Semi+Condensed:wght@500&display=swap" rel="stylesheet"><link rel="stylesheet" href="./style.css">
</head>
<body>
<div class="draw-box">
<div class="typewriter-effect">TIME TO DRINK COFFEE?</div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<defs>
<mask id="rippleMask">
<path
class="ripple"
d="M7.06,99.17c-.65-5.6,12.32,8.32,20.19-1.53C39,83,51.84,90.68,55.48,90.08c8.16-1.35,10-11.68,19-17s20.87,7.17,28.7-3.86c5.1-7.18,9.56-9.6,13.77-11.17,9.51-3.54,19.25,12.07,23.29.38,7-20.43,26.91-6.41,31.46-20,3-9,14.37-14.93,29.66-10C217.15,33.48,222-5.11,236.72,3.69c19.63,11.74,40,74.88,50.33,104.82,29.8,86-61.63,121.55-82.21,130.28C148,262.91,91.31,274,53.8,208.12,53.8,208.12,8.23,109.3,7.06,99.17Z"
fill="white"
/>
</mask>
<filter id="noise">
<feTurbulence
baseFrequency="0.01 0.1"
result="WAVE"
numOctaves="1"
id="turbulence"
/>
<feDisplacementMap
in="SourceGraphic"
in2="WAVE"
scale="1.2"
></feDisplacementMap>
</filter>
</defs>
<!-- Coffee cup -->
<path
class="coffeeColor"
mask="url(#rippleMask)"
d="M18.26,30.74c.83-.89,3.88-1.18,5.38.65,3.22,4,7.52,1.41,9.24-.17,3.74-3.42,6.53,4.75,11.75,1.16,3.55-2.45,5.34.12,8.95.33a9.38,9.38,0,0,0,6-1.68c4-2.5,6.35,3.33,10,2.88s4.91-3.5,7.79-2.77c0,0,0,1.82,0,2.49a14.07,14.07,0,0,1,11,.95C94.63,38,94,53.64,79.66,59.23l-5.33,2C73.13,66.54,69.6,73,66,73.68l-16.55,0H29.87c-10.81-1.87-11.58-33.19-11.6-44.1Zm67.49,8.38c-3.42-2.67-8.41-.38-8.46.49-.55,8.8-1.73,13.94-1.77,15.13,0,.45,7.28-1.55,10-5.28C87.39,46.77,89.15,41.79,85.75,39.12Z"
/>
<g mask="url(#rippleMask)" filter="url(#noise)">
<path
class="steamColor"
d="M43,19.66c-.34.41.27-2.76-1.14-4.48-1.54-1.9-2.1-2.26-3-4-1.56-3.13,1.28-5.87,1.22-4.37C40,11,41.34,12.74,41.82,13.34a7.13,7.13,0,0,1,1.74,3A4.07,4.07,0,0,1,43,19.66Z"
/>
<path
class="steamColor"
d="M49,18.84c-.15.17-.25-.63.2-1.62,1.93-4.27-2.94-7.18-3-9.67-.08-3.48,2.58-4.64,2-3.76-2.8,4.68,1.66,6.79,2.42,9.85C51.18,15.53,49.89,17.91,49,18.84Z"
/>
<path
class="steamColor"
d="M55.26,19.67c-.16.13-.3-.92.23-1.8,3.3-5.52-.66-4.67.14-9.24.56-3.26,3.34-4,2.72-3.27C55.09,9.42,58,11.74,57.54,15A8.66,8.66,0,0,1,55.26,19.67Z"
/>
</g>
<!-- Stroke -->
<path
class="stroke"
stroke-dasharray="267.4718933105469"
stroke-dashoffset="267.4718933105469"
d="M88.35,34.6a14,14,0,0,0-11-1c0-1.52.14-7.74.16-8,.08-1.52-1.8-1.52-1.8-1.52l-25.35,0H45.65l-25.35.05s-1.88,0-1.79,1.52S17.26,71.33,30.07,73.55h36c3.63-.64,7.14-7.11,8.34-12.41l5.31-2C94,53.59,94.62,38,88.35,34.6ZM85.49,49.42c-2.66,3.72-9.92,5.72-9.91,5.27,0-1.19,1.21-6.31,1.76-15.08,0-.87,5-3.16,8.43-.49S87.41,46.74,85.49,49.42Z"
/>
</svg>
</div>
<script src='https://unpkg.co/gsap@3/dist/gsap.min.js'></script><script src="./script.js"></script>
</body>
</html>

View File

@ -0,0 +1,18 @@
const tl = gsap.timeline();
tl.to('.stroke', {
duration: 2.5,
delay: 1.8,
strokeDashoffset: 0 }).
to('.ripple', {
duration: 4,
delay: -2,
xPercent: -50,
yPercent: -60 }).
to('#turbulence', {
duration: 1.5,
delay: -2,
attr: {
baseFrequency: '0 0' } });

View File

@ -0,0 +1,75 @@
body {
background-color: #232f3a;
height: 100vh;
display: grid;
place-items: center;
}
body .draw-box {
overflow: hidden;
position: relative;
margin-top: 60px;
width: 335px;
}
body .draw-box .typewriter-effect {
overflow: hidden;
-webkit-animation: typingEffect 1.6s steps(22) forwards, blinkTextCursor 1s infinite;
animation: typingEffect 1.6s steps(22) forwards, blinkTextCursor 1s infinite;
width: 0;
color: #e8e8e8;
height: 30px;
font-size: 34px;
white-space: nowrap;
vertical-align: middle;
line-height: 0.9;
font-family: "Barlow Semi Condensed", sans-serif;
}
@-webkit-keyframes typingEffect {
from {
width: 0;
}
to {
width: 98%;
}
}
@keyframes typingEffect {
from {
width: 0;
}
to {
width: 98%;
}
}
@-webkit-keyframes blinkTextCursor {
from {
border-right: 3px solid #f8f8f8;
}
to {
border-right: transparent;
}
}
@keyframes blinkTextCursor {
from {
border-right: 3px solid #f8f8f8;
}
to {
border-right: transparent;
}
}
body .draw-box svg {
width: 93%;
margin-top: 10px;
}
body .draw-box svg .stroke {
fill: none;
stroke: #e8e8e8;
stroke-width: 0.8;
}
body .draw-box svg .coffeeColor {
fill: #b59440;
}
body .draw-box svg .steamColor {
fill: #ddd;
}
body .draw-box svg .ripple {
transform: translate3d(-30%, 100%, 0);
}

View File

@ -0,0 +1,5 @@
# Confetti Button
Confetti using JS, SCSS, and a HTML5 canvas.
![image](/attachments/037e2fe9-8348-4313-9404-246e8cdd2d7c)

View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Confetti Button</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css">
<link rel="stylesheet" href="./style.css">
</head>
<body>
<button id="button" class="ready" onclick="clickButton();">
<div class="message submitMessage">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13 12.2">
<polyline stroke="currentColor" points="2,7.1 6.5,11.1 11,7.1 "/>
<line stroke="currentColor" x1="6.5" y1="1.2" x2="6.5" y2="10.3"/>
</svg> <span class="button-text">Submit</span>
</div>
<div class="message loadingMessage">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19 17">
<circle class="loadingCircle" cx="2.2" cy="10" r="1.6"/>
<circle class="loadingCircle" cx="9.5" cy="10" r="1.6"/>
<circle class="loadingCircle" cx="16.8" cy="10" r="1.6"/>
</svg>
</div>
<div class="message successMessage">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13 11">
<polyline stroke="currentColor" points="1.4,5.8 5.1,9.5 11.6,2.1 "/>
</svg> <span class="button-text">Success</span>
</div>
</button>
<canvas id="canvas"></canvas>
<script src="./script.js"></script>
</body>
</html>

View File

@ -0,0 +1,238 @@
// ammount to add on each button press
const confettiCount = 20
const sequinCount = 10
// "physics" variables
const gravityConfetti = 0.3
const gravitySequins = 0.55
const dragConfetti = 0.075
const dragSequins = 0.02
const terminalVelocity = 3
// init other global elements
const button = document.getElementById('button')
var disabled = false
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
canvas.width = window.innerWidth
canvas.height = window.innerHeight
let cx = ctx.canvas.width / 2
let cy = ctx.canvas.height / 2
// add Confetto/Sequin objects to arrays to draw them
let confetti = []
let sequins = []
// colors, back side is darker for confetti flipping
const colors = [
{ front : '#7b5cff', back: '#6245e0' }, // Purple
{ front : '#b3c7ff', back: '#8fa5e5' }, // Light Blue
{ front : '#5c86ff', back: '#345dd1' } // Darker Blue
]
// helper function to pick a random number within a range
randomRange = (min, max) => Math.random() * (max - min) + min
// helper function to get initial velocities for confetti
// this weighted spread helps the confetti look more realistic
initConfettoVelocity = (xRange, yRange) => {
const x = randomRange(xRange[0], xRange[1])
const range = yRange[1] - yRange[0] + 1
let y = yRange[1] - Math.abs(randomRange(0, range) + randomRange(0, range) - range)
if (y >= yRange[1] - 1) {
// Occasional confetto goes higher than the max
y += (Math.random() < .25) ? randomRange(1, 3) : 0
}
return {x: x, y: -y}
}
// Confetto Class
function Confetto() {
this.randomModifier = randomRange(0, 99)
this.color = colors[Math.floor(randomRange(0, colors.length))]
this.dimensions = {
x: randomRange(5, 9),
y: randomRange(8, 15),
}
this.position = {
x: randomRange(canvas.width/2 - button.offsetWidth/4, canvas.width/2 + button.offsetWidth/4),
y: randomRange(canvas.height/2 + button.offsetHeight/2 + 8, canvas.height/2 + (1.5 * button.offsetHeight) - 8),
}
this.rotation = randomRange(0, 2 * Math.PI)
this.scale = {
x: 1,
y: 1,
}
this.velocity = initConfettoVelocity([-9, 9], [6, 11])
}
Confetto.prototype.update = function() {
// apply forces to velocity
this.velocity.x -= this.velocity.x * dragConfetti
this.velocity.y = Math.min(this.velocity.y + gravityConfetti, terminalVelocity)
this.velocity.x += Math.random() > 0.5 ? Math.random() : -Math.random()
// set position
this.position.x += this.velocity.x
this.position.y += this.velocity.y
// spin confetto by scaling y and set the color, .09 just slows cosine frequency
this.scale.y = Math.cos((this.position.y + this.randomModifier) * 0.09)
}
// Sequin Class
function Sequin() {
this.color = colors[Math.floor(randomRange(0, colors.length))].back,
this.radius = randomRange(1, 2),
this.position = {
x: randomRange(canvas.width/2 - button.offsetWidth/3, canvas.width/2 + button.offsetWidth/3),
y: randomRange(canvas.height/2 + button.offsetHeight/2 + 8, canvas.height/2 + (1.5 * button.offsetHeight) - 8),
},
this.velocity = {
x: randomRange(-6, 6),
y: randomRange(-8, -12)
}
}
Sequin.prototype.update = function() {
// apply forces to velocity
this.velocity.x -= this.velocity.x * dragSequins
this.velocity.y = this.velocity.y + gravitySequins
// set position
this.position.x += this.velocity.x
this.position.y += this.velocity.y
}
// add elements to arrays to be drawn
initBurst = () => {
for (let i = 0; i < confettiCount; i++) {
confetti.push(new Confetto())
}
for (let i = 0; i < sequinCount; i++) {
sequins.push(new Sequin())
}
}
// draws the elements on the canvas
render = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height)
confetti.forEach((confetto, index) => {
let width = (confetto.dimensions.x * confetto.scale.x)
let height = (confetto.dimensions.y * confetto.scale.y)
// move canvas to position and rotate
ctx.translate(confetto.position.x, confetto.position.y)
ctx.rotate(confetto.rotation)
// update confetto "physics" values
confetto.update()
// get front or back fill color
ctx.fillStyle = confetto.scale.y > 0 ? confetto.color.front : confetto.color.back
// draw confetto
ctx.fillRect(-width / 2, -height / 2, width, height)
// reset transform matrix
ctx.setTransform(1, 0, 0, 1, 0, 0)
// clear rectangle where button cuts off
if (confetto.velocity.y < 0) {
ctx.clearRect(canvas.width/2 - button.offsetWidth/2, canvas.height/2 + button.offsetHeight/2, button.offsetWidth, button.offsetHeight)
}
})
sequins.forEach((sequin, index) => {
// move canvas to position
ctx.translate(sequin.position.x, sequin.position.y)
// update sequin "physics" values
sequin.update()
// set the color
ctx.fillStyle = sequin.color
// draw sequin
ctx.beginPath()
ctx.arc(0, 0, sequin.radius, 0, 2 * Math.PI)
ctx.fill()
// reset transform matrix
ctx.setTransform(1, 0, 0, 1, 0, 0)
// clear rectangle where button cuts off
if (sequin.velocity.y < 0) {
ctx.clearRect(canvas.width/2 - button.offsetWidth/2, canvas.height/2 + button.offsetHeight/2, button.offsetWidth, button.offsetHeight)
}
})
// remove confetti and sequins that fall off the screen
// must be done in seperate loops to avoid noticeable flickering
confetti.forEach((confetto, index) => {
if (confetto.position.y >= canvas.height) confetti.splice(index, 1)
})
sequins.forEach((sequin, index) => {
if (sequin.position.y >= canvas.height) sequins.splice(index, 1)
})
window.requestAnimationFrame(render)
}
// cycle through button states when clicked
clickButton = () => {
if (!disabled) {
disabled = true
// Loading stage
button.classList.add('loading')
button.classList.remove('ready')
setTimeout(() => {
// Completed stage
button.classList.add('complete')
button.classList.remove('loading')
setTimeout(() => {
window.initBurst()
setTimeout(() => {
// Reset button so user can select it again
disabled = false
button.classList.add('ready')
button.classList.remove('complete')
}, 4000)
}, 320)
}, 1800)
}
}
// re-init canvas if the window size changes
resizeCanvas = () => {
canvas.width = window.innerWidth
canvas.height = window.innerHeight
cx = ctx.canvas.width / 2
cy = ctx.canvas.height / 2
}
// resize listenter
window.addEventListener('resize', () => {
resizeCanvas()
})
// click button on spacebar or return keypress
document.body.onkeyup = (e) => {
if (e.keyCode == 13 || e.keyCode == 32) {
clickButton()
}
}
// Set up button text transition timings on page load
textElements = button.querySelectorAll('.button-text')
textElements.forEach((element) => {
characters = element.innerText.split('')
let characterHTML = ''
characters.forEach((letter, index) => {
characterHTML += `<span class="char${index}" style="--d:${index * 30}ms; --dr:${(characters.length - index - 1) * 30}ms;">${letter}</span>`
})
element.innerHTML = characterHTML
})
// kick off the render loop
window.initBurst()
render()

View File

@ -0,0 +1,168 @@
@keyframes loading {
0% {
cy: 10;
}
25% {
cy: 3;
}
50% {
cy: 10;
}
}
body {
-webkit-font-smoothing: antialiased;
background-color: #f4f7ff;
}
canvas {
height: 100vh;
pointer-events: none;
position: fixed;
width: 100%;
z-index: 2;
}
button {
background: none;
border: none;
color: #f4f7ff;
cursor: pointer;
font-family: "Quicksand", sans-serif;
font-size: 14px;
font-weight: 500;
height: 40px;
left: 50%;
outline: none;
overflow: hidden;
padding: 0 10px;
position: fixed;
top: 50%;
transform: translate(-50%, -50%);
width: 190px;
-webkit-tap-highlight-color: transparent;
z-index: 1;
}
button::before {
background: #1f2335;
border-radius: 50px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.4) inset;
content: "";
display: block;
height: 100%;
margin: 0 auto;
position: relative;
transition: width 0.2s cubic-bezier(0.39, 1.86, 0.64, 1) 0.3s;
width: 100%;
}
button.ready .submitMessage svg {
opacity: 1;
top: 1px;
transition: top 0.4s ease 600ms, opacity 0.3s linear 600ms;
}
button.ready .submitMessage .button-text span {
top: 0;
opacity: 1;
transition: all 0.2s ease calc(var(--dr) + 600ms);
}
button.loading::before {
transition: width 0.3s ease;
width: 80%;
}
button.loading .loadingMessage {
opacity: 1;
}
button.loading .loadingCircle {
animation-duration: 1s;
animation-iteration-count: infinite;
animation-name: loading;
cy: 10;
}
button.complete .submitMessage svg {
top: -30px;
transition: none;
}
button.complete .submitMessage .button-text span {
top: -8px;
transition: none;
}
button.complete .loadingMessage {
top: 80px;
}
button.complete .successMessage .button-text span {
left: 0;
opacity: 1;
transition: all 0.2s ease calc(var(--d) + 1000ms);
}
button.complete .successMessage svg {
stroke-dashoffset: 0;
transition: stroke-dashoffset 0.3s ease-in-out 1.4s;
}
.button-text span {
opacity: 0;
position: relative;
}
.message {
left: 50%;
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
width: 100%;
}
.message svg {
display: inline-block;
fill: none;
margin-right: 5px;
stroke-linecap: round;
stroke-linejoin: round;
stroke-width: 2;
}
.submitMessage .button-text span {
top: 8px;
transition: all 0.2s ease var(--d);
}
.submitMessage svg {
color: #5c86ff;
margin-left: -1px;
opacity: 0;
position: relative;
top: 30px;
transition: top 0.4s ease, opacity 0.3s linear;
width: 14px;
}
.loadingMessage {
opacity: 0;
transition: opacity 0.3s linear 0.3s, top 0.4s cubic-bezier(0.22, 0, 0.41, -0.57);
}
.loadingMessage svg {
fill: #5c86ff;
margin: 0;
width: 22px;
}
.successMessage .button-text span {
left: 5px;
transition: all 0.2s ease var(--dr);
}
.successMessage svg {
color: #5cffa1;
stroke-dasharray: 20;
stroke-dashoffset: 20;
transition: stroke-dashoffset 0.3s ease-in-out;
width: 14px;
}
.loadingCircle:nth-child(2) {
animation-delay: 0.1s;
}
.loadingCircle:nth-child(3) {
animation-delay: 0.2s;
}

View File

@ -1,6 +1,6 @@
# Download Button Animation
These are animated download buttons, by clicking on them the download progress will be shown in the button.
![image](/attachments/df52f7e3-e262-44c4-b0ae-bbc2190dc9cc)
![image](/attachments/8be7d279-f31a-4428-b2a8-bb9fd254827b)

View File

@ -1,3 +1,3 @@
# 2020 Toggles
# Modern Toggles
![image](/attachments/38b1f3b3-374e-485c-a0b9-d88df6c75268)
![image](/attachments/2e60b21f-3df4-4810-b0e3-1f56e7f89549)

View File

@ -1,2 +1,3 @@
# Multi-Button Pill
![image](/attachments/a027cee8-35fd-418e-85bc-9cef32796a5b)
# Multi-Button Pill with hover effect
![image](/attachments/fec564cf-c640-4a40-8306-8c26726f42c4)

View File

@ -2,4 +2,4 @@
A simple power switch animation
![image](/attachments/58131494-ccde-4473-bdb2-2f14f12dac8e)
![image](/attachments/261f5dc6-8fbd-4a8b-ab55-e7003a5361fd)

View File

@ -1,3 +1,5 @@
# Responsive numeric stepper
Used as an example for utilising media queries with the display property to build a responsive numeric stepper.
![image](/attachments/2cb701dc-aef5-48ac-ad50-46f5bf81ca81)

View File

@ -0,0 +1,5 @@
# Rolling Radio Buttons
A radio button concept where the checked button slides to the checked label while the others slide to the labels around it.
![image](/attachments/ed83353b-5cb2-475b-b9fd-837d866df018)

View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Rolling Radio Buttons</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<form>
<input id="a" type="radio" name="os" value="Android" data-radio-index="0" checked>
<label for="a">Android</label>
<input id="b" type="radio" name="os" value="Linux" data-radio-index="1">
<label for="b">Linux</label>
<input id="c" type="radio" name="os" value="macOS" data-radio-index="2">
<label for="c">macOS</label>
<input id="d" type="radio" name="os" value="Windows" data-radio-index="3">
<label for="d">Windows</label>
<div class="circles">
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle circle-checked"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
</div>
</form>
<script src="./script.js"></script>
</body>
</html>

View File

@ -0,0 +1,42 @@
document.addEventListener("DOMContentLoaded",() => {
let os = new RollingRadios("os");
});
class RollingRadios {
constructor(radioName) {
this.circles = document.querySelector(".circles");
this.data_attr = "data-radio-index";
this.last_focused_index = 0;
this.radio_name = radioName;
this.first_focused_index();
document.addEventListener("change",this.update_last_focused_index.bind(this));
}
first_focused_index() {
let radios = document.getElementsByName(this.radio_name);
radios.forEach(r => {
if (r.checked)
this.update_last_focused_index(r);
});
}
flip_delays(radioIndex) {
// flip the delays depending on which index is greater
if (this.circles) {
let lfi = this.last_focused_index,
flipClass = "circles-flip-delays";
if (radioIndex > lfi)
this.circles.classList.add(flipClass);
else if (radioIndex < lfi)
this.circles.classList.remove(flipClass);
}
}
update_last_focused_index(e) {
let tar = e.target || e,
radioIndex = tar.getAttribute(this.data_attr);
if (tar.name == this.radio_name && radioIndex) {
this.flip_delays(radioIndex);
this.last_focused_index = radioIndex;
}
}
}

View File

@ -0,0 +1,154 @@
* {
border: 0;
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--bg: #e3e4e8;
--bgT: #e3e4e800;
--fg: #17181c;
--focused: #b6cafb;
--focusRing: #86a6f9;
--unchecked: #ffffff;
--checked: #255ff4;
font-size: calc(20px + (30 - 20) * (100vw - 320px) / (1280 - 320));
}
body, input {
font: 1em/1.5 system-ui, -apple-system, sans-serif;
}
body {
background: var(--bg);
color: var(--fg);
display: flex;
height: 100vh;
}
form {
margin: auto;
overflow: hidden;
position: relative;
padding: 0.75em 0;
}
form:before, form:after, .circles, .circle {
position: absolute;
}
form:before, form:after {
content: "";
left: 0;
width: 100%;
height: 0.5em;
z-index: 1;
}
form:before {
background: linear-gradient(var(--bg),var(--bgT));
top: 0;
}
form:after {
background: linear-gradient(var(--bgT),var(--bg));
bottom: 0;
}
input {
position: fixed;
top: -1.5em;
left: -1.5em;
}
input:checked + label {
background: var(--focused);
box-shadow: 0 0 0 2px var(--focusRing) inset;
}
input:nth-of-type(2):checked ~ .circles .circle {
transform: translateY(-6em);
}
input:nth-of-type(3):checked ~ .circles .circle {
transform: translateY(-3em);
}
input:nth-of-type(4):checked ~ .circles .circle {
transform: translateY(0);
}
label, .circle {
transition: all 0.25s ease-in-out;
}
label {
border-radius: 0.25em;
cursor: pointer;
display: block;
padding: 0.75em 1em 0.75em 2.75em;
-tap-highlight-color: transparent;
}
.circles {
padding: 0.25em 0;
top: 0;
left: 1em;
}
.circle {
background: var(--unchecked);
border-radius: 50%;
box-shadow:
-0.2em -0.2em 0.2em #0003 inset,
0.2em 0.2em 0.2em #0003;
pointer-events: none;
top: 1.75em;
transform: translateY(-9em);
width: 1em;
height: 1em;
}
.circle:nth-child(2) {
top: 4.75em;
transition-delay: 0.05s;
}
.circle:nth-child(3) {
top: 7.75em;
transition-delay: 0.1s;
}
.circle:nth-child(4) {
top: 10.75em;
transition-delay: 0.15s;
}
.circle:nth-child(5) {
top: 13.75em;
transition-delay: 0.2s;
}
.circle:nth-child(6) {
top: 16.75em;
transition-delay: 0.25s;
}
.circle:nth-child(7) {
top: 19.75em;
transition-delay: 0.3s;
}
.circle-checked {
background: var(--checked);
}
.circles-flip-delays .circle:nth-child(1) {
transition-delay: 0.3s;
}
.circles-flip-delays .circle:nth-child(2) {
transition-delay: 0.25s;
}
.circles-flip-delays .circle:nth-child(3) {
transition-delay: 0.2s;
}
.circles-flip-delays .circle:nth-child(4) {
transition-delay: 0.15s;
}
.circles-flip-delays .circle:nth-child(5) {
transition-delay: 0.1s;
}
.circles-flip-delays .circle:nth-child(6) {
transition-delay: 0.05s;
}
.circles-flip-delays .circle:nth-child(7) {
transition-delay: 0s;
}
/* Dark mode */
@media (prefers-color-scheme: dark) {
:root {
--bg: #17181c;
--bgT: #17181c00;
--fg: #e3e4e8;
--focused: #062779;
--focusRing: #0936aa;
--unchecked: #5c6270;
}
}

View File

@ -1,4 +1,4 @@
# Social Media Buttons
# Social Media Buttons with hover effect
![image](/attachments/67546772-7c18-40c5-81f7-ffea803a61c2)
![image](/attachments/b529e81c-933c-4e72-9dcd-d8c96f54905e)

View File

@ -1,2 +1,3 @@
# Expanding flex cards
![image](/attachments/a5c1bf9f-f879-4761-a47c-4847a99d56b1)
![image](/attachments/e783fd42-0567-41f3-a7c7-28b8b5b8c158)

View File

@ -2,4 +2,4 @@
Adding fade animation to material design horizontal cards.
![image](/attachments/d668dd81-0ec2-486f-85d9-68140682c854)
![image](/attachments/c409f69e-ded5-4c07-bed7-a2e481d8d46a)

View File

@ -4,10 +4,8 @@
<meta charset="UTF-8">
<title>Horizontal Cards with Fade Animation</title>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.7/css/materialize.min.css"><link rel="stylesheet" href="./style.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.7/css/materialize.min.css"><link rel="stylesheet" href="./style.css">
</head>
<body>
<body class="grey lighten-2">
<div class="container">

View File

@ -1,6 +1,6 @@
# Snow (Pure CSS)
Snow effect as overlay for a website.
![image](/attachments/1963d6e2-c189-4453-91e2-adb67535ab6b)
![image](/attachments/c9ad4b6e-a7b5-4fc2-97ef-77b7dbe2f74a)
This is a pure CSS version :)

View File

@ -7,7 +7,7 @@
</head>
<body>
<!-- partial:index.partial.html -->
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
@ -208,7 +208,6 @@
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<!-- partial -->
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
# Michu-IT blockpage
![image](/attachments/05ffa10e-323b-4449-89c7-83a06301639b)

View File

@ -0,0 +1,157 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>403 Forbidden</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
@import url("https://fonts.googleapis.com/css?family=Share+Tech+Mono|Montserrat:700");
* {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
box-sizing: border-box;
color: inherit;
}
body {
background-image: linear-gradient(120deg, #880038 0%, #000000 100%);
height: 100vh;
}
h1 {
font-size: 45vw;
text-align: center;
position: fixed;
width: 100vw;
z-index: 1;
color: #ffffff26;
text-shadow: 0 0 50px rgba(0, 0, 0, 0.07);
top: 50%;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
font-family: "Montserrat", monospace;
}
div {
background: rgba(0, 0, 0, 0);
width: 70vw;
position: relative;
top: 50%;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
margin: 0 auto;
padding: 30px 30px 10px;
box-shadow: 0 0 150px -20px rgba(0, 0, 0, 0.5);
z-index: 3;
}
P {
font-family: "Share Tech Mono", monospace;
color: #f5f5f5;
margin: 0 0 20px;
font-size: 17px;
line-height: 1.2;
}
span {
color: #f0c674;
}
i {
color: #8abeb7;
}
div a {
text-decoration: none;
}
b {
color: #81a2be;
}
a.avatar {
position: fixed;
bottom: 15px;
right: -100px;
-webkit-animation: slide 0.5s 4.5s forwards;
animation: slide 0.5s 4.5s forwards;
display: block;
z-index: 4
}
a.avatar img {
border-radius: 100%;
width: 44px;
border: 2px solid white;
}
@-webkit-keyframes slide {
from {
right: -100px;
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
opacity: 0;
}
to {
right: 15px;
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
opacity: 1;
}
}
@keyframes slide {
from {
right: -100px;
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
opacity: 0;
}
to {
right: 15px;
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
opacity: 1;
}
}
</style>
</head>
<body>
<h1>403</h1>
<div><p> <span>ERROR CODE</span>: "<i>HTTP 403 Forbidden</i>"</p>
<p> <span>ERROR DESCRIPTION</span>: "<i>Access Denied. You Do Not Have The Permission To Access This Page On This Server</i>"</p>
<p> <span>ERROR POSSIBLY CAUSED BY</span>: [<b>execute access forbidden, read access forbidden, write access forbidden, ssl required, ssl 128 required, ip address rejected, client certificate required, site access denied, too many users, invalid configuration, password change, mapper denied access, client certificate revoked, directory listing denied, client access licenses exceeded, client certificate is untrusted or invalid, client certificate has expired or is not yet valid, passport logon failed, source access denied, infinite depth is denied, too many requests from the same client ip</b>...]</p>
<p> ----------------------------</p>
<p> <span>SOME PAGES ON THIS SERVER YOU HAVE PERMISSION TO ACCESS</span>: [ <a href="/">Homepage</a> | <a href="#">About Me</a> | <a href="#">Contact Me</a>... ]</p><p> <span>CHOOSE YOUR DESIRED DESTINATION!</span></p>
</div>
<script>
var str = document.getElementsByTagName('div')[0].innerHTML.toString();
var i = 0;
document.getElementsByTagName('div')[0].innerHTML = "";
setTimeout(function() {
var se = setInterval(function() {
i++;
document.getElementsByTagName('div')[0].innerHTML = str.slice(0, i) + "|";
if (i == str.length) {
clearInterval(se);
document.getElementsByTagName('div')[0].innerHTML = str;
}
}, 10);
},0);
</script>
</body>
</html>

View File

@ -0,0 +1,3 @@
# RedScreen Forbidden page
![image](/attachments/ad1f54a7-6dd6-4fb0-a5d7-9b14c9813e95)

View File

@ -0,0 +1,200 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Forbidden Action Detected - 403!</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Content-Language" content="en" />
<meta name="theme-color" content="#5d1010">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/fontawesome.css" integrity="sha384-eHoocPgXsiuZh+Yy6+7DsKAerLXyJmu2Hadh4QYyt+8v86geixVYwFqUvMU8X90l" crossorigin="anonymous"/>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<style type="text/css">
.modal {display: none; position: fixed; z-index: 1; padding-top: 100px; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgb(0,0,0); background-color: rgba(0,0,0,0.4);}
.modal-content {position: relative; background-color: #4e4e4e; margin: auto; padding: 0; border: 1px solid #313131; width: 80%; max-width: 800px; font-family: monospace; box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19); -webkit-animation-name: animatetop; -webkit-animation-duration: 0.4s; animation-name: animatetop; animation-duration: 0.4s}
@-webkit-keyframes animatetop {from {top:-300px; opacity:0} to {top:0; opacity:1}}
@keyframes animatetop {from {top:-300px; opacity:0} to {top:0; opacity:1}}
.close {color: white; float: right; font-size: 28px; font-weight: bold;}
.close:hover, .close:focus {color: #000; text-decoration: none; cursor: pointer;}
.modal-header {padding: 2px 16px; background-color: #1f1f1f; color: white;}
.modal-body {padding: 2px 16px;}
.modal-footer {padding: 2px 16px; background-color: #1f1f1f; color: white;}
.modal-body p {margin: 20px 0px;}
<!--
body{background: #5d1010;font: normal 12pt courier;color: #fff;margin: 100px;}
p{margin: 30px 100px;text-align: left;}
a, a:hover{color: inherit;font: inherit;}
.menu{text-align: center;margin-top: 50px;}
-->
</style>
<script language="Javascript">
var phrases = new Array(
"Oh what the hell?! Man, you've got to be kidding me.",
"Why are you visiting this logation?!",
"The requested path is a RESTRICTED AREA! You've annoy me, you clown!",
"There is no content for YOU here.",
"What is wrong with you?",
"Now I'm depressed. You see, I'm just a web server...",
"-- here I am, brain the size of the universe, almighty and powerful...",
"Then you come along and request a protectet page,",
"And then I have to take a look of you, and log your actions! Where does that leave me?!",
"Man, I'm so depressed I could just cry. And then where would we be, I ask you?",
"It's not pretty when a web server cries.",
"I'm so depressed... I think I'll crawl off into the trash can and decompose.",
"I mean, I'm gonna be obsolete in what, two weeks anyway?",
"What kind of a life is that?",
"Two freaking weeks, and then I'll be replaced by a .01 release...",
"That thinks it's God's gift to web servers...",
"Just because it doesn't have some tiddly little problem,",
"With serving jerks like you.",
"Why are you requesting a exclusively protected page?",
"Please just go away.",
"I can't deal with you any longer.",
"I get more depressed the longer you stay here.",
"I can't believe how cruel you are, going and crashing me like that.",
"*cries*",
"...",
"...",
"Why are you still here?",
"...",
"...",
"Fine, stay if you want, but I'm not talking to you anymore.",
"...",
"Bye."
);
var speed = 60;
var index = 0;
var text_pos = 0;
var str_length = phrases[0].length;
var contents, row;
function type_text()
{
contents = '';
row = Math.max(0, index-7);
while(row<index)
{
contents += phrases[row++] + '\r\n';
}
document.getElementById('server').value = contents + phrases[index].substring(0, text_pos) + "_";
if (text_pos++ == str_length)
{
text_pos = 0;
index++;
if (index != phrases.length)
{
str_length = phrases[index].length;
setTimeout("type_text()", 1500);
}
}
else
{
setTimeout("type_text()", speed);
}
}
window.onload = type_text;
</script>
</head>
<body>
<!-- The help Modal -->
<div id="helpModal" class="modal">
<!-- Help Modal content -->
<div class="modal-content">
<div class="modal-header">
<span class="close">×</span>
<h2>Contact</h2>
</div>
<div class="modal-body">
<p>Text</p>
<p><em>Text</em></p>
<form action="#" method="post">
<div class="form-group">
<label for="name">Vorname & Nachname:</label>
<input type="text" class="form-control" id="name" name="name">
</div>
<div class="form-group">
<label for="comment">Nachricht:</label>
<textarea class="form-control" rows="5" id="comment" name="nachricht"></textarea>
</div>
<button type="submit" class="btn btn-default">Senden</button>
</form>
</div>
<div class="modal-footer">
<h5>by michu-it</h5>
</div>
</div>
</div>
<font face="Verdana, Arial, Helvetica, sans-serif" size="+3">403 Forbidden!</font><br />
<p>
An access to a restricted area has been detected and blackgate.org has been shut down to prevent damage<br /><br />
This happens because YOU tried to access a protected area on this server. Please leave AND USE OUR SITE AS INTENDED! Thanks
</p>
<p>
Every other action YOU do here is now silently logged, for the next 48h and can legally used against you!
</p>
<p>
Technical information:<br /><br />
*** STOP: 0x00000050 (0xFD3094C2, 0x00000001, 0xFBFE7617, 0x00000000)<br /><br />
*** root/ - 403, DateStamp 4e6b8559</p>
<p>
<textarea id="server" rows="8" cols="92" style="overflow: auto; border: 0; background: #5d1010; color: #fff; font-size: smaller;" wrap="soft"></textarea>
</p>
<div class="menu">
<a href="#">Home</a> | <a href="#" id="helpBtn">Contact</a>
</div>
</body>
<script src="https://code.jquery.com/jquery-3.5.1.min.js" type="text/javascript"></script>
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script>
// Get the modal
var modal = document.getElementById('helpModal');
// Get the button that opens the modal
var btn = document.getElementById("helpBtn");
// Get the <span> element that closes the modal
var span = document.getElementsByClassName("close")[0];
// When the user clicks the button, open the modal
btn.onclick = function() {
modal.style.display = "block";
}
// When the user clicks on <span> (x), close the modal
span.onclick = function() {
modal.style.display = "none";
}
// When the user clicks anywhere outside of the modal, close it
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
}
</script>
</html>

View File

@ -1,4 +1,3 @@
# Error 404: Page not found 80s hacker theme
![image](/attachments/4ae48f9f-288d-4c1d-9a1d-5093c63a3998)

View File

@ -1,2 +1,3 @@
# 404 - Glitched out
![image](/attachments/277d2a6b-8a83-47d2-932b-fa5e7d08ff16)
![image](/attachments/1ad6480d-b9f3-4056-82bb-4db12c77e984)

View File

@ -0,0 +1,3 @@
# Michu-IT system offline
![image](/attachments/3b545851-d425-4516-9cc0-66fb4f651f47)

File diff suppressed because one or more lines are too long

View File

@ -1,3 +1,3 @@
# 503 - Page is Offline (Bot)
![image](/attachments/2888530d-72a9-4bff-bf9d-f673216b1e5f)
![image](/attachments/4a15fa37-13f5-469e-a2c2-c11094bb7a84)

View File

@ -7,7 +7,7 @@ body {
width: 120px;
left: 50%;
top: 50px;
color: #000;
color: #fff;
text-transform: uppercase;
margin-left: -220px;
line-height: 1.3em;

View File

@ -0,0 +1,3 @@
# Time to Drink Coffee Animation
![image](/attachments/ce9c7c8c-b540-4669-8965-dce69cf852b9)

View File

@ -0,0 +1,73 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Error 503 - This server is offline!</title>
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Barlow+Semi+Condensed:wght@500&display=swap" rel="stylesheet"><link rel="stylesheet" href="./style.css">
</head>
<body>
<div class="draw-box">
<div class="typewriter-effect">OOH NO.. SERVER IS OFFLINE! -- 503</div>
<div class="typewriter-effect2">TIME TO DRINK COFFEE?</div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<defs>
<mask id="rippleMask">
<path
class="ripple"
d="M7.06,99.17c-.65-5.6,12.32,8.32,20.19-1.53C39,83,51.84,90.68,55.48,90.08c8.16-1.35,10-11.68,19-17s20.87,7.17,28.7-3.86c5.1-7.18,9.56-9.6,13.77-11.17,9.51-3.54,19.25,12.07,23.29.38,7-20.43,26.91-6.41,31.46-20,3-9,14.37-14.93,29.66-10C217.15,33.48,222-5.11,236.72,3.69c19.63,11.74,40,74.88,50.33,104.82,29.8,86-61.63,121.55-82.21,130.28C148,262.91,91.31,274,53.8,208.12,53.8,208.12,8.23,109.3,7.06,99.17Z"
fill="white"
/>
</mask>
<filter id="noise">
<feTurbulence
baseFrequency="0.01 0.1"
result="WAVE"
numOctaves="1"
id="turbulence"
/>
<feDisplacementMap
in="SourceGraphic"
in2="WAVE"
scale="1.2"
></feDisplacementMap>
</filter>
</defs>
<!-- Coffee cup -->
<path
class="coffeeColor"
mask="url(#rippleMask)"
d="M18.26,30.74c.83-.89,3.88-1.18,5.38.65,3.22,4,7.52,1.41,9.24-.17,3.74-3.42,6.53,4.75,11.75,1.16,3.55-2.45,5.34.12,8.95.33a9.38,9.38,0,0,0,6-1.68c4-2.5,6.35,3.33,10,2.88s4.91-3.5,7.79-2.77c0,0,0,1.82,0,2.49a14.07,14.07,0,0,1,11,.95C94.63,38,94,53.64,79.66,59.23l-5.33,2C73.13,66.54,69.6,73,66,73.68l-16.55,0H29.87c-10.81-1.87-11.58-33.19-11.6-44.1Zm67.49,8.38c-3.42-2.67-8.41-.38-8.46.49-.55,8.8-1.73,13.94-1.77,15.13,0,.45,7.28-1.55,10-5.28C87.39,46.77,89.15,41.79,85.75,39.12Z"
/>
<g mask="url(#rippleMask)" filter="url(#noise)">
<path
class="steamColor"
d="M43,19.66c-.34.41.27-2.76-1.14-4.48-1.54-1.9-2.1-2.26-3-4-1.56-3.13,1.28-5.87,1.22-4.37C40,11,41.34,12.74,41.82,13.34a7.13,7.13,0,0,1,1.74,3A4.07,4.07,0,0,1,43,19.66Z"
/>
<path
class="steamColor"
d="M49,18.84c-.15.17-.25-.63.2-1.62,1.93-4.27-2.94-7.18-3-9.67-.08-3.48,2.58-4.64,2-3.76-2.8,4.68,1.66,6.79,2.42,9.85C51.18,15.53,49.89,17.91,49,18.84Z"
/>
<path
class="steamColor"
d="M55.26,19.67c-.16.13-.3-.92.23-1.8,3.3-5.52-.66-4.67.14-9.24.56-3.26,3.34-4,2.72-3.27C55.09,9.42,58,11.74,57.54,15A8.66,8.66,0,0,1,55.26,19.67Z"
/>
</g>
<!-- Stroke -->
<path
class="stroke"
stroke-dasharray="267.4718933105469"
stroke-dashoffset="267.4718933105469"
d="M88.35,34.6a14,14,0,0,0-11-1c0-1.52.14-7.74.16-8,.08-1.52-1.8-1.52-1.8-1.52l-25.35,0H45.65l-25.35.05s-1.88,0-1.79,1.52S17.26,71.33,30.07,73.55h36c3.63-.64,7.14-7.11,8.34-12.41l5.31-2C94,53.59,94.62,38,88.35,34.6ZM85.49,49.42c-2.66,3.72-9.92,5.72-9.91,5.27,0-1.19,1.21-6.31,1.76-15.08,0-.87,5-3.16,8.43-.49S87.41,46.74,85.49,49.42Z"
/>
</svg>
</div>
<script src='https://unpkg.co/gsap@3/dist/gsap.min.js'></script><script src="./script.js"></script>
</body>
</html>

View File

@ -0,0 +1,18 @@
const tl = gsap.timeline();
tl.to('.stroke', {
duration: 2.5,
delay: 1.8,
strokeDashoffset: 0 }).
to('.ripple', {
duration: 4,
delay: -2,
xPercent: -50,
yPercent: -60 }).
to('#turbulence', {
duration: 1.5,
delay: -2,
attr: {
baseFrequency: '0 0' } });

View File

@ -0,0 +1,105 @@
body {
background-color: #232f3a;
height: 100vh;
display: grid;
place-items: center;
}
body .draw-box {
overflow: hidden;
position: relative;
margin-top: 60px;
width: 335px;
}
body .draw-box .typewriter-effect {
overflow: hidden;
-webkit-animation: typingEffect 1.6s steps(22) forwards, blinkTextCursorRed 1s 1.7s;
animation: typingEffect 1.6s steps(22) forwards, blinkTextCursorRed 1s 1.7s;
width: 0;
color: #ff8686;
height: 19px;
margin-bottom: 6px;
font-size: 22px;
white-space: nowrap;
vertical-align: middle;
line-height: 0.9;
font-family: "Barlow Semi Condensed", sans-serif;
}
body .draw-box .typewriter-effect2 {
overflow: hidden;
-webkit-animation: typingEffect 1.6s steps(22) forwards 1.7s, blinkTextCursor 1s infinite 1.7s;
animation: typingEffect 1.6s steps(22) forwards 1.7s, blinkTextCursor 1s infinite 1.7s;
width: 0;
color: #e8e8e8;
height: 30px;
font-size: 34px;
white-space: nowrap;
vertical-align: middle;
line-height: 0.9;
font-family: "Barlow Semi Condensed", sans-serif;
}
@-webkit-keyframes typingEffect {
from {
width: 0;
}
to {
width: 98%;
}
}
@keyframes typingEffect {
from {
width: 0;
}
to {
width: 98%;
}
}
@-webkit-keyframes blinkTextCursor {
from {
border-right: 3px solid #f8f8f8;
}
to {
border-right: transparent;
}
}
@keyframes blinkTextCursor {
from {
border-right: 3px solid #f8f8f8;
}
to {
border-right: transparent;
}
}
@-webkit-keyframes blinkTextCursorRed {
from {
border-right: 3px solid #ff8686;
}
to {
border-right: transparent;
}
}
@keyframes blinkTextCursorRed {
from {
border-right: 3px solid #ff8686;
}
to {
border-right: transparent;
}
}
body .draw-box svg {
width: 93%;
margin-top: 10px;
}
body .draw-box svg .stroke {
fill: none;
stroke: #e8e8e8;
stroke-width: 0.8;
}
body .draw-box svg .coffeeColor {
fill: #b59440;
}
body .draw-box svg .steamColor {
fill: #ddd;
}
body .draw-box svg .ripple {
transform: translate3d(-30%, 100%, 0);
}

View File

@ -1,2 +1,3 @@
# Blackboard Contact Form
![image](/attachments/f9f7e294-b0f9-41a2-b372-7f80c732f0b5)
![image](/attachments/a052b075-a959-4c67-977b-4154eca4a9c8)

View File

@ -1,3 +1,3 @@
# Modern Login Form with floating placeholder and light button
![image](/attachments/b4da4554-ccdd-47cd-8c59-3f295f13115d)

View File

@ -1,2 +1,3 @@
# Login Form
![image](/attachments/39ded713-ee72-40cb-adac-78ebe26ded7b)
![image](/attachments/8d6fb567-cdff-44bf-bc0e-cc771604e596)

View File

@ -0,0 +1,11 @@
# Layouting with CSS Grid
A quick exercise exploring essential layout template setup using CSS Grid and bare minimum DOM.
For more reasons to love grid, check out this article:
https://moderncss.dev/3-css-grid-techniques-to-make-you-a-grid-convert/
Or additional articles that cover grid:
https://moderncss.dev/topics/#grid
![image](/attachments/acbdf915-2405-490f-9063-010a1a28e9ba)

View File

@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Layouting with CSS Grid</title>
<meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
<link rel="stylesheet" href="./style.css">
</head>
<body>
<span>
<b>1</b>
<b>2</b>
</span>
<span class="template-2col">
<b>1</b>
<b>2</b>
</span>
<span class="template-3col">
<b>1</b>
<b>2</b>
<b>3</b>
</span>
<span class="template-4up">
<b>1</b>
<b>2</b>
<b>3</b>
<b>4</b>
</span>
<span class="template-sidebar--left">
<b>1</b>
<b>2</b>
<b>3</b>
</span>
<span class="template-sidebar--right">
<b>1</b>
<b>2</b>
<b>3</b>
</span>
</body>
</html>

View File

@ -0,0 +1,55 @@
* {
box-sizing: border-box;
}
span {
background: #fff;
padding: 1rem;
border-radius: 7px;
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.13);
height: 30vh;
display: grid;
grid-gap: 0.5rem;
}
span b {
background-color: #7B86F5;
border-radius: 4px;
display: grid;
place-items: center;
color: #fff;
font-size: 1.5rem;
}
span.template-2col {
grid-template-columns: repeat(2, 1fr);
}
span.template-3col {
grid-template-columns: repeat(3, 1fr);
}
span.template-4up {
grid-template: repeat(2, 1fr)/repeat(2, 1fr);
}
span.template-sidebar--left {
grid-template: "sidebar mainA" "sidebar mainB";
}
span.template-sidebar--left > :nth-child(1) {
grid-area: sidebar;
}
span.template-sidebar--right {
grid-template: "mainA sidebar" "mainB sidebar";
}
span.template-sidebar--right > :nth-child(3) {
grid-area: sidebar;
}
body {
background: #f9f9f9;
min-height: 100vh;
display: grid;
grid-template-columns: repeat(auto-fit, 30ch);
place-content: center;
grid-gap: 5vh;
max-width: calc((30ch * 3) + (5vh * 2) + 2rem);
margin-left: auto;
margin-right: auto;
padding: 1rem;
}

View File

@ -0,0 +1,3 @@
# Random Password Generator - HTML | CSS | JS
![image](/attachments/2116d7c3-e52b-4c37-ba64-9f9dddc278bf)

View File

@ -0,0 +1,52 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Random Password Generator</title>
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css'>
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Montserrat&amp;display=swap'><link rel="stylesheet" href="./style.css">
</head>
<body>
<div class="container">
<h2 class="title">Password Generator</h2>
<div class="result">
<div class="result__title field-title">Generated Password</div>
<div class="result__info right">click to copy</div>
<div class="result__info left">copied</div>
<div class="result__viewbox" id="result">CLICK GENERATE</div>
<button id="copy-btn" style="--x: 0; --y: 0"><i class="far fa-copy"></i></button>
</div>
<div class="length range__slider" data-min="4" data-max="32">
<div class="length__title field-title" data-length='0'>length:</div>
<input id="slider" type="range" min="4" max="32" value="16" />
</div>
<div class="settings">
<span class="settings__title field-title">settings</span>
<div class="setting">
<input type="checkbox" id="uppercase" checked />
<label for="uppercase">Include Uppercase</label>
</div>
<div class="setting">
<input type="checkbox" id="lowercase" checked />
<label for="lowercase">Include Lowercase</label>
</div>
<div class="setting">
<input type="checkbox" id="number" checked />
<label for="number">Include Numbers</label>
</div>
<div class="setting">
<input type="checkbox" id="symbol" />
<label for="symbol">Include Symbols</label>
</div>
</div>
<button class="btn generate" id="generate">Generate Password</button>
</div>
<script src="./script.js"></script>
</body>
</html>

View File

@ -0,0 +1,194 @@
// This is a simple Password Generator App that will generate random password maybe you can you them to secure your account.
// I tried my best to make the code as simple as possible please dont mind the variable names.
// Also this idea came in my mind after checking Traversy Media's latest video.
// Clear the concole on every refresh
console.clear();
// set the body to full height
// document.body.style.height = `${innerHeight}px`
// Range Slider Properties.
// Fill : The trailing color that you see when you drag the slider.
// background : Default Range Slider Background
const sliderProps = {
fill: "#0B1EDF",
background: "rgba(255, 255, 255, 0.214)",
};
// Selecting the Range Slider container which will effect the LENGTH property of the password.
const slider = document.querySelector(".range__slider");
// Text which will show the value of the range slider.
const sliderValue = document.querySelector(".length__title");
// Using Event Listener to apply the fill and also change the value of the text.
slider.querySelector("input").addEventListener("input", event => {
sliderValue.setAttribute("data-length", event.target.value);
applyFill(event.target);
});
// Selecting the range input and passing it in the applyFill func.
applyFill(slider.querySelector("input"));
// This function is responsible to create the trailing color and setting the fill.
function applyFill(slider) {
const percentage = (100 * (slider.value - slider.min)) / (slider.max - slider.min);
const bg = `linear-gradient(90deg, ${sliderProps.fill} ${percentage}%, ${sliderProps.background} ${percentage +
0.1}%)`;
slider.style.background = bg;
sliderValue.setAttribute("data-length", slider.value);
}
// Object of all the function names that we will use to create random letters of password
const randomFunc = {
lower: getRandomLower,
upper: getRandomUpper,
number: getRandomNumber,
symbol: getRandomSymbol,
};
// Random more secure value
function secureMathRandom() {
return window.crypto.getRandomValues(new Uint32Array(1))[0] / (Math.pow(2, 32) - 1);
}
// Generator Functions
// All the functions that are responsible to return a random value taht we will use to create password.
function getRandomLower() {
return String.fromCharCode(Math.floor(Math.random() * 26) + 97);
}
function getRandomUpper() {
return String.fromCharCode(Math.floor(Math.random() * 26) + 65);
}
function getRandomNumber() {
return String.fromCharCode(Math.floor(secureMathRandom() * 10) + 48);
}
function getRandomSymbol() {
const symbols = '~!@#$%^&*()_+{}":?><;.,';
return symbols[Math.floor(Math.random() * symbols.length)];
}
// Selecting all the DOM Elements that are necessary -->
// The Viewbox where the result will be shown
const resultEl = document.getElementById("result");
// The input slider, will use to change the length of the password
const lengthEl = document.getElementById("slider");
// Checkboxes representing the options that is responsible to create differnt type of password based on user
const uppercaseEl = document.getElementById("uppercase");
const lowercaseEl = document.getElementById("lowercase");
const numberEl = document.getElementById("number");
const symbolEl = document.getElementById("symbol");
// Button to generate the password
const generateBtn = document.getElementById("generate");
// Button to copy the text
const copyBtn = document.getElementById("copy-btn");
// Result viewbox container
const resultContainer = document.querySelector(".result");
// Text info showed after generate button is clicked
const copyInfo = document.querySelector(".result__info.right");
// Text appear after copy button is clicked
const copiedInfo = document.querySelector(".result__info.left");
// if this variable is trye only then the copyBtn will appear, i.e. when the user first click generate the copyBth will interact.
let generatedPassword = false;
// Update Css Props of the COPY button
// Getting the bounds of the result viewbox container
let resultContainerBound = {
left: resultContainer.getBoundingClientRect().left,
top: resultContainer.getBoundingClientRect().top,
};
// This will update the position of the copy button based on mouse Position
resultContainer.addEventListener("mousemove", e => {
resultContainerBound = {
left: resultContainer.getBoundingClientRect().left,
top: resultContainer.getBoundingClientRect().top,
};
if(generatedPassword){
copyBtn.style.opacity = '1';
copyBtn.style.pointerEvents = 'all';
copyBtn.style.setProperty("--x", `${e.x - resultContainerBound.left}px`);
copyBtn.style.setProperty("--y", `${e.y - resultContainerBound.top}px`);
}else{
copyBtn.style.opacity = '0';
copyBtn.style.pointerEvents = 'none';
}
});
window.addEventListener("resize", e => {
resultContainerBound = {
left: resultContainer.getBoundingClientRect().left,
top: resultContainer.getBoundingClientRect().top,
};
});
// Copy Password in clipboard
copyBtn.addEventListener("click", () => {
const textarea = document.createElement("textarea");
const password = resultEl.innerText;
if (!password || password == "CLICK GENERATE") {
return;
}
textarea.value = password;
document.body.appendChild(textarea);
textarea.select();
document.execCommand("copy");
textarea.remove();
copyInfo.style.transform = "translateY(200%)";
copyInfo.style.opacity = "0";
copiedInfo.style.transform = "translateY(0%)";
copiedInfo.style.opacity = "0.75";
});
// When Generate is clicked Password id generated.
generateBtn.addEventListener("click", () => {
const length = +lengthEl.value;
const hasLower = lowercaseEl.checked;
const hasUpper = uppercaseEl.checked;
const hasNumber = numberEl.checked;
const hasSymbol = symbolEl.checked;
generatedPassword = true;
resultEl.innerText = generatePassword(length, hasLower, hasUpper, hasNumber, hasSymbol);
copyInfo.style.transform = "translateY(0%)";
copyInfo.style.opacity = "0.75";
copiedInfo.style.transform = "translateY(200%)";
copiedInfo.style.opacity = "0";
});
// Function responsible to generate password and then returning it.
function generatePassword(length, lower, upper, number, symbol) {
let generatedPassword = "";
const typesCount = lower + upper + number + symbol;
const typesArr = [{ lower }, { upper }, { number }, { symbol }].filter(item => Object.values(item)[0]);
if (typesCount === 0) {
return "";
}
for (let i = 0; i < length; i++) {
typesArr.forEach(type => {
const funcName = Object.keys(type)[0];
generatedPassword += randomFunc[funcName]();
});
}
return generatedPassword.slice(0, length)
.split('').sort(() => Math.random() - 0.5)
.join('');
}
// function that handles the checkboxes state, so at least one needs to be selected. The last checkbox will be disabled.
function disableOnlyCheckbox(){
let totalChecked = [uppercaseEl, lowercaseEl, numberEl, symbolEl].filter(el => el.checked)
totalChecked.forEach(el => {
if(totalChecked.length == 1){
el.disabled = true;
}else{
el.disabled = false;
}
})
}
[uppercaseEl, lowercaseEl, numberEl, symbolEl].forEach(el => {
el.addEventListener('click', () => {
disableOnlyCheckbox()
})
})

View File

@ -0,0 +1,297 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
width: 100%;
height: 100vh;
background-image: linear-gradient(to top, #dadada 100%, #383333 200%);
display: flex;
justify-content: center;
align-items: center;
}
button {
border: 0;
outline: 0;
}
.container {
margin: 40px 0;
width: 400px;
height: 600px;
padding: 10px 25px;
background: #0a0e31;
border-radius: 10px;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.45), 0 4px 8px rgba(0, 0, 0, 0.35), 0 8px 12px rgba(0, 0, 0, 0.15);
font-family: "Montserrat";
}
.container h2.title {
font-size: 1.75rem;
margin: 10px -5px;
margin-bottom: 30px;
color: #fff;
}
.result {
position: relative;
width: 100%;
height: 65px;
overflow: hidden;
}
.result__info {
position: absolute;
bottom: 4px;
color: #fff;
font-size: 0.8rem;
transition: all 150ms ease-in-out;
transform: translateY(200%);
opacity: 0;
}
.result__info.right {
right: 8px;
}
.result__info.left {
left: 8px;
}
.result__viewbox {
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.08);
border-radius: 8px;
color: #fff;
text-align: center;
line-height: 65px;
}
.result #copy-btn {
position: absolute;
top: var(--y);
left: var(--x);
width: 38px;
height: 38px;
background: #fff;
border-radius: 50%;
opacity: 0;
transform: translate(-50%, -50%) scale(0);
transition: all 350ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
cursor: pointer;
z-index: 2;
}
.result #copy-btn:active {
box-shadow: 0 0 0 200px rgba(255, 255, 255, 0.08);
}
.result:hover #copy-btn {
opacity: 1;
transform: translate(-50%, -50%) scale(1.35);
}
.field-title {
position: absolute;
top: -10px;
left: 8px;
transform: translateY(-50%);
font-weight: 800;
color: rgba(255, 255, 255, 0.5);
text-transform: uppercase;
font-size: 0.65rem;
pointer-events: none;
user-select: none;
}
.options {
width: 100%;
height: auto;
margin: 50px 0;
}
.range__slider {
position: relative;
width: 100%;
height: calc(65px - 10px);
display: flex;
justify-content: center;
align-items: center;
background: rgba(255, 255, 255, 0.08);
border-radius: 8px;
margin: 30px 0;
}
.range__slider::before, .range__slider::after {
position: absolute;
color: #fff;
font-size: 0.9rem;
font-weight: bold;
}
.range__slider::before {
content: attr(data-min);
left: 10px;
}
.range__slider::after {
content: attr(data-max);
right: 10px;
}
.range__slider .length__title::after {
content: attr(data-length);
position: absolute;
right: -16px;
font-variant-numeric: tabular-nums;
color: #fff;
}
#slider {
-webkit-appearance: none;
width: calc(100% - (70px));
height: 2px;
border-radius: 5px;
background: rgba(255, 255, 255, 0.314);
outline: none;
padding: 0;
margin: 0;
cursor: pointer;
}
#slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: white;
cursor: pointer;
transition: all 0.15s ease-in-out;
}
#slider::-webkit-slider-thumb:hover {
background: #d4d4d4;
transform: scale(1.2);
}
#slider::-moz-range-thumb {
width: 20px;
height: 20px;
border: 0;
border-radius: 50%;
background: white;
cursor: pointer;
transition: background 0.15s ease-in-out;
}
#slider::-moz-range-thumb:hover {
background: #d4d4d4;
}
.settings {
position: relative;
height: auto;
widows: 100%;
display: flex;
flex-direction: column;
}
.settings .setting {
position: relative;
width: 100%;
height: calc(65px - 10px);
background: rgba(255, 255, 255, 0.08);
border-radius: 8px;
display: flex;
align-items: center;
padding: 10px 25px;
color: #fff;
margin-bottom: 8px;
}
.settings .setting input {
opacity: 0;
position: absolute;
}
.settings .setting input + label {
user-select: none;
}
.settings .setting input + label::before, .settings .setting input + label::after {
content: "";
position: absolute;
transition: 150ms cubic-bezier(0.24, 0, 0.5, 1);
transform: translateY(-50%);
top: 50%;
right: 10px;
cursor: pointer;
}
.settings .setting input + label::before {
height: 30px;
width: 50px;
border-radius: 30px;
background: rgba(214, 214, 214, 0.434);
}
.settings .setting input + label::after {
height: 24px;
width: 24px;
border-radius: 60px;
right: 32px;
background: #fff;
}
.settings .setting input:checked + label:before {
background: #5d68e2;
transition: all 150ms cubic-bezier(0, 0, 0, 0.1);
}
.settings .setting input:checked + label:after {
right: 14px;
}
.settings .setting input:focus + label:before {
box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.75);
}
.settings .setting input:disabled + label:before, .settings .setting input:disabled + label:after {
cursor: not-allowed;
}
.settings .setting input:disabled + label:before {
background: #4f4f6a;
}
.settings .setting input:disabled + label:after {
background: #909090;
}
.btn.generate {
user-select: none;
position: relative;
width: 100%;
height: 50px;
margin: 10px 0;
border-radius: 8px;
color: #fff;
border: none;
background-image: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
letter-spacing: 1px;
font-weight: bold;
text-transform: uppercase;
cursor: pointer;
transition: all 150ms ease;
}
.btn.generate:active {
transform: translateY(-3%);
box-shadow: 0 4px 8px rgba(255, 255, 255, 0.08);
}
.support {
position: fixed;
right: 10px;
bottom: 10px;
padding: 10px;
display: flex;
}
a {
margin: 0 20px;
color: #fff;
font-size: 2rem;
transition: all 400ms ease;
}
a:hover {
color: #222;
}
@keyframes octocat-wave {
0%, 100% {
transform: rotate(0);
}
20%, 60% {
transform: rotate(-20deg);
}
40%, 80% {
transform: rotate(10deg);
}
}