Update some existing projects and as well adding new
This commit is contained in:
parent
243a252e24
commit
0ef1c36a5e
@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
This repository contains a collection of carefully engineered html5 snippets, which can be used in your own projects.
|
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)
|
@ -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.
|
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.
|
The Soundcloud embed is just there for ambiance.
|
||||||
|
|
||||||
|
![image](/attachments/2c7bac61-a6a9-40bd-89a8-909d8118a38d)
|
@ -1,3 +1,5 @@
|
|||||||
# Basic Matrix Effect
|
# Basic Matrix Effect
|
||||||
|
|
||||||
Hello World in 92 Languages falling down in Matrix code rain
|
Hello World in 92 Languages falling down in Matrix code rain
|
||||||
|
|
||||||
|
![image](/attachments/0bd4f9d5-0ebf-4c8c-8100-5464f00d72d5)
|
@ -10,7 +10,8 @@
|
|||||||
|
|
||||||
<!-- Hello World in 92 Languages falling down in Matrix code rain-->
|
<!-- 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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
6
animations/floating-words/README.md
Normal file
6
animations/floating-words/README.md
Normal 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)
|
13
animations/floating-words/index.html
Normal file
13
animations/floating-words/index.html
Normal 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>
|
87
animations/floating-words/script.js
Normal file
87
animations/floating-words/script.js
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
4
animations/floating-words/style.css
Normal file
4
animations/floating-words/style.css
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
body{
|
||||||
|
background:#141414;
|
||||||
|
overflow:hidden;
|
||||||
|
}
|
3
animations/infinity-tunnel/README.md
Normal file
3
animations/infinity-tunnel/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Inifinity Tunnel
|
||||||
|
|
||||||
|
![image](/attachments/49ed7d89-9d9c-44c3-bd7d-ec29bad4a48d)
|
726
animations/infinity-tunnel/apxlmx.js
Normal file
726
animations/infinity-tunnel/apxlmx.js
Normal 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;
|
286
animations/infinity-tunnel/index.html
Normal file
286
animations/infinity-tunnel/index.html
Normal 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>
|
BIN
animations/infinity-tunnel/noise.png
Normal file
BIN
animations/infinity-tunnel/noise.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 258 KiB |
833
animations/infinity-tunnel/script.js
Normal file
833
animations/infinity-tunnel/script.js
Normal 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');
|
||||||
|
});
|
9
animations/infinity-tunnel/style.css
Normal file
9
animations/infinity-tunnel/style.css
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
body {
|
||||||
|
margin:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
height: 100vh !important;
|
||||||
|
position: fixed;
|
||||||
|
width: 100vw !important;
|
||||||
|
}
|
3
animations/modern-tunnel-vision/README.md
Normal file
3
animations/modern-tunnel-vision/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Modern Tunnel Vision
|
||||||
|
|
||||||
|
![image](/attachments/8dc80b64-8e2c-4df4-ad9c-f3ebf4f15374)
|
13
animations/modern-tunnel-vision/index.html
Normal file
13
animations/modern-tunnel-vision/index.html
Normal 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>
|
BIN
animations/modern-tunnel-vision/map5.png
Normal file
BIN
animations/modern-tunnel-vision/map5.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 480 KiB |
85
animations/modern-tunnel-vision/script.js
Normal file
85
animations/modern-tunnel-vision/script.js
Normal 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();
|
8
animations/modern-tunnel-vision/style.css
Normal file
8
animations/modern-tunnel-vision/style.css
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: black;
|
||||||
|
overflow: hidden;
|
||||||
|
height: 100%;
|
||||||
|
}
|
4
animations/time-to-drink-coffee/README.md
Normal file
4
animations/time-to-drink-coffee/README.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Time to Drink Coffee Animation
|
||||||
|
|
||||||
|
![image](/attachments/dfe21f29-12a9-484b-a194-017141825c74)
|
||||||
|
|
72
animations/time-to-drink-coffee/index.html
Normal file
72
animations/time-to-drink-coffee/index.html
Normal 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>
|
18
animations/time-to-drink-coffee/script.js
Normal file
18
animations/time-to-drink-coffee/script.js
Normal 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' } });
|
75
animations/time-to-drink-coffee/style.css
Normal file
75
animations/time-to-drink-coffee/style.css
Normal 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);
|
||||||
|
}
|
5
buttons-and-toggles/confetti-button/README.md
Normal file
5
buttons-and-toggles/confetti-button/README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Confetti Button
|
||||||
|
|
||||||
|
Confetti using JS, SCSS, and a HTML5 canvas.
|
||||||
|
|
||||||
|
![image](/attachments/037e2fe9-8348-4313-9404-246e8cdd2d7c)
|
40
buttons-and-toggles/confetti-button/index.html
Normal file
40
buttons-and-toggles/confetti-button/index.html
Normal 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>
|
238
buttons-and-toggles/confetti-button/script.js
Normal file
238
buttons-and-toggles/confetti-button/script.js
Normal 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()
|
168
buttons-and-toggles/confetti-button/style.css
Normal file
168
buttons-and-toggles/confetti-button/style.css
Normal 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;
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
# Download Button Animation
|
# Download Button Animation
|
||||||
These are animated download buttons, by clicking on them the download progress will be shown in the button.
|
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)
|
||||||
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
|||||||
# 2020 Toggles
|
# Modern Toggles
|
||||||
|
|
||||||
![image](/attachments/38b1f3b3-374e-485c-a0b9-d88df6c75268)
|
![image](/attachments/2e60b21f-3df4-4810-b0e3-1f56e7f89549)
|
@ -1,2 +1,3 @@
|
|||||||
# Multi-Button Pill
|
# Multi-Button Pill with hover effect
|
||||||
![image](/attachments/a027cee8-35fd-418e-85bc-9cef32796a5b)
|
|
||||||
|
![image](/attachments/fec564cf-c640-4a40-8306-8c26726f42c4)
|
@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
A simple power switch animation
|
A simple power switch animation
|
||||||
|
|
||||||
![image](/attachments/58131494-ccde-4473-bdb2-2f14f12dac8e)
|
![image](/attachments/261f5dc6-8fbd-4a8b-ab55-e7003a5361fd)
|
@ -1,3 +1,5 @@
|
|||||||
# Responsive numeric stepper
|
# Responsive numeric stepper
|
||||||
|
|
||||||
Used as an example for utilising media queries with the display property to build a 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)
|
5
buttons-and-toggles/rolling-radio-buttons/README.md
Normal file
5
buttons-and-toggles/rolling-radio-buttons/README.md
Normal 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)
|
34
buttons-and-toggles/rolling-radio-buttons/index.html
Normal file
34
buttons-and-toggles/rolling-radio-buttons/index.html
Normal 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>
|
42
buttons-and-toggles/rolling-radio-buttons/script.js
Normal file
42
buttons-and-toggles/rolling-radio-buttons/script.js
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
154
buttons-and-toggles/rolling-radio-buttons/style.css
Normal file
154
buttons-and-toggles/rolling-radio-buttons/style.css
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
# Expanding flex cards
|
# Expanding flex cards
|
||||||
![image](/attachments/a5c1bf9f-f879-4761-a47c-4847a99d56b1)
|
|
||||||
|
![image](/attachments/e783fd42-0567-41f3-a7c7-28b8b5b8c158)
|
@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
Adding fade animation to material design horizontal cards.
|
Adding fade animation to material design horizontal cards.
|
||||||
|
|
||||||
![image](/attachments/d668dd81-0ec2-486f-85d9-68140682c854)
|
![image](/attachments/c409f69e-ded5-4c07-bed7-a2e481d8d46a)
|
@ -5,9 +5,7 @@
|
|||||||
<title>Horizontal Cards with Fade Animation</title>
|
<title>Horizontal Cards with Fade Animation</title>
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
<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>
|
</head>
|
||||||
<body>
|
|
||||||
|
|
||||||
<body class="grey lighten-2">
|
<body class="grey lighten-2">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Snow (Pure CSS)
|
# Snow (Pure CSS)
|
||||||
Snow effect as overlay for a website.
|
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 :)
|
This is a pure CSS version :)
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- partial:index.partial.html -->
|
|
||||||
<div class="snow"></div>
|
<div class="snow"></div>
|
||||||
<div class="snow"></div>
|
<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>
|
<div class="snow"></div>
|
||||||
<div class="snow"></div>
|
<div class="snow"></div>
|
||||||
<!-- partial -->
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
File diff suppressed because it is too large
Load Diff
3
error-pages/403-pages/michu-it-blockpage/README.md
Normal file
3
error-pages/403-pages/michu-it-blockpage/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Michu-IT blockpage
|
||||||
|
|
||||||
|
![image](/attachments/05ffa10e-323b-4449-89c7-83a06301639b)
|
157
error-pages/403-pages/michu-it-blockpage/index.html
Normal file
157
error-pages/403-pages/michu-it-blockpage/index.html
Normal 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>
|
3
error-pages/403-pages/redscreen-block/README.md
Normal file
3
error-pages/403-pages/redscreen-block/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# RedScreen Forbidden page
|
||||||
|
|
||||||
|
![image](/attachments/ad1f54a7-6dd6-4fb0-a5d7-9b14c9813e95)
|
200
error-pages/403-pages/redscreen-block/index.html
Normal file
200
error-pages/403-pages/redscreen-block/index.html
Normal 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>
|
||||||
|
|
@ -1,4 +1,3 @@
|
|||||||
# Error 404: Page not found – 80s hacker theme
|
# Error 404: Page not found – 80s hacker theme
|
||||||
|
|
||||||
|
![image](/attachments/4ae48f9f-288d-4c1d-9a1d-5093c63a3998)
|
||||||
|
|
@ -1,2 +1,3 @@
|
|||||||
# 404 - Glitched out
|
# 404 - Glitched out
|
||||||
![image](/attachments/277d2a6b-8a83-47d2-932b-fa5e7d08ff16)
|
|
||||||
|
![image](/attachments/1ad6480d-b9f3-4056-82bb-4db12c77e984)
|
3
error-pages/503-pages/michu-it-system-offline/README.md
Normal file
3
error-pages/503-pages/michu-it-system-offline/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Michu-IT system offline
|
||||||
|
|
||||||
|
![image](/attachments/3b545851-d425-4516-9cc0-66fb4f651f47)
|
148
error-pages/503-pages/michu-it-system-offline/index.html
Normal file
148
error-pages/503-pages/michu-it-system-offline/index.html
Normal file
File diff suppressed because one or more lines are too long
@ -1,3 +1,3 @@
|
|||||||
# 503 - Page is Offline (Bot)
|
# 503 - Page is Offline (Bot)
|
||||||
|
|
||||||
![image](/attachments/2888530d-72a9-4bff-bf9d-f673216b1e5f)
|
![image](/attachments/4a15fa37-13f5-469e-a2c2-c11094bb7a84)
|
@ -7,7 +7,7 @@ body {
|
|||||||
width: 120px;
|
width: 120px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
top: 50px;
|
top: 50px;
|
||||||
color: #000;
|
color: #fff;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
margin-left: -220px;
|
margin-left: -220px;
|
||||||
line-height: 1.3em;
|
line-height: 1.3em;
|
||||||
|
3
error-pages/503-pages/time-to-drink-coffee/README.md
Normal file
3
error-pages/503-pages/time-to-drink-coffee/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Time to Drink Coffee Animation
|
||||||
|
|
||||||
|
![image](/attachments/ce9c7c8c-b540-4669-8965-dce69cf852b9)
|
73
error-pages/503-pages/time-to-drink-coffee/index.html
Normal file
73
error-pages/503-pages/time-to-drink-coffee/index.html
Normal 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>
|
18
error-pages/503-pages/time-to-drink-coffee/script.js
Normal file
18
error-pages/503-pages/time-to-drink-coffee/script.js
Normal 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' } });
|
105
error-pages/503-pages/time-to-drink-coffee/style.css
Normal file
105
error-pages/503-pages/time-to-drink-coffee/style.css
Normal 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);
|
||||||
|
}
|
@ -1,2 +1,3 @@
|
|||||||
# Blackboard Contact Form
|
# Blackboard Contact Form
|
||||||
![image](/attachments/f9f7e294-b0f9-41a2-b372-7f80c732f0b5)
|
|
||||||
|
![image](/attachments/a052b075-a959-4c67-977b-4154eca4a9c8)
|
@ -1,3 +1,3 @@
|
|||||||
# Modern Login Form with floating placeholder and light button
|
# Modern Login Form with floating placeholder and light button
|
||||||
|
|
||||||
|
![image](/attachments/b4da4554-ccdd-47cd-8c59-3f295f13115d)
|
@ -1,2 +1,3 @@
|
|||||||
# Login Form
|
# Login Form
|
||||||
![image](/attachments/39ded713-ee72-40cb-adac-78ebe26ded7b)
|
|
||||||
|
![image](/attachments/8d6fb567-cdff-44bf-bc0e-cc771604e596)
|
11
layout-techniques/layouting-with-css-grid/README.md
Normal file
11
layout-techniques/layouting-with-css-grid/README.md
Normal 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)
|
48
layout-techniques/layouting-with-css-grid/index.html
Normal file
48
layout-techniques/layouting-with-css-grid/index.html
Normal 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>
|
55
layout-techniques/layouting-with-css-grid/style.css
Normal file
55
layout-techniques/layouting-with-css-grid/style.css
Normal 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;
|
||||||
|
}
|
3
web-snippets/random-password-generator/README.md
Normal file
3
web-snippets/random-password-generator/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Random Password Generator - HTML | CSS | JS
|
||||||
|
|
||||||
|
![image](/attachments/2116d7c3-e52b-4c37-ba64-9f9dddc278bf)
|
52
web-snippets/random-password-generator/index.html
Normal file
52
web-snippets/random-password-generator/index.html
Normal 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&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>
|
194
web-snippets/random-password-generator/script.js
Normal file
194
web-snippets/random-password-generator/script.js
Normal 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()
|
||||||
|
})
|
||||||
|
})
|
297
web-snippets/random-password-generator/style.css
Normal file
297
web-snippets/random-password-generator/style.css
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user