Bump ECMAPortal to v1.3
This commit is contained in:
parent
a93611fc43
commit
125e9f8451
|
@ -21,8 +21,7 @@
|
||||||
<link href="/styles/styles.css" rel="stylesheet" />
|
<link href="/styles/styles.css" rel="stylesheet" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<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">
|
<div class="container">
|
||||||
<main>
|
<main>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
|
@ -184,8 +183,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="bottom-right">
|
<div class="bottom-right">
|
||||||
<img src="/images/enderman.gif" alt="Hello!">
|
<img src="/images/enderman.gif" alt="Hello!">
|
||||||
</div>
|
</div>-->
|
||||||
|
<script src="/scripts/portal-min.js" type="text/javascript"></script>
|
||||||
<script src="/scripts/portal-min.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,5 @@
|
||||||
class Portal {
|
class Portal {
|
||||||
constructor(canvas, resourceDir) {
|
constructor(resourceDir = '/', animate = true, randomize = false) {
|
||||||
|
|
||||||
// Vertex shader.
|
// Vertex shader.
|
||||||
const vglsl = `#version 300 es
|
const vglsl = `#version 300 es
|
||||||
|
|
||||||
|
@ -104,36 +103,51 @@ class Portal {
|
||||||
];
|
];
|
||||||
|
|
||||||
// Version & notice.
|
// Version & notice.
|
||||||
this.version = '1.1';
|
this.version = '1.3';
|
||||||
this.notice();
|
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');
|
this.gl = this.canvas.getContext('webgl2');
|
||||||
|
|
||||||
// If WebGL isn't part of available features, fail.
|
// If WebGL isn't part of available features, fail.
|
||||||
if (!this.gl) {
|
if (!this.gl) {
|
||||||
alert("Unable to initialize WebGL 2. Your browser or machine may not support it.");
|
alert("Unable to initialize WebGL 2.\nThe website will lack parallax animation.");
|
||||||
return;
|
console.error("Unable to initialize WebGL 2. Your browser or machine may not support it.");
|
||||||
|
|
||||||
|
return this.destroyCanvas();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size the canvas to window initially.
|
// Set default speed.
|
||||||
this.canvas.width = window.innerWidth;
|
this.speed = 1;
|
||||||
this.canvas.height = window.innerHeight;
|
this.initialSpeed = this.speed;
|
||||||
|
|
||||||
// Get current tick and slightly randomize it.
|
// It's integral for the animation to tie it to the current time, else it will be tied to the framerate.
|
||||||
this.time = Date.now() - this.randomRange(60 * 1000, 180 * 1000);
|
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.
|
// Build shaders.
|
||||||
this.prog = this.build(vglsl, fglsl);
|
this.prog = this.build(vglsl, fglsl);
|
||||||
|
|
||||||
if (!this.prog) {
|
if (!this.prog) {
|
||||||
alert("Failed to compile WebGL 2 shaders.\nOpen the developer console for debug output.");
|
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.
|
// Bind class context to the resize handler, pass it to the event bus.
|
||||||
window.addEventListener('resize', this.resize.bind(this));
|
window.addEventListener('resize', this.resize.bind(this));
|
||||||
|
|
||||||
|
// Set the clickTime to null for the onClick event.
|
||||||
|
this.clickTime = null;
|
||||||
|
|
||||||
// Create image loading promises.
|
// Create image loading promises.
|
||||||
this.promises = images.map(image => this.loadImage(image));
|
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() {
|
notice() {
|
||||||
console.log(
|
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`,
|
`%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) {
|
randomRange(min, max) {
|
||||||
min = Math.ceil(min);
|
return Math.random() * (max - min) + min;
|
||||||
max = Math.floor(max);
|
|
||||||
|
|
||||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
build(vshSource, fshSource) {
|
build(vshSource, fshSource) {
|
||||||
|
@ -283,7 +325,7 @@ class Portal {
|
||||||
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);
|
||||||
|
|
||||||
let vertexTexBuffer = this.gl.createBuffer();
|
let vertexTexBuffer = this.gl.createBuffer();
|
||||||
let texcoords = new Float32Array([
|
let texCoords = new Float32Array([
|
||||||
0.0, 1.0,
|
0.0, 1.0,
|
||||||
1.0, 1.0,
|
1.0, 1.0,
|
||||||
1.0, 0.0,
|
1.0, 0.0,
|
||||||
|
@ -293,7 +335,7 @@ class Portal {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, vertexTexBuffer);
|
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);
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);
|
||||||
|
|
||||||
let vertexPosLocation = 0;
|
let vertexPosLocation = 0;
|
||||||
|
@ -320,14 +362,24 @@ class Portal {
|
||||||
0.0, 0.0, 0.0, 1.0
|
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.modelViewMatrix, false, identityMatrix);
|
||||||
this.gl.uniformMatrix4fv(this.uniforms.projectionMatrix, false, identityMatrix);
|
this.gl.uniformMatrix4fv(this.uniforms.projectionMatrix, false, identityMatrix);
|
||||||
|
|
||||||
this.gl.uniform2f(this.uniforms.resolution, this.canvas.clientWidth, this.canvas.clientHeight);
|
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.sky, 0);
|
||||||
this.gl.uniform1i(this.uniforms.particles, 1);
|
this.gl.uniform1i(this.uniforms.particles, 1);
|
||||||
|
|
||||||
|
@ -338,11 +390,15 @@ class Portal {
|
||||||
// Make sure the viewport matches the size of the canvas' drawingBuffer.
|
// Make sure the viewport matches the size of the canvas' drawingBuffer.
|
||||||
this.gl.viewport(0, 0, this.gl.drawingBufferWidth, this.gl.drawingBufferHeight);
|
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.
|
// Run the scene.
|
||||||
this.scene();
|
this.scene();
|
||||||
|
|
||||||
// Request the next animation frame.
|
// Request the next animation frame if animation is enabled.
|
||||||
requestAnimationFrame(this.render.bind(this));
|
if (this.animate) requestAnimationFrame(this.render.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
resize() {
|
resize() {
|
||||||
|
@ -356,21 +412,42 @@ class Portal {
|
||||||
this.canvas.width = width;
|
this.canvas.width = width;
|
||||||
this.canvas.height = height;
|
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() {
|
destruct() {
|
||||||
// Doesn't need to be called usually, the garbage collector should do its job.
|
// 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:
|
// The method isn't implemented, but the pseudocode is as follows:
|
||||||
|
|
||||||
this.gl.deleteBuffer(vertexPosBuffer);
|
// this.gl.deleteBuffer(vertexPosBuffer);
|
||||||
this.gl.deleteBuffer(vertexTexBuffer);
|
// this.gl.deleteBuffer(vertexTexBuffer);
|
||||||
|
|
||||||
this.gl.deleteSampler(sampler);
|
// this.gl.deleteSampler(sampler);
|
||||||
this.gl.deleteTexture(texture);
|
// this.gl.deleteTexture(texture);
|
||||||
|
|
||||||
this.gl.deleteProgram(this.prog);
|
this.gl.deleteProgram(this.prog);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const canvas = document.querySelector('#ecmaportal');
|
const portal = new Portal('/images/portal', true, true);
|
||||||
const portal = new Portal(canvas, '/images/portal');
|
|
||||||
|
|
|
@ -3,10 +3,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-color: black;
|
background-color: rgb(6, 25, 28);
|
||||||
|
background-image: url("/images/portal/sky.png");
|
||||||
background-attachment: fixed;
|
background-attachment: fixed;
|
||||||
|
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
|
background-blend-mode: multiply;
|
||||||
|
image-rendering: pixelated;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
|
||||||
color: white;
|
color: white;
|
||||||
|
@ -18,7 +21,7 @@ canvas.portal {
|
||||||
display: block;
|
display: block;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|
||||||
top: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
|
@ -179,6 +182,9 @@ div.bottom-right {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
|
||||||
opacity: 30%;
|
opacity: 30%;
|
||||||
|
|
||||||
|
-webkit-transition: ease 0.3s;
|
||||||
|
transition: 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.bottom-right:hover {
|
div.bottom-right:hover {
|
||||||
|
|
Loading…
Reference in New Issue