Bump ECMAPortal to v1.3

This commit is contained in:
endermanch committed 2023-11-06 00:59:31 +03:00
1 parent a93611fc43
commit 125e9f8451
4 files changed
+117 -36

No files matched your search

+3 -5
View File
@@ -21,8 +21,7 @@
<link href="/styles/styles.css" rel="stylesheet" />
</head>
<body>
<canvas class="portal" id="ecmaportal">Your browser does not seem to support HTML canvas.</canvas>
<div class="body-wrapper">
<!--<div class="body-wrapper">
<div class="container">
<main>
<div class="text">
@@ -184,8 +183,7 @@
</div>
<div class="bottom-right">
<img src="/images/enderman.gif" alt="Hello!">
</div>
<script src="/scripts/portal-min.js"></script>
</div>-->
<script src="/scripts/portal-min.js" type="text/javascript"></script>
</body>
</html>
+1 -1
View File
@@ -1 +1 @@
class Portal{constructor(t,e){const i=[`${"/"!==e?e:""}/sky.png`,`${"/"!==e?e:""}/portal.png`];this.version="1.1",this.notice(),this.canvas=t,this.gl=this.canvas.getContext("webgl2"),this.gl?(this.canvas.width=window.innerWidth,this.canvas.height=window.innerHeight,this.time=Date.now()-this.randomRange(6e4,18e4),this.prog=this.build("#version 300 es\n\n\t\tlayout (location = 0) in vec3 Position;\n\n\t\tuniform mat4 modelViewMatrix;\n\t\tuniform mat4 projectionMatrix;\n\t\t\n\t\tuniform vec2 canvasResolution;\n\n\t\tvec4 projection_from_position(vec4 position) {\n\t\t\tvec4 projection = position * 0.5;\n\t\t\tprojection.xy = vec2(projection.x + projection.w, projection.y + projection.w);\n\t\t\tprojection.zw = position.zw;\n\t\t\treturn projection;\n\t\t}\n\n\t\tout vec4 texProj0;\n\n\t\tvoid main() {\n\t\t\tgl_Position = projectionMatrix * modelViewMatrix * vec4(Position, 1.0);\n\n\t\t\ttexProj0 = projection_from_position(gl_Position);\n\t\t\ttexProj0 = vec4(texProj0.xy * canvasResolution / max(canvasResolution.x, canvasResolution.y), texProj0.zw);\n\t\t}","#version 300 es\n\n\t\tprecision highp float;\n\n\t\tuniform sampler2D sky;\n\t\tuniform sampler2D particles;\n\t\tuniform float dt;\n\n\t\tin vec4 texProj0;\n\n\t\tconst int LAYERS = 15;\n\t\tconst vec3 COLORS[] = vec3[](\n\t\t\tvec3(0.022087, 0.098399, 0.110818),\n\t\t\tvec3(0.011892, 0.095924, 0.089485),\n\t\t\tvec3(0.027636, 0.101689, 0.100326),\n\t\t\tvec3(0.046564, 0.109883, 0.114838),\n\t\t\tvec3(0.064901, 0.117696, 0.097189),\n\t\t\tvec3(0.063761, 0.086895, 0.123646),\n\t\t\tvec3(0.084817, 0.111994, 0.166380),\n\t\t\tvec3(0.097489, 0.154120, 0.091064),\n\t\t\tvec3(0.106152, 0.131144, 0.195191),\n\t\t\tvec3(0.097721, 0.110188, 0.187229),\n\t\t\tvec3(0.133516, 0.138278, 0.148582),\n\t\t\tvec3(0.070006, 0.243332, 0.235792),\n\t\t\tvec3(0.196766, 0.142899, 0.214696),\n\t\t\tvec3(0.047281, 0.315338, 0.321970),\n\t\t\tvec3(0.204675, 0.390010, 0.302066),\n\t\t\tvec3(0.080955, 0.314821, 0.661491)\n\t\t);\n\n\t\tconst mat4 SCALE_TRANSLATE = mat4(\n\t\t\t0.5, 0.0, 0.0, 0.25,\n\t\t\t0.0, 0.5, 0.0, 0.25,\n\t\t\t0.0, 0.0, 1.0, 0.0,\n\t\t\t0.0, 0.0, 0.0, 1.0\n\t\t);\n\n\t\tmat2 mat2_rotate_z(float radians) {\n\t\t\treturn mat2(\n\t\t\t\tcos(radians), -sin(radians),\n\t\t\t\tsin(radians), cos(radians)\n\t\t\t);\n\t\t}\n\n\t\tmat4 portal_layer(float layer) {\n\t\t\tmat4 translate = mat4(\n\t\t\t\t1.0, 0.0, 0.0, 17.0 / layer,\n\t\t\t\t0.0, 1.0, 0.0, (2.0 + layer / 1.5) * (dt * 1.5),\n\t\t\t\t0.0, 0.0, 1.0, 0.0,\n\t\t\t\t0.0, 0.0, 0.0, 1.0\n\t\t\t);\n\n\t\t\tmat2 rotate = mat2_rotate_z(radians((layer * layer * 4321.0 + layer * 9.0) * 2.0));\n\n\t\t\tmat2 scale = mat2((4.5 - layer / 4.0) * 2.0);\n\n\t\t\treturn mat4(scale * rotate) * translate * SCALE_TRANSLATE;\n\t\t}\n\n\t\tout vec4 fragColor;\n\n\t\tvoid main() {\n\t\t\tvec3 color = textureProj(sky, texProj0).rgb * COLORS[0];\n\t\t\t\n\t\t\tfor (int i = 0; i < LAYERS; i++) {\n\t\t\t\tcolor += textureProj(particles, texProj0 * portal_layer(float(i + 1))).rgb * COLORS[i];\n\t\t\t}\n\t\t\t\n\t\t\tfragColor = vec4(color, 1.0);\n\t\t}"),this.prog?(window.addEventListener("resize",this.resize.bind(this)),this.promises=i.map((t=>this.loadImage(t))),this.initialize(),Promise.all(this.promises).then((t=>{t.forEach(((t,e)=>this.loadResource(t,e))),this.gl.useProgram(this.prog),this.render()}))):alert("Failed to compile WebGL 2 shaders.\nOpen the developer console for debug output.")):alert("Unable to initialize WebGL 2. Your browser or machine may not support it.")}notice(){console.log(`%c \u2587%c\u2587%c\u2587 %c\u2587%c\u2587%c\u2587 %c // ECMAPortal v${this.version} by Endermanch & WiPet\n\n\thttps://enderman.ch\n\thttps://go.enderman.ch/wipet`,"color: #E58EFF","color: #D52DFF","color: #E58EFF","color: #E58EFF","color: #D52DFF","color: #E58EFF","color: #008000"),console.log("If any errors occur below, please send a screenshot of them my way!\n\t%[email protected]","color: #87CEFA")}randomRange(t,e){return t=Math.ceil(t),e=Math.floor(e),Math.floor(Math.random()*(e-t+1))+t}build(t,e){let i=this.gl.createShader(this.gl.VERTEX_SHADER);if(this.gl.shaderSource(i,t),this.gl.compileShader(i),!this.gl.getShaderParameter(i,this.gl.COMPILE_STATUS))return console.error(this.gl.getShaderInfoLog(i)),!1;let r=this.gl.createShader(this.gl.FRAGMENT_SHADER);if(this.gl.shaderSource(r,e),this.gl.compileShader(r),!this.gl.getShaderParameter(r,this.gl.COMPILE_STATUS))return console.error(this.gl.getShaderInfoLog(r)),!1;let n=this.gl.createProgram();return this.gl.attachShader(n,i),this.gl.attachShader(n,r),this.gl.linkProgram(n),this.gl.getProgramParameter(n,this.gl.LINK_STATUS)?n:(console.error(this.gl.getProgramInfoLog(n)),!1)}loadImage(t){return new Promise(((e,i)=>{let r=new Image;r.onload=()=>e(r),r.onerror=i,r.src=t}))}loadResource(t,e){const i=[this.gl.CLAMP_TO_EDGE,this.gl.MIRRORED_REPEAT];let r=this.gl.createSampler();this.gl.samplerParameteri(r,this.gl.TEXTURE_WRAP_S,i[e]),this.gl.samplerParameteri(r,this.gl.TEXTURE_WRAP_T,iLine truncated
class Portal{constructor(t="/",i=!0,e=!1){const n=[`${"/"!==t?t:""}/sky.png`,`${"/"!==t?t:""}/portal.png`];return this.version="1.3",this.notice(),this.animate=i,this.randomize=e,this.canvas=this.createCanvas(),this.gl=this.canvas.getContext("webgl2"),this.gl?(this.speed=1,this.initialSpeed=this.speed,this.currentTime=Date.now(),this.pauseTime=null,this.tick=(this.animate&&e)*this.randomRange(0,10),this.prog=this.build("#version 300 es\n\n\t\tlayout (location = 0) in vec3 Position;\n\n\t\tuniform mat4 modelViewMatrix;\n\t\tuniform mat4 projectionMatrix;\n\t\t\n\t\tuniform vec2 canvasResolution;\n\n\t\tvec4 projection_from_position(vec4 position) {\n\t\t\tvec4 projection = position * 0.5;\n\t\t\tprojection.xy = vec2(projection.x + projection.w, projection.y + projection.w);\n\t\t\tprojection.zw = position.zw;\n\t\t\treturn projection;\n\t\t}\n\n\t\tout vec4 texProj0;\n\n\t\tvoid main() {\n\t\t\tgl_Position = projectionMatrix * modelViewMatrix * vec4(Position, 1.0);\n\n\t\t\ttexProj0 = projection_from_position(gl_Position);\n\t\t\ttexProj0 = vec4(texProj0.xy * canvasResolution / max(canvasResolution.x, canvasResolution.y), texProj0.zw);\n\t\t}","#version 300 es\n\n\t\tprecision highp float;\n\n\t\tuniform sampler2D sky;\n\t\tuniform sampler2D particles;\n\t\tuniform float dt;\n\n\t\tin vec4 texProj0;\n\n\t\tconst int LAYERS = 15;\n\t\tconst vec3 COLORS[] = vec3[](\n\t\t\tvec3(0.022087, 0.098399, 0.110818),\n\t\t\tvec3(0.011892, 0.095924, 0.089485),\n\t\t\tvec3(0.027636, 0.101689, 0.100326),\n\t\t\tvec3(0.046564, 0.109883, 0.114838),\n\t\t\tvec3(0.064901, 0.117696, 0.097189),\n\t\t\tvec3(0.063761, 0.086895, 0.123646),\n\t\t\tvec3(0.084817, 0.111994, 0.166380),\n\t\t\tvec3(0.097489, 0.154120, 0.091064),\n\t\t\tvec3(0.106152, 0.131144, 0.195191),\n\t\t\tvec3(0.097721, 0.110188, 0.187229),\n\t\t\tvec3(0.133516, 0.138278, 0.148582),\n\t\t\tvec3(0.070006, 0.243332, 0.235792),\n\t\t\tvec3(0.196766, 0.142899, 0.214696),\n\t\t\tvec3(0.047281, 0.315338, 0.321970),\n\t\t\tvec3(0.204675, 0.390010, 0.302066),\n\t\t\tvec3(0.080955, 0.314821, 0.661491)\n\t\t);\n\n\t\tconst mat4 SCALE_TRANSLATE = mat4(\n\t\t\t0.5, 0.0, 0.0, 0.25,\n\t\t\t0.0, 0.5, 0.0, 0.25,\n\t\t\t0.0, 0.0, 1.0, 0.0,\n\t\t\t0.0, 0.0, 0.0, 1.0\n\t\t);\n\n\t\tmat2 mat2_rotate_z(float radians) {\n\t\t\treturn mat2(\n\t\t\t\tcos(radians), -sin(radians),\n\t\t\t\tsin(radians), cos(radians)\n\t\t\t);\n\t\t}\n\n\t\tmat4 portal_layer(float layer) {\n\t\t\tmat4 translate = mat4(\n\t\t\t\t1.0, 0.0, 0.0, 17.0 / layer,\n\t\t\t\t0.0, 1.0, 0.0, (2.0 + layer / 1.5) * (dt * 1.5),\n\t\t\t\t0.0, 0.0, 1.0, 0.0,\n\t\t\t\t0.0, 0.0, 0.0, 1.0\n\t\t\t);\n\n\t\t\tmat2 rotate = mat2_rotate_z(radians((layer * layer * 4321.0 + layer * 9.0) * 2.0));\n\n\t\t\tmat2 scale = mat2((4.5 - layer / 4.0) * 2.0);\n\n\t\t\treturn mat4(scale * rotate) * translate * SCALE_TRANSLATE;\n\t\t}\n\n\t\tout vec4 fragColor;\n\n\t\tvoid main() {\n\t\t\tvec3 color = textureProj(sky, texProj0).rgb * COLORS[0];\n\t\t\t\n\t\t\tfor (int i = 0; i < LAYERS; i++) {\n\t\t\t\tcolor += textureProj(particles, texProj0 * portal_layer(float(i + 1))).rgb * COLORS[i];\n\t\t\t}\n\t\t\t\n\t\t\tfragColor = vec4(color, 1.0);\n\t\t}"),this.prog?(window.addEventListener("resize",this.resize.bind(this)),this.clickTime=null,this.promises=n.map((t=>this.loadImage(t))),this.initialize(),void Promise.all(this.promises).then((t=>{t.forEach(((t,i)=>this.loadResource(t,i))),this.gl.useProgram(this.prog),this.render()}))):(alert("Failed to compile WebGL 2 shaders.\nOpen the developer console for debug output."),this.destroyCanvas())):(alert("Unable to initialize WebGL 2.\nThe website will lack parallax animation."),console.error("Unable to initialize WebGL 2. Your browser or machine may not support it."),this.destroyCanvas())}createCanvas(){let t=document.createElement("canvas");return t.width=window.innerWidth,t.height=window.innerHeight,t.style.display="block",t.style.position="fixed",t.style.bottom="0",t.style.left="0",t.style.zIndex="-1",t.innerHTML="Your browser does not support the canvas element, which is required for animation.",t.classList.add("ecmaportal"),t.id="ecmaportal",this.animate&&(t.onclick=()=>this.clickTime=Date.now()),document.body.appendChild(t),t}destroyCanvas(){document.body.removeChild(this.canvas)}notice(){console.log(`%c \u2587%c\u2587%c\u2587 %c\u2587%c\u2587%c\u2587 %c // ECMAPortal v${this.version} by Endermanch & WiPet\n\n\thttps://enderman.ch\n\thttps://go.enderman.ch/wipet`,"color: #E58EFF","color: #D52DFF","color: #E58EFF","color: #E58EFF","color: #D52DFF","color: #E58EFF","color: #008000"),console.log("If any errors occur below, please send a screenshot of them my way!\n\t%[email protected]","color: #87CEFA")}randomRange(t,i){return Math.random()*(i-t)+t}build(t,i){let e=this.gl.createShader(this.gl.VERTEX_SHADER);if(this.gl.shaderSource(e,t),this.gl.compileShader(e),!this.gl.getShaderParameter(e,this.gl.COMPILE_STATUS))return console.error(this.gl.getShaderInfoLog(e)),!1;let n=this.gl.createShader(Line truncated
+105 -28
View File
@@ -1,6 +1,5 @@
class Portal {
constructor(canvas, resourceDir) {
constructor(resourceDir = '/', animate = true, randomize = false) {
// Vertex shader.
const vglsl = `#version 300 es
@@ -104,36 +103,51 @@ class Portal {
];
// Version & notice.
this.version = '1.1';
this.version = '1.3';
this.notice();
this.canvas = canvas;
// Set properties.
this.animate = animate;
this.randomize = randomize;
// Create a canvas and acquire its context.
this.canvas = this.createCanvas();
this.gl = this.canvas.getContext('webgl2');
// If WebGL isn't part of available features, fail.
if (!this.gl) {
alert("Unable to initialize WebGL 2. Your browser or machine may not support it.");
return;
alert("Unable to initialize WebGL 2.\nThe website will lack parallax animation.");
console.error("Unable to initialize WebGL 2. Your browser or machine may not support it.");
return this.destroyCanvas();
}
// Size the canvas to window initially.
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight;
// Set default speed.
this.speed = 1;
this.initialSpeed = this.speed;
// Get current tick and slightly randomize it.
this.time = Date.now() - this.randomRange(60 * 1000, 180 * 1000);
// It's integral for the animation to tie it to the current time, else it will be tied to the framerate.
this.currentTime = Date.now();
this.pauseTime = null;
// Get current tick and slightly randomize it if animation is enabled.
this.tick = (this.animate && randomize) * this.randomRange(0, 10);
// Build shaders.
this.prog = this.build(vglsl, fglsl);
if (!this.prog) {
alert("Failed to compile WebGL 2 shaders.\nOpen the developer console for debug output.");
return;
return this.destroyCanvas();
}
// Bind class context to the resize handler, pass it to the event bus.
window.addEventListener('resize', this.resize.bind(this));
// Set the clickTime to null for the onClick event.
this.clickTime = null;
// Create image loading promises.
this.promises = images.map(image => this.loadImage(image));
@@ -154,6 +168,37 @@ class Portal {
});
}
createCanvas() {
let canvas = document.createElement('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.style.display = 'block';
canvas.style.position = 'fixed';
canvas.style.bottom = '0';
canvas.style.left = '0';
canvas.style.zIndex = '-1';
canvas.innerHTML = 'Your browser does not support the canvas element, which is required for animation.';
// Add a class for subclassing support.
canvas.classList.add('ecmaportal');
canvas.id = 'ecmaportal';
if (this.animate) {
canvas.onclick = () => this.clickTime = Date.now();
}
document.body.appendChild(canvas);
return canvas;
}
destroyCanvas() {
document.body.removeChild(this.canvas);
}
notice() {
console.log(
`%c \u2587%c\u2587%c\u2587 %c\u2587%c\u2587%c\u2587 %c // ECMAPortal v${this.version} by Endermanch & WiPet\n\n\thttps://enderman.ch\n\thttps://go.enderman.ch/wipet`,
@@ -173,10 +218,7 @@ class Portal {
}
randomRange(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
return Math.random() * (max - min) + min;
}
build(vshSource, fshSource) {
@@ -283,7 +325,7 @@ class Portal {
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);
let vertexTexBuffer = this.gl.createBuffer();
let texcoords = new Float32Array([
let texCoords = new Float32Array([
0.0, 1.0,
1.0, 1.0,
1.0, 0.0,
@@ -293,7 +335,7 @@ class Portal {
]);
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, vertexTexBuffer);
this.gl.bufferData(this.gl.ARRAY_BUFFER, texcoords, this.gl.STATIC_DRAW);
this.gl.bufferData(this.gl.ARRAY_BUFFER, texCoords, this.gl.STATIC_DRAW);
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);
let vertexPosLocation = 0;
@@ -320,14 +362,24 @@ class Portal {
0.0, 0.0, 0.0, 1.0
]);
let dt = (Date.now() - this.time) / 1000 / 1000;
if (this.animate) {
// Speed up the animation if the user has clicked.
if (this.clickTime !== null) {
this.speed = 20 - Math.min((Date.now() - this.clickTime) / 100, 20 - this.initialSpeed);
if (this.speed === this.initialSpeed) this.clickTime = null;
}
// R(t) = t + dt (mod 20)
this.tick += this.dt;
this.tick %= 20;
}
this.gl.uniformMatrix4fv(this.uniforms.modelViewMatrix, false, identityMatrix);
this.gl.uniformMatrix4fv(this.uniforms.projectionMatrix, false, identityMatrix);
this.gl.uniform2f(this.uniforms.resolution, this.canvas.clientWidth, this.canvas.clientHeight);
this.gl.uniform1f(this.uniforms.dt, dt);
this.gl.uniform1f(this.uniforms.dt, this.tick);
this.gl.uniform1i(this.uniforms.sky, 0);
this.gl.uniform1i(this.uniforms.particles, 1);
@@ -338,11 +390,15 @@ class Portal {
// Make sure the viewport matches the size of the canvas' drawingBuffer.
this.gl.viewport(0, 0, this.gl.drawingBufferWidth, this.gl.drawingBufferHeight);
// Calculate the delta time.
this.dt = (Date.now() - this.currentTime) * this.speed / 1000000;
this.currentTime = Date.now();
// Run the scene.
this.scene();
// Request the next animation frame.
requestAnimationFrame(this.render.bind(this));
// Request the next animation frame if animation is enabled.
if (this.animate) requestAnimationFrame(this.render.bind(this));
}
resize() {
@@ -356,21 +412,42 @@ class Portal {
this.canvas.width = width;
this.canvas.height = height;
}
// If we have animation disabled, we still have to re-render the scene once on resize.
if (!this.animate) requestAnimationFrame(this.render.bind(this));
}
continue() {
if (this.pauseTime === null) return;
// Add the time that has passed since the pause to the current time.
this.currentTime += Date.now() - this.pauseTime;
this.pauseTime = null;
this.animate = true;
this.render();
}
pause() {
if (this.pauseTime !== null) return;
// Save the pause time to calculate the delta time.
this.pauseTime = Date.now();
this.animate = false;
}
destruct() {
// Doesn't need to be called usually, the garbage collector should do its job.
// The method isn't implemented, but the pseudocode is as follows:
this.gl.deleteBuffer(vertexPosBuffer);
this.gl.deleteBuffer(vertexTexBuffer);
// this.gl.deleteBuffer(vertexPosBuffer);
// this.gl.deleteBuffer(vertexTexBuffer);
this.gl.deleteSampler(sampler);
this.gl.deleteTexture(texture);
// this.gl.deleteSampler(sampler);
// this.gl.deleteTexture(texture);
this.gl.deleteProgram(this.prog);
}
}
const canvas = document.querySelector('#ecmaportal');
const portal = new Portal(canvas, '/images/portal');
const portal = new Portal('/images/portal', true, true);
+8 -2
View File
@@ -3,10 +3,13 @@
}
body {
background-color: black;
background-color: rgb(6, 25, 28);
background-image: url("/images/portal/sky.png");
background-attachment: fixed;
background-size: cover;
background-blend-mode: multiply;
image-rendering: pixelated;
background-repeat: no-repeat;
color: white;
@@ -18,7 +21,7 @@ canvas.portal {
display: block;
position: fixed;
top: 0;
bottom: 0;
left: 0;
z-index: -1;
@@ -179,6 +182,9 @@ div.bottom-right {
bottom: 0;
opacity: 30%;
-webkit-transition: ease 0.3s;
transition: 0.3s ease;
}
div.bottom-right:hover {