Compare commits
6
Commits
e95c848296
...
dc2f0013b0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc2f0013b0
|
||
|
|
36d9ac4230
|
||
|
|
3e7c9475e5
|
||
|
|
9f67c28bc0
|
||
|
|
0170db486c
|
||
|
|
94907c178e
|
@@ -0,0 +1 @@
|
|||||||
|
status/index.html
|
||||||
+2
-4
@@ -160,10 +160,8 @@
|
|||||||
</article>
|
</article>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
<video id="texture" autoplay muted loop src="./assets/video/close.webm" class="closedBackground"></video>
|
<video data-cute-texture-video class="cute-texture-video" src="/assets/video/background.webm" autoplay muted loop ></video>
|
||||||
<video id="texture" autoplay muted loop src="./assets/video/open.webm" class="openBackground"></video>
|
<canvas class="cute-texture"></canvas>
|
||||||
<video id="background" autoplay muted loop src="./assets/video/background.webm" class="openBackground"></video>
|
|
||||||
<canvas></canvas>
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<section class="floatingWindow" id="posterWindow">
|
<section class="floatingWindow" id="posterWindow">
|
||||||
|
|||||||
+328
-67
@@ -1,87 +1,348 @@
|
|||||||
const target = document.querySelector('canvas');
|
const DEFAULT_VERTEX_SHADER = `
|
||||||
|
attribute float aVertexIndex;
|
||||||
|
varying highp vec2 vTextureCoord;
|
||||||
|
|
||||||
const ctx = target.getContext('2d');
|
// This vertex shader produces the following, when drawn using indices 0..3:
|
||||||
|
//
|
||||||
|
// 1 | 0-----x.....2
|
||||||
|
// 0 | | s | . ´
|
||||||
|
// -1 | x_____x´
|
||||||
|
// -2 | : .´
|
||||||
|
// -3 | 1´
|
||||||
|
// +---------------
|
||||||
|
// -1 0 1 2 3
|
||||||
|
//
|
||||||
|
// The axes are clip-space x and y. The region marked s is the visible region.
|
||||||
|
// The digits in the corners of the right-angled triangle are the vertex
|
||||||
|
// indices.
|
||||||
|
//
|
||||||
|
// The top-left has UV 0,0, the bottom-left has 0,2, and the top-right has 2,0.
|
||||||
|
// This means that the UV gets interpolated to 1,1 at the bottom-right corner
|
||||||
|
// of the clip-space rectangle that is at 1,-1 in clip space.
|
||||||
|
|
||||||
export let lolState = false;
|
void main() {
|
||||||
|
vec2 uv = vec2(floor(aVertexIndex / 2.0), floor(mod(aVertexIndex, 2.0))) * 2.0;
|
||||||
let scale = 0.7;
|
gl_Position = vec4(uv * vec2(2.0, -2.0) + vec2(-1.0, 1.0), 0.0, 1.0);
|
||||||
|
vTextureCoord = uv;
|
||||||
function analogLikeNoise(){
|
|
||||||
let tH = target.height;
|
|
||||||
let tW = target.width;
|
|
||||||
//console.log('window dimensions :', tH, tW)
|
|
||||||
const img = ctx.createImageData(tW, tH);
|
|
||||||
let offset = 0;
|
|
||||||
let index = 0;
|
|
||||||
let brightness = 0;
|
|
||||||
|
|
||||||
for(let x = 0; x<tW; x++){
|
|
||||||
const sx = x * scale + offset
|
|
||||||
for(let y = 0; y<tH; y++){
|
|
||||||
let lum = Math.random()*161
|
|
||||||
|
|
||||||
//img.data[index++] = lum*150/255;
|
|
||||||
index++
|
|
||||||
img.data[index++] = lum;
|
|
||||||
img.data[index++] = lum;
|
|
||||||
img.data[index++] = 255;
|
|
||||||
}
|
}
|
||||||
}
|
`
|
||||||
ctx.putImageData(img, 0, 0);
|
|
||||||
|
|
||||||
offset ++;
|
const FRAGMENT_SHADER_UTILITIES = `
|
||||||
if(offset>1024){
|
highp float noise1(highp vec2 co){
|
||||||
offset = 0;
|
return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color spaces from https://github.com/tobspr/GLSL-Color-Spaces/blob/master/ColorSpaces.inc.glsl
|
||||||
|
|
||||||
|
const highp float HCV_EPSILON = 1e-10;
|
||||||
|
const highp float HSL_EPSILON = 1e-10;
|
||||||
|
|
||||||
|
highp vec3 hue_to_rgb(highp float hue)
|
||||||
|
{
|
||||||
|
highp float R = abs(hue * 6.0 - 3.0) - 1.0;
|
||||||
|
highp float G = 2.0 - abs(hue * 6.0 - 2.0);
|
||||||
|
highp float B = 2.0 - abs(hue * 6.0 - 4.0);
|
||||||
|
return clamp(vec3(R,G,B), vec3(0), vec3(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts from HSL to linear RGB
|
||||||
|
highp vec3 hsl_to_rgb(highp vec3 hsl)
|
||||||
|
{
|
||||||
|
highp vec3 rgb = hue_to_rgb(hsl.x);
|
||||||
|
highp float C = (1.0 - abs(2.0 * hsl.z - 1.0)) * hsl.y;
|
||||||
|
return (rgb - 0.5) * C + hsl.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts a value from linear RGB to HCV (Hue, Chroma, Value)
|
||||||
|
highp vec3 rgb_to_hcv(highp vec3 rgb)
|
||||||
|
{
|
||||||
|
// Based on work by Sam Hocevar and Emil Persson
|
||||||
|
highp vec4 P = (rgb.g < rgb.b) ? vec4(rgb.bg, -1.0, 2.0/3.0) : vec4(rgb.gb, 0.0, -1.0/3.0);
|
||||||
|
highp vec4 Q = (rgb.r < P.x) ? vec4(P.xyw, rgb.r) : vec4(rgb.r, P.yzx);
|
||||||
|
highp float C = Q.x - min(Q.w, Q.y);
|
||||||
|
highp float H = abs((Q.w - Q.y) / (6.0 * C + HCV_EPSILON) + Q.z);
|
||||||
|
return vec3(H, C, Q.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts from linear rgb to HSL
|
||||||
|
highp vec3 rgb_to_hsl(highp vec3 rgb)
|
||||||
|
{
|
||||||
|
highp vec3 HCV = rgb_to_hcv(rgb);
|
||||||
|
highp float L = HCV.z - HCV.y * 0.5;
|
||||||
|
highp float S = HCV.y / (1.0 - abs(L * 2.0 - 1.0) + HSL_EPSILON);
|
||||||
|
return vec3(HCV.x, S, L);
|
||||||
|
}
|
||||||
|
|
||||||
|
highp vec4 hue_rotate(highp vec4 rgb_input, highp float amount) {
|
||||||
|
highp vec3 hsl = rgb_to_hsl(rgb_input.xyz);
|
||||||
|
hsl.x = mod(hsl.x+amount,1.0);
|
||||||
|
return vec4(hsl_to_rgb(hsl), rgb_input.w);
|
||||||
|
}
|
||||||
|
|
||||||
|
highp vec4 grayscale(highp vec4 rgb_input, highp float amount) {
|
||||||
|
highp vec3 hsl = rgb_to_hsl(rgb_input.xyz);
|
||||||
|
hsl.y = 1.0 - amount;
|
||||||
|
return vec4(hsl_to_rgb(hsl), rgb_input.w);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const FRAGMENT_SHADER_UTILITIES_N_LINES = FRAGMENT_SHADER_UTILITIES.split(/\n/g).length
|
||||||
|
|
||||||
|
const DEFAULT_FRAGMENT_SHADER = `
|
||||||
|
// Some random values computed for each frame
|
||||||
|
uniform highp vec4 uRandom;
|
||||||
|
// Size (in logical pixel) of current CuteTexture canvas
|
||||||
|
uniform lowp vec2 uCanvasSize;
|
||||||
|
// Number of seconds since canvas was initialized
|
||||||
|
uniform lowp float uTime;
|
||||||
|
// Current frame of first video with [data-cute-texture-video] attribute
|
||||||
|
uniform sampler2D uVideo;
|
||||||
|
// Size (in pixels) of current video frame in uVideo
|
||||||
|
uniform lowp vec2 uVideoSize;
|
||||||
|
// Coordinates of current pixel being computed from [0,0] (top left) to [1,1] (bottom right)
|
||||||
|
varying highp vec2 vTextureCoord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_FragColor = vec4(vTextureCoord.x, vTextureCoord.y, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise un canvas d'arrière plan "CuteTexture" avec les options données
|
||||||
|
*/
|
||||||
|
export function initCuteTexture(options){
|
||||||
|
let shaderSourceOption = options?.shaderSource;
|
||||||
|
|
||||||
|
/** @type {HTMLCanvasElement} */
|
||||||
|
const targetCanvas = document.querySelector("canvas[data-cute-texture]") || document.querySelector("canvas.cute-texture") || document.querySelector("canvas");
|
||||||
|
if(!targetCanvas)
|
||||||
|
throw new Error(`Aucun élément <canvas> trouvé sur la page`)
|
||||||
|
|
||||||
|
{
|
||||||
|
let computedStyle = getComputedStyle(targetCanvas)
|
||||||
|
let elementWidth = parseFloat(computedStyle.width)
|
||||||
|
let elementHeight = parseFloat(computedStyle.height)
|
||||||
|
targetCanvas.width = elementWidth
|
||||||
|
targetCanvas.height = elementHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
const ctx = targetCanvas.getContext("webgl");
|
||||||
|
if(ctx == null)
|
||||||
|
throw new Error("WebGL n'ext pas supporté sur votre machine")
|
||||||
|
|
||||||
|
let mainShader = initMainShader(ctx, {
|
||||||
|
shaderSource: shaderSourceOption
|
||||||
|
})
|
||||||
|
console.log("Shader CuteTexture initialisé", mainShader)
|
||||||
|
|
||||||
|
const initTime = Date.now()
|
||||||
|
|
||||||
|
function startRender(){
|
||||||
|
renderCuteTexture(targetCanvas, mainShader, initTime)
|
||||||
|
requestAnimationFrame(startRender)
|
||||||
|
}
|
||||||
|
|
||||||
|
startRender()
|
||||||
|
|
||||||
|
return {
|
||||||
|
target: targetCanvas,
|
||||||
|
initTime,
|
||||||
|
resize: function(width, height) {
|
||||||
|
targetCanvas.width = width
|
||||||
|
targetCanvas.height = height
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let time = 0
|
/**
|
||||||
|
* @param {WebGLRenderingContext} ctx
|
||||||
|
*/
|
||||||
|
function initMainShader(ctx, options){
|
||||||
|
let fragmentShaderCode = options?.shaderSource;
|
||||||
|
|
||||||
function scanlines(){
|
const vertexShader = ctx.createShader(ctx.VERTEX_SHADER)
|
||||||
time += 0.05;
|
ctx.shaderSource(vertexShader, DEFAULT_VERTEX_SHADER)
|
||||||
let tH = target.height;
|
ctx.compileShader(vertexShader)
|
||||||
let tW = target.width;
|
|
||||||
//console.log('window dimensions :', tH, tW)
|
|
||||||
const img = ctx.createImageData(tW, tH);
|
|
||||||
let index = 0;
|
|
||||||
|
|
||||||
for(let y = 0; y<tH; y++){
|
if (!ctx.getShaderParameter(vertexShader, ctx.COMPILE_STATUS)) {
|
||||||
for(let x = 0; x<tW; x++){
|
throw new Error(`Le vertex shader n'a pas pu être compilé: ${ctx.getShaderInfoLog(vertexShader)}`)
|
||||||
let lum = (Math.sin(y*2 - time*3.1415) * 44) + 161;
|
ctx.deleteShader(vertexShader)
|
||||||
|
return null
|
||||||
//img.data[index++] = lum;
|
|
||||||
index++;
|
|
||||||
img.data[index++] = lum;
|
|
||||||
//img.data[index++] = lum + Math.random()*32;
|
|
||||||
index++
|
|
||||||
img.data[index++] = 255;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctx.putImageData(img, 0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function scanlinesAnimation(){
|
if(!fragmentShaderCode){
|
||||||
scanlines();
|
fragmentShaderCode = getDocumentShaderSource(document);
|
||||||
requestAnimationFrame(scanlinesAnimation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function noiseAnimation(){
|
if(!fragmentShaderCode){
|
||||||
analogLikeNoise();
|
fragmentShaderCode = DEFAULT_FRAGMENT_SHADER
|
||||||
requestAnimationFrame(noiseAnimation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fragmentShader = ctx.createShader(ctx.FRAGMENT_SHADER)
|
||||||
|
ctx.shaderSource(fragmentShader, FRAGMENT_SHADER_UTILITIES+fragmentShaderCode)
|
||||||
|
ctx.compileShader(fragmentShader)
|
||||||
|
|
||||||
export function loadCanvas(){
|
if (!ctx.getShaderParameter(fragmentShader, ctx.COMPILE_STATUS)) {
|
||||||
let textureVideo = document.querySelectorAll('main > #texture');
|
let error = ctx.getShaderInfoLog(fragmentShader)
|
||||||
|
|
||||||
for (let video of textureVideo){
|
let errorLine = error.match(/\d+:(\d+):/)
|
||||||
video.style.visibility = 'collapse';
|
if(errorLine) {
|
||||||
|
let lineNum = parseInt(errorLine[1])
|
||||||
|
let lineNumeWithoutUtils = lineNum-FRAGMENT_SHADER_UTILITIES_N_LINES;
|
||||||
|
if(lineNumeWithoutUtils >= 0){
|
||||||
|
error = `Ligne ${lineNumeWithoutUtils}: ${error}`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let textureCanvas = document.querySelector('main > canvas');
|
ctx.deleteShader(fragmentShader)
|
||||||
textureCanvas.style.visibility = 'visible';
|
throw new Error(`Le fragment shader n'a pas pu être compilé: ${error}`)
|
||||||
|
}
|
||||||
//console.log(textureCanvas);
|
|
||||||
|
const shaderProgram = ctx.createProgram()
|
||||||
//console.log(textureVideo)
|
ctx.attachShader(shaderProgram, vertexShader)
|
||||||
|
ctx.attachShader(shaderProgram, fragmentShader)
|
||||||
|
ctx.linkProgram(shaderProgram)
|
||||||
|
|
||||||
|
if (!ctx.getProgramParameter(shaderProgram, ctx.LINK_STATUS)) {
|
||||||
|
throw new Error(`Le shader n'a pas pu être linké: ${ctx.getProgramInfoLog(shaderProgram)}`)
|
||||||
|
ctx.deleteShader(vertexShader)
|
||||||
|
ctx.deleteShader(fragmentShader)
|
||||||
|
ctx.deleteProgram(shaderProgram)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
let videoElement, videoTexture = null;
|
||||||
|
if(ctx.getUniformLocation(shaderProgram, "uVideo") != null){
|
||||||
|
videoElement = document.querySelector("video[data-cute-texture-video]") || document.querySelector("video");
|
||||||
|
if(!videoElement){
|
||||||
|
console.warn("Aucune vidéo avec l'attribut [data-cute-texture-video] n'a été trouvé sur la page");
|
||||||
|
} else {
|
||||||
|
videoTexture = ctx.createTexture();
|
||||||
|
ctx.bindTexture(ctx.TEXTURE_2D, videoTexture)
|
||||||
|
ctx.texImage2D(
|
||||||
|
ctx.TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
ctx.RGBA,
|
||||||
|
1, 1,
|
||||||
|
0,
|
||||||
|
ctx.RGBA,
|
||||||
|
ctx.UNSIGNED_BYTE,
|
||||||
|
new Uint8Array([0, 0, 0, 255])
|
||||||
|
)
|
||||||
|
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_S, ctx.CLAMP_TO_EDGE);
|
||||||
|
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_T, ctx.CLAMP_TO_EDGE);
|
||||||
|
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.LINEAR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const vertexIndexBuffer = ctx.createBuffer()
|
||||||
|
ctx.bindBuffer(ctx.ARRAY_BUFFER, vertexIndexBuffer)
|
||||||
|
ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array([0, 1, 2]), ctx.STATIC_DRAW)
|
||||||
|
|
||||||
|
return {
|
||||||
|
program: shaderProgram,
|
||||||
|
vertexShader,
|
||||||
|
fragmentShader,
|
||||||
|
videoElement,
|
||||||
|
videoTexture,
|
||||||
|
aVertexIndex: ctx.getAttribLocation(shaderProgram, "aVertexIndex"),
|
||||||
|
uRandom: ctx.getUniformLocation(shaderProgram, "uRandom"),
|
||||||
|
uCanvasSize: ctx.getUniformLocation(shaderProgram, "uCanvasSize"),
|
||||||
|
uTime: ctx.getUniformLocation(shaderProgram, "uTime"),
|
||||||
|
uVideo: ctx.getUniformLocation(shaderProgram, "uVideo"),
|
||||||
|
uVideoSize: ctx.getUniformLocation(shaderProgram, "uVideoSize"),
|
||||||
|
vertexIndexBuffer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {HTMLCanvasElement} target
|
||||||
|
* @param {*} mainShader
|
||||||
|
*/
|
||||||
|
function renderCuteTexture(target, mainShader, initTime){
|
||||||
|
let ctx = target.getContext("webgl")
|
||||||
|
|
||||||
|
ctx.clearColor(0, 0, 0, 0)
|
||||||
|
ctx.clearDepth(1.0)
|
||||||
|
ctx.enable(ctx.DEPTH_TEST)
|
||||||
|
ctx.depthFunc(ctx.LEQUAL)
|
||||||
|
|
||||||
|
ctx.clear(ctx.COLOR_BUFFER_BIT)
|
||||||
|
|
||||||
|
ctx.bindBuffer(ctx.ARRAY_BUFFER, mainShader.vertexIndexBuffer)
|
||||||
|
ctx.vertexAttribPointer(
|
||||||
|
mainShader.aVertexIndex, // location
|
||||||
|
1, // pull out 1 value per iteration
|
||||||
|
ctx.FLOAT, // the data in the buffer is 32bit floats
|
||||||
|
false, // don't normalize
|
||||||
|
0, // how many bytes to get from one set of values to the next
|
||||||
|
0 // how many bytes inside the buffer to start from
|
||||||
|
)
|
||||||
|
ctx.enableVertexAttribArray(mainShader.aVertexIndex)
|
||||||
|
|
||||||
|
ctx.useProgram(mainShader.program)
|
||||||
|
if(mainShader.uRandom){
|
||||||
|
ctx.uniform4f(
|
||||||
|
mainShader.uRandom,
|
||||||
|
Math.random(),
|
||||||
|
Math.random(),
|
||||||
|
Math.random(),
|
||||||
|
Math.random()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mainShader.uCanvasSize){
|
||||||
|
ctx.uniform2f(
|
||||||
|
mainShader.uCanvasSize,
|
||||||
|
target.width,
|
||||||
|
target.height
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mainShader.uTime){
|
||||||
|
ctx.uniform1f(
|
||||||
|
mainShader.uTime,
|
||||||
|
(Date.now() - initTime)/1000
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mainShader.uVideo){
|
||||||
|
ctx.activeTexture(ctx.TEXTURE0);
|
||||||
|
ctx.bindTexture(ctx.TEXTURE_2D, mainShader.videoTexture);
|
||||||
|
ctx.uniform1i(
|
||||||
|
mainShader.uVideo,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mainShader.uVideo && mainShader.videoElement && mainShader.videoTexture){
|
||||||
|
ctx.activeTexture(ctx.TEXTURE0);
|
||||||
|
ctx.bindTexture(ctx.TEXTURE_2D, mainShader.videoTexture);
|
||||||
|
ctx.texImage2D(
|
||||||
|
ctx.TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
ctx.RGBA,
|
||||||
|
ctx.RGBA,
|
||||||
|
ctx.UNSIGNED_BYTE,
|
||||||
|
mainShader.videoElement,
|
||||||
|
);
|
||||||
|
ctx.uniform1i(
|
||||||
|
mainShader.uVideo,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mainShader.uVideoSize && mainShader.videoElement) {
|
||||||
|
ctx.uniform2f(
|
||||||
|
mainShader.uVideoSize,
|
||||||
|
mainShader.videoElement.videoWidth,
|
||||||
|
mainShader.videoElement.videoHeight
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.drawArrays(ctx.TRIANGLE_STRIP, 0, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDocumentShaderSource(doc=document){
|
||||||
|
return doc.querySelector(`script[type="x-shader/glsl+fragment"]`)?.innerHTML
|
||||||
}
|
}
|
||||||
+11
-37
@@ -1,45 +1,19 @@
|
|||||||
import { loadEvents } from './events.js'
|
import { loadEvents } from './events.js'
|
||||||
import { loadStatus } from './status.js'
|
import { loadStatus } from './status.js'
|
||||||
import { scanlinesAnimation } from './cuteTexture.js'
|
import { initCuteTexture } from './cuteTexture.js'
|
||||||
import { noiseAnimation } from './cuteTexture.js'
|
|
||||||
import { loadCanvas } from './cuteTexture.js'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =================================== background texture
|
|
||||||
loadCanvas();
|
|
||||||
let backgroundVideo = document.querySelector('main > #background');
|
|
||||||
|
|
||||||
let proxyTarget = {};
|
|
||||||
let proxyHandler = {
|
|
||||||
|
|
||||||
};
|
|
||||||
export let statusProxy = new Proxy(proxyTarget, {
|
|
||||||
set(obj, prop, value){
|
|
||||||
if(prop === 'lolStatus'){
|
|
||||||
if (value){
|
|
||||||
backgroundVideo.style.visibility = 'visible';
|
|
||||||
scanlinesAnimation();
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
backgroundVideo.style.visibility = 'collapse';
|
|
||||||
noiseAnimation();
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
statusProxy.lolStatus = false;
|
|
||||||
|
|
||||||
//console.log(proxyTarget.lolStatus);
|
|
||||||
|
|
||||||
|
|
||||||
// ======================================== events & statut d'ouverture
|
// ======================================== events & statut d'ouverture
|
||||||
loadEvents();
|
loadEvents();
|
||||||
loadStatus();
|
loadStatus()
|
||||||
|
.then((status) => {
|
||||||
|
console.log("Status actuel du LOL", status);
|
||||||
|
|
||||||
|
let cuteTexture = initCuteTexture({
|
||||||
|
shaderSource: status.shaderSource
|
||||||
|
});
|
||||||
|
|
||||||
|
cuteTexture.target.style.visibility = "visible"
|
||||||
|
});
|
||||||
|
|
||||||
// ======================================== floating event window
|
// ======================================== floating event window
|
||||||
|
|
||||||
|
|||||||
+7
-9
@@ -1,11 +1,13 @@
|
|||||||
import { statusProxy } from './main.js'
|
import { getDocumentShaderSource } from './cuteTexture.js';
|
||||||
|
|
||||||
export async function loadStatus(){
|
export async function loadStatus(){
|
||||||
let res = await fetch('/status/index.ouvert.html');
|
let res = await fetch('/status/index.html');
|
||||||
|
|
||||||
let html_text = await res.text()
|
let html_text = await res.text()
|
||||||
let html = new DOMParser().parseFromString(html_text, "text/html");
|
let html = new DOMParser().parseFromString(html_text, "text/html");
|
||||||
|
|
||||||
|
let shaderSource = getDocumentShaderSource(html);
|
||||||
|
|
||||||
let target = document.querySelector('#statuSection');
|
let target = document.querySelector('#statuSection');
|
||||||
|
|
||||||
let hiddenBtn = target.querySelector('a');
|
let hiddenBtn = target.querySelector('a');
|
||||||
@@ -15,13 +17,7 @@ export async function loadStatus(){
|
|||||||
let status = html.querySelector('#lolStatus');
|
let status = html.querySelector('#lolStatus');
|
||||||
console.log('current status:', status.innerText);
|
console.log('current status:', status.innerText);
|
||||||
|
|
||||||
|
let isOpen = status.innerText.includes('ouvert');
|
||||||
if (status.innerText.includes('ouvert')){
|
|
||||||
statusProxy.lolStatus = true;
|
|
||||||
}else{
|
|
||||||
statusProxy.lolStatus = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let icon = html.querySelector('link[rel="icon"]');
|
let icon = html.querySelector('link[rel="icon"]');
|
||||||
let style = html.querySelectorAll('link[rel="stylesheet"]')[0];
|
let style = html.querySelectorAll('link[rel="stylesheet"]')[0];
|
||||||
@@ -31,4 +27,6 @@ export async function loadStatus(){
|
|||||||
document.head.append(style);
|
document.head.append(style);
|
||||||
|
|
||||||
target.appendChild(status);
|
target.appendChild(status);
|
||||||
|
|
||||||
|
return { open: isOpen, shaderSource };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,3 +28,39 @@ Ce document sert à échanger et partager des idées et trucs à faire pour cont
|
|||||||
* [ ] désactiver la video pour les personnes qui veulent pas d'effets visuels
|
* [ ] désactiver la video pour les personnes qui veulent pas d'effets visuels
|
||||||
|
|
||||||
* [ ] réglages padding section statut
|
* [ ] réglages padding section statut
|
||||||
|
|
||||||
|
# Démarrer un serveur de developpement
|
||||||
|
|
||||||
|
```
|
||||||
|
python3 -m http.server
|
||||||
|
```
|
||||||
|
|
||||||
|
Et ensuite, le site est disponible sur http://localhost:8000/
|
||||||
|
|
||||||
|
## Tester sur son tel
|
||||||
|
|
||||||
|
Il faut démarrer le serveur avec la commande suivante:
|
||||||
|
|
||||||
|
```
|
||||||
|
python3 -m http.server -b 0.0.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Ensuite il faut obtenir l'adresse IP de son ordinateur
|
||||||
|
```
|
||||||
|
ip a
|
||||||
|
```
|
||||||
|
|
||||||
|
Dans notre cas c'est `10.0.0.165` (voir l'exemple ci-dessous).
|
||||||
|
```
|
||||||
|
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
|
||||||
|
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
|
||||||
|
inet 127.0.0.1/8 scope host lo
|
||||||
|
valid_lft forever preferred_lft forever
|
||||||
|
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
|
||||||
|
link/ether c4:9d:ed:15:78:cc brd ff:ff:ff:ff:ff:ff
|
||||||
|
inet 10.0.0.165/24 brd 10.0.0.255 scope global dynamic noprefixroute mlan0
|
||||||
|
valid_lft 5204sec preferred_lft 5204sec
|
||||||
|
```
|
||||||
|
|
||||||
|
Sur son tel, on peut alors acceder a http://10.0.0.165:8000/.
|
||||||
|
Si cela ne fonctionne pas, c'est peut être que le pare feu interdit la connexion depuis un autre ordinateur sur le port 8000.
|
||||||
|
|||||||
@@ -8,9 +8,24 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
margin: 0;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background-color: var(--main-color);
|
background-color: var(--main-color);
|
||||||
|
|
||||||
|
#closedContainer {
|
||||||
|
padding: 15px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cute-texture {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#closedContainer{
|
#closedContainer{
|
||||||
|
|||||||
@@ -10,11 +10,49 @@
|
|||||||
<title>Le LOL est Fermé ...</title>
|
<title>Le LOL est Fermé ...</title>
|
||||||
</head>
|
</head>
|
||||||
<body id="lolIsClosed">
|
<body id="lolIsClosed">
|
||||||
|
<canvas data-cute-texture class="cute-texture"></canvas>
|
||||||
<main id="closedContainer">
|
<main id="closedContainer">
|
||||||
<section>
|
<section>
|
||||||
<p>Le LOL est actuellement</p>
|
<p>Le LOL est actuellement</p>
|
||||||
<h3 id="lolStatus">fermé ...</h3>
|
<h3 id="lolStatus">fermé ...</h3>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<script type="x-shader/glsl+fragment">
|
||||||
|
// Some random values computed for each frame
|
||||||
|
uniform highp vec4 uRandom;
|
||||||
|
// Size (in logical pixel) of current CuteTexture canvas
|
||||||
|
uniform highp vec2 uCanvasSize;
|
||||||
|
// Coordinates of current pixel being computed from [0,0] (top left) to [1,1] (bottom right)
|
||||||
|
varying highp vec2 vTextureCoord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
highp float pixelSize = 50.0;
|
||||||
|
highp vec2 coords = floor((uCanvasSize.xy * vTextureCoord.xy) / pixelSize);
|
||||||
|
highp float lum = (noise1(uRandom.yz+coords) * 0.631372549);
|
||||||
|
lowp float opacity = 0.5;
|
||||||
|
|
||||||
|
highp vec4 baseColor = vec4(
|
||||||
|
0.0,
|
||||||
|
lum,
|
||||||
|
lum,
|
||||||
|
1.0
|
||||||
|
);
|
||||||
|
|
||||||
|
highp vec4 rotatedColor = hue_rotate(baseColor, 13.12);
|
||||||
|
|
||||||
|
gl_FragColor = vec4(
|
||||||
|
rotatedColor.x*opacity,
|
||||||
|
rotatedColor.y*opacity,
|
||||||
|
rotatedColor.z*opacity,
|
||||||
|
1.0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import {initCuteTexture} from "../js/cuteTexture.js"
|
||||||
|
initCuteTexture()
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="fr">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<link rel="icon" type="image/svg" href="/assets/favicon_open.svg" />
|
|
||||||
<style>
|
|
||||||
:root {
|
|
||||||
--back-color: #00FF44;
|
|
||||||
--main-color: #000000;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<title>Le LOL est Ouvert</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<main>
|
|
||||||
<p id="statusText">Ouvert !</p>
|
|
||||||
</main>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -11,11 +11,60 @@
|
|||||||
<title>Le LOL est Ouvert</title>
|
<title>Le LOL est Ouvert</title>
|
||||||
</head>
|
</head>
|
||||||
<body id="lolIsOpen">
|
<body id="lolIsOpen">
|
||||||
|
<video data-cute-texture-video class="cute-texture-video" src="/assets/video/background.webm" autoplay muted loop ></video>
|
||||||
|
<canvas data-cute-texture class="cute-texture"></canvas>
|
||||||
<main id="openContainer">
|
<main id="openContainer">
|
||||||
<section>
|
<section>
|
||||||
<p>Le LOL est actuellement</p>
|
<p>Le LOL est actuellement</p>
|
||||||
<h3 id="lolStatus">ouvert !</h3>
|
<h3 id="lolStatus">ouvert !</h3>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<script type="x-shader/glsl+fragment">
|
||||||
|
// Some random values computed for each frame
|
||||||
|
uniform highp vec4 uRandom;
|
||||||
|
// Size (in logical pixel) of current CuteTexture canvas
|
||||||
|
uniform highp vec2 uCanvasSize;
|
||||||
|
// Number of seconds since canvas was initialized
|
||||||
|
uniform lowp float uTime;
|
||||||
|
// Current frame of first video with [data-cute-texture-video] attribute
|
||||||
|
uniform sampler2D uVideo;
|
||||||
|
// Coordinates of current pixel being computed from [0,0] (top left) to [1,1] (bottom right)
|
||||||
|
varying highp vec2 vTextureCoord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
highp float waveSize = 10.0;
|
||||||
|
highp vec2 coords = (uCanvasSize.xy * vTextureCoord.xy) / waveSize;
|
||||||
|
highp float lum = abs(sin(coords.y-uTime)) + 0.631372549;
|
||||||
|
lowp float opacity = 0.5;
|
||||||
|
|
||||||
|
gl_FragColor = vec4(
|
||||||
|
0.0,
|
||||||
|
lum,
|
||||||
|
0.0,
|
||||||
|
1.0
|
||||||
|
);
|
||||||
|
|
||||||
|
gl_FragColor = hue_rotate(gl_FragColor, 13.12);
|
||||||
|
|
||||||
|
gl_FragColor = vec4(
|
||||||
|
gl_FragColor.rgb * opacity,
|
||||||
|
1.0
|
||||||
|
);
|
||||||
|
|
||||||
|
highp vec2 videoCoords = (vTextureCoord.xy / 1.5) + vec2(0, 0.25);
|
||||||
|
highp vec4 videoPix = clamp(( grayscale(texture2D(uVideo, videoCoords), 1.0) * 2.0 ), vec4(0), vec4(1));
|
||||||
|
videoPix *= 0.777;
|
||||||
|
|
||||||
|
gl_FragColor = abs( videoPix - gl_FragColor );
|
||||||
|
|
||||||
|
gl_FragColor.a = 1.0;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import {initCuteTexture} from "../js/cuteTexture.js"
|
||||||
|
initCuteTexture()
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -29,13 +29,29 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*======================= Page Statut LOL ouvert ===*/
|
/*======================= Page Statut LOL ouvert ===*/
|
||||||
|
|
||||||
#lolIsOpen {
|
#lolIsOpen {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
margin: 0;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background-color: var(--main-color);
|
background-color: var(--main-color);
|
||||||
|
|
||||||
|
#openContainer {
|
||||||
|
padding: 15px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cute-texture, .cute-texture-video {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#openContainer{
|
#openContainer{
|
||||||
|
|||||||
Executable
+23
@@ -0,0 +1,23 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ "$#" = "0" ]; then
|
||||||
|
echo "Usage set-status.sh [new-status]"
|
||||||
|
echo ""
|
||||||
|
echo "[new-status] can be \"opened\" or \"closed\""
|
||||||
|
echo ""
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#SPACE_JSON=/var/www/html/spaceapi/space.json
|
||||||
|
|
||||||
|
if [ "$1" = "opened" ]; then
|
||||||
|
ln -sf "index.ouvert.html" "$(dirname $0)/index.html"
|
||||||
|
#jq "setpath([\"state\", \"open\"]; true) | setpath([\"state\", \"lastchange\"]; $(date +%s))" < $SPACE_JSON > $SPACE_JSON.new &&
|
||||||
|
#mv -f $SPACE_JSON.new $SPACE_JSON
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$1" = "closed" ]; then
|
||||||
|
ln -sf "index.fermé.html" "$(dirname $0)/index.html"
|
||||||
|
#jq "setpath([\"state\", \"open\"]; false) | setpath([\"state\", \"lastchange\"]; $(date +%s))" < $SPACE_JSON > $SPACE_JSON.new &&
|
||||||
|
#mv -f $SPACE_JSON.new $SPACE_JSON
|
||||||
|
fi
|
||||||
+111
-14
@@ -104,12 +104,7 @@ video{
|
|||||||
width: auto;
|
width: auto;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
z-index: -30;
|
z-index: -32;
|
||||||
}
|
|
||||||
|
|
||||||
.closedBackground{
|
|
||||||
opacity: .5;
|
|
||||||
mix-blend-mode: exclusion;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.openBackground{
|
.openBackground{
|
||||||
@@ -217,9 +212,14 @@ main{
|
|||||||
color: var(--back-color);
|
color: var(--back-color);
|
||||||
justify-self: end;
|
justify-self: end;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
#planContainer{
|
#planContainer{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: max-content;
|
||||||
margin-left: 13.12px;
|
margin-left: 13.12px;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
@@ -369,9 +369,110 @@ main{
|
|||||||
padding-left: 16.1px;
|
padding-left: 16.1px;
|
||||||
padding-right: 33px;
|
padding-right: 33px;
|
||||||
|
|
||||||
.iconBtnStyle{
|
summary {
|
||||||
width: 33px;
|
padding-left: 33px;
|
||||||
|
padding-top: 7.77px;
|
||||||
|
height:50px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
|
||||||
|
|
||||||
|
.title{
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
width: 77%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 16.1px;
|
||||||
|
padding-top: 3.33px;
|
||||||
|
padding-left: 33px;
|
||||||
|
margin-bottom: 13.12px;
|
||||||
|
|
||||||
|
font-family: 'velvelyne';
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 16.1px;
|
||||||
|
|
||||||
|
p{
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4{
|
||||||
|
margin: 0;
|
||||||
|
font-family: 'velvelyne';
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 16.1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.resume{
|
||||||
|
font-family: 'velvelyne';
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 13.12px;
|
||||||
|
text-transform: none;
|
||||||
|
padding-left: 33px;
|
||||||
|
width: 90%;
|
||||||
height: 33px;
|
height: 33px;
|
||||||
|
|
||||||
|
border-radius: 16.1px;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: thin;
|
||||||
|
border-color: var(--main-color);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.description{
|
||||||
|
margin-left: 44px;
|
||||||
|
margin-bottom: 33px;
|
||||||
|
height: auto;
|
||||||
|
width: 95%;
|
||||||
|
overflow-y: scroll;
|
||||||
|
background-color: var(--back-color);
|
||||||
|
border-radius: 16.1px;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: thin;
|
||||||
|
border-color: var(--main-color);
|
||||||
|
|
||||||
|
margin-top: 44px;
|
||||||
|
padding: 13.12px;
|
||||||
|
padding-right: 33px;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
|
||||||
|
font-size: 13.12px;
|
||||||
|
font-family: 'velvelyne';
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
details:hover{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
details[open]{
|
||||||
|
height: auto;
|
||||||
|
border-color: var(--back-color);
|
||||||
|
color: var(--back-color);
|
||||||
|
background-color: var(--main-color);
|
||||||
|
|
||||||
|
summary .resume{
|
||||||
|
border-color: var(--back-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
::marker{
|
||||||
|
color: var(--main-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.description{
|
||||||
|
color: var(--main-color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -399,9 +500,5 @@ canvas{
|
|||||||
width: auto;
|
width: auto;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
z-index: -32;
|
z-index: -30;
|
||||||
|
|
||||||
opacity: .5;
|
|
||||||
filter: hue-rotate(13.12deg);
|
|
||||||
mix-blend-mode: multiply;
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user