Implement togglable site-wide woo mode
This commit is contained in:
233
_includes/wooMode.njk
Normal file
233
_includes/wooMode.njk
Normal file
@@ -0,0 +1,233 @@
|
||||
<!-- Woo Mode -->
|
||||
<!-- Based on https://codepen.io/tommyho/pen/JjgoZLK -->
|
||||
{# This include replaces the page background with a crazy rainbow animated shader #}
|
||||
|
||||
<canvas id="shaderCanvas"></canvas>
|
||||
<script src="/js/three.min.js"></script>
|
||||
<script>
|
||||
let scene, camera, renderer, uniforms, material, mesh;
|
||||
|
||||
function initWoo() {
|
||||
scene = new THREE.Scene();
|
||||
camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
|
||||
|
||||
renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('shaderCanvas'), antialias: true });
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
|
||||
const vertexShader = `
|
||||
varying vec2 vUv;
|
||||
void main() {
|
||||
vUv = uv;
|
||||
gl_Position = vec4(position, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
const fragmentShader = `
|
||||
uniform float time;
|
||||
uniform vec2 resolution;
|
||||
varying vec2 vUv;
|
||||
|
||||
#define PI 3.14159265358979323846
|
||||
|
||||
vec2 rotate(vec2 v, float a) {
|
||||
float s = sin(a);
|
||||
float c = cos(a);
|
||||
mat2 m = mat2(c, -s, s, c);
|
||||
return m * v;
|
||||
}
|
||||
|
||||
float random(vec2 st) {
|
||||
return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
|
||||
}
|
||||
|
||||
float noise(vec2 st) {
|
||||
vec2 i = floor(st);
|
||||
vec2 f = fract(st);
|
||||
|
||||
float a = random(i);
|
||||
float b = random(i + vec2(1.0, 0.0));
|
||||
float c = random(i + vec2(0.0, 1.0));
|
||||
float d = random(i + vec2(1.0, 1.0));
|
||||
|
||||
vec2 u = f * f * (3.0 - 2.0 * f);
|
||||
|
||||
return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
|
||||
}
|
||||
|
||||
float fbm(vec2 st) {
|
||||
float value = 0.0;
|
||||
float amplitude = 0.5;
|
||||
float frequency = 0.0;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
value += amplitude * noise(st);
|
||||
st *= 2.0;
|
||||
amplitude *= 0.5;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
vec3 palette(float t, vec3 a, vec3 b, vec3 c, vec3 d) {
|
||||
return a + b * cos(6.28318 * (c * t + d));
|
||||
}
|
||||
|
||||
vec3 vibrancePalette(float t) {
|
||||
vec3 a = vec3(0.5, 0.5, 0.5);
|
||||
vec3 b = vec3(0.5, 0.5, 0.5);
|
||||
vec3 c = vec3(1.0, 1.0, 1.0);
|
||||
vec3 d = vec3(0.0, 0.33, 0.67);
|
||||
return palette(t, a, b, c, d);
|
||||
}
|
||||
|
||||
vec3 warmPalette(float t) {
|
||||
vec3 a = vec3(0.5, 0.5, 0.5);
|
||||
vec3 b = vec3(0.5, 0.5, 0.5);
|
||||
vec3 c = vec3(1.0, 1.0, 1.0);
|
||||
vec3 d = vec3(0.0, 0.10, 0.20);
|
||||
return palette(t, a, b, c, d);
|
||||
}
|
||||
|
||||
vec3 coolPalette(float t) {
|
||||
vec3 a = vec3(0.5, 0.5, 0.5);
|
||||
vec3 b = vec3(0.5, 0.5, 0.5);
|
||||
vec3 c = vec3(1.0, 1.0, 1.0);
|
||||
vec3 d = vec3(0.3, 0.20, 0.20);
|
||||
return palette(t, a, b, c, d);
|
||||
}
|
||||
|
||||
vec3 rainbowPalette(float t) {
|
||||
vec3 a = vec3(0.5, 0.5, 0.5);
|
||||
vec3 b = vec3(0.5, 0.5, 0.5);
|
||||
vec3 c = vec3(1.0, 1.0, 1.0);
|
||||
vec3 d = vec3(0.0, 0.33, 0.67);
|
||||
return palette(t, a, b, c, d);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 st = gl_FragCoord.xy / resolution.xy;
|
||||
st.x *= resolution.x / resolution.y;
|
||||
|
||||
vec2 q = vec2(0.);
|
||||
q.x = fbm(st + 0.1 * time);
|
||||
q.y = fbm(st + vec2(1.0));
|
||||
|
||||
vec2 r = vec2(0.);
|
||||
r.x = fbm(st + 1.0 * q + vec2(1.7, 9.2) + 0.15 * time);
|
||||
r.y = fbm(st + 1.0 * q + vec2(8.3, 2.8) + 0.126 * time);
|
||||
|
||||
float f = fbm(st + r);
|
||||
|
||||
vec2 p = st * 2.0 - 1.0;
|
||||
float a = atan(p.y, p.x);
|
||||
float r2 = length(p);
|
||||
|
||||
vec2 uv = vec2(a / PI, r2);
|
||||
uv = rotate(uv, time * 0.1);
|
||||
|
||||
vec3 color1 = vibrancePalette(f + time * 0.1);
|
||||
vec3 color2 = warmPalette(length(q));
|
||||
vec3 color3 = coolPalette(length(r.x));
|
||||
vec3 color4 = rainbowPalette(f * 2.0 + time * 0.2);
|
||||
|
||||
vec3 color = mix(color1, color2, 0.5);
|
||||
color = mix(color, color3, 0.3);
|
||||
color = mix(color, color4, sin(time * 0.1) * 0.5 + 0.5);
|
||||
|
||||
color += 0.05 * vec3(1.0) * smoothstep(0.1, 0.2, fbm(10.0 * uv + time * 0.5));
|
||||
|
||||
// Add some extra vibrancy
|
||||
color = pow(color, vec3(0.8));
|
||||
color *= 1.1;
|
||||
|
||||
gl_FragColor = vec4(color, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
uniforms = {
|
||||
time: { value: 1.0 },
|
||||
resolution: { value: new THREE.Vector2() }
|
||||
};
|
||||
|
||||
material = new THREE.ShaderMaterial({
|
||||
uniforms: uniforms,
|
||||
vertexShader: vertexShader,
|
||||
fragmentShader: fragmentShader
|
||||
});
|
||||
|
||||
mesh = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), material);
|
||||
scene.add(mesh);
|
||||
|
||||
onWindowResize();
|
||||
window.addEventListener('resize', onWindowResize, false);
|
||||
}
|
||||
|
||||
function onWindowResize() {
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
uniforms.resolution.value.x = renderer.domElement.width;
|
||||
uniforms.resolution.value.y = renderer.domElement.height;
|
||||
}
|
||||
|
||||
function animate(timestamp) {
|
||||
requestAnimationFrame(animate);
|
||||
uniforms.time.value = timestamp * 0.001;
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
background: none;
|
||||
}
|
||||
canvas {
|
||||
display: block;
|
||||
opacity: .35;
|
||||
pointer-events: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const wooCanvas = document.getElementById("shaderCanvas");
|
||||
const wooToggle = document.getElementById("wooToggle");
|
||||
const wooPreference = localStorage.getItem("wooMode");
|
||||
let forcedWoo = false;
|
||||
|
||||
// Initial preference check on page load
|
||||
if ({% if metadata.wooModeOnByDefault == false %}!wooPreference || {% endif %}wooPreference == 0) {
|
||||
forcedWoo ? null : wooCanvas.classList.add("hidden");
|
||||
wooToggle.checked = false;
|
||||
} else {
|
||||
wooCanvas.classList.remove("hidden");
|
||||
wooToggle.checked = true;
|
||||
initWoo();
|
||||
animate(0);
|
||||
};
|
||||
|
||||
// Handle setting toggle
|
||||
wooToggle.addEventListener('change', function() {
|
||||
if (this.checked) {
|
||||
localStorage.setItem("wooMode", 1);
|
||||
wooCanvas.classList.remove("hidden");
|
||||
initWoo();
|
||||
animate(0);
|
||||
} else {
|
||||
localStorage.setItem("wooMode", 0);
|
||||
forcedWoo ? null : wooCanvas.classList.add("hidden");
|
||||
};
|
||||
});
|
||||
|
||||
{% if forcedWoo %}
|
||||
// Handle forced woo
|
||||
forcedWoo = true;
|
||||
console.log("Forcing woo mode: woo!");
|
||||
wooCanvas.classList.remove("hidden");
|
||||
initWoo();
|
||||
animate(0);
|
||||
{% endif %}
|
||||
|
||||
|
||||
</script>
|
||||
<!-- /weather -->
|
||||
Reference in New Issue
Block a user