Fix clouds2d: create own canvas element
The 2D WebGL renderer now creates its own <canvas> overlay inside the target element, instead of expecting the target to already be a canvas. This fixes 'canvas.getContext is not a function' when the target is a <div>.
This commit is contained in:
1 parent
e8df75eebc
commit
c6764fd534
4 files changed
+75
-61
No files matched your search
+37
-30
@@ -927,7 +927,7 @@ else window.SunCalc = SunCalc;
|
|||||||
<body>
|
<body>
|
||||||
<div id="vanta" style="background: #1c1b1f;"></div>
|
<div id="vanta" style="background: #1c1b1f;"></div>
|
||||||
<div id="debug-panel">
|
<div id="debug-panel">
|
||||||
<div class="debug-title expanded" onclick="toggleDebugPanel()"><span>DEBUG - Palette: <span id="dbg-palette-name">Ash</span></span><span class="debug-title-toggle" id="debug-toggle-icon">▼</span></div>
|
<div class="debug-title expanded" onclick="toggleDebugPanel()">\n<span>DEBUG - Palette: <span id="dbg-palette-name">Ash</span></span><span class="debug-title-toggle" id="debug-toggle-icon">▼</span></div>
|
||||||
<div id="debug-content" class="debug-content">
|
<div id="debug-content" class="debug-content">
|
||||||
<div class="debug-controls">
|
<div class="debug-controls">
|
||||||
<select id="dbg-theme-select" class="debug-select" onchange="setDebugTheme(this.value)">
|
<select id="dbg-theme-select" class="debug-select" onchange="setDebugTheme(this.value)">
|
||||||
@@ -1094,6 +1094,7 @@ else window.SunCalc = SunCalc;
|
|||||||
* fragment shader to produce animated cloud-like patterns.
|
* fragment shader to produce animated cloud-like patterns.
|
||||||
*
|
*
|
||||||
* Drop-in replacement for Vanta when CONFIG.renderer === 'clouds2d'.
|
* Drop-in replacement for Vanta when CONFIG.renderer === 'clouds2d'.
|
||||||
|
* Creates its own <canvas> overlay and attaches to the target element.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
@@ -1180,7 +1181,7 @@ var FRAG_SRC = [
|
|||||||
function init(canvas) {
|
function init(canvas) {
|
||||||
var gl = canvas.getContext('webgl', { antialias: false, alpha: false });
|
var gl = canvas.getContext('webgl', { antialias: false, alpha: false });
|
||||||
if (!gl) {
|
if (!gl) {
|
||||||
console.error('clouds2d: WebGL not available, using fallback');
|
console.error('clouds2d: WebGL not available');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var prog = makeProgram(gl, VERT_SRC, FRAG_SRC);
|
var prog = makeProgram(gl, VERT_SRC, FRAG_SRC);
|
||||||
@@ -1209,22 +1210,38 @@ function init(canvas) {
|
|||||||
|
|
||||||
function hex(r, g, b) { return [r/255, g/255, b/255]; }
|
function hex(r, g, b) { return [r/255, g/255, b/255]; }
|
||||||
|
|
||||||
window.clouds2dRender = function(canvas) {
|
window.clouds2dRender = function(target) {
|
||||||
|
if (!target || typeof target !== 'object') {
|
||||||
|
console.error('clouds2d: invalid target');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Get or create the WebGL canvas
|
||||||
|
var canvas = target.__clouds2dCanvas;
|
||||||
if (!canvas) {
|
if (!canvas) {
|
||||||
console.error('clouds2d: no canvas');
|
// Replace the target's content with a WebGL canvas
|
||||||
return;
|
canvas = document.createElement('canvas');
|
||||||
|
canvas.style.position = 'absolute';
|
||||||
|
canvas.style.top = '0';
|
||||||
|
canvas.style.left = '0';
|
||||||
|
canvas.style.width = '100%';
|
||||||
|
canvas.style.height = '100%';
|
||||||
|
// Empty the target and insert the canvas as the only child
|
||||||
|
while (target.firstChild) target.removeChild(target.firstChild);
|
||||||
|
target.appendChild(canvas);
|
||||||
|
target.style.position = 'relative';
|
||||||
|
target.__clouds2dCanvas = canvas;
|
||||||
}
|
}
|
||||||
if (!canvas._clouds2d) {
|
|
||||||
canvas._clouds2d = init(canvas);
|
if (!canvas.__state) {
|
||||||
if (!canvas._clouds2d) {
|
canvas.__state = init(canvas);
|
||||||
// Fallback: render the body background color via 2D canvas
|
if (!canvas.__state) return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
var s = canvas._clouds2d;
|
var s = canvas.__state;
|
||||||
var gl = s.gl;
|
var gl = s.gl;
|
||||||
var w = canvas.width, h = canvas.height;
|
canvas.width = target.clientWidth || window.innerWidth;
|
||||||
gl.viewport(0, 0, w, h);
|
canvas.height = target.clientHeight || window.innerHeight;
|
||||||
|
gl.viewport(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
gl.clearColor(0, 0, 0, 1);
|
gl.clearColor(0, 0, 0, 1);
|
||||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||||
@@ -1234,14 +1251,12 @@ window.clouds2dRender = function(canvas) {
|
|||||||
gl.enableVertexAttribArray(s.aPos);
|
gl.enableVertexAttribArray(s.aPos);
|
||||||
gl.vertexAttribPointer(s.aPos, 2, gl.FLOAT, false, 0, 0);
|
gl.vertexAttribPointer(s.aPos, 2, gl.FLOAT, false, 0, 0);
|
||||||
|
|
||||||
gl.uniform2f(s.uRes, w, h);
|
gl.uniform2f(s.uRes, canvas.width, canvas.height);
|
||||||
gl.uniform1f(s.uTime, performance.now() / 1000);
|
gl.uniform1f(s.uTime, performance.now() / 1000);
|
||||||
|
|
||||||
// Default theme colors (used until renderWeather() sets the right ones)
|
|
||||||
var sky = [0.39, 0.54, 0.78];
|
var sky = [0.39, 0.54, 0.78];
|
||||||
var cloud = [0.43, 0.43, 0.43];
|
var cloud = [0.43, 0.43, 0.43];
|
||||||
var light = [0.89, 0.87, 0.82];
|
var light = [0.89, 0.87, 0.82];
|
||||||
// Pull from the same source as Vanta does
|
|
||||||
var cfg = window.CONFIG || {};
|
var cfg = window.CONFIG || {};
|
||||||
var theme = (cfg.currentWeatherTheme || (cfg.weatherThemes && cfg.weatherThemes[cfg.theme])) || null;
|
var theme = (cfg.currentWeatherTheme || (cfg.weatherThemes && cfg.weatherThemes[cfg.theme])) || null;
|
||||||
if (theme) {
|
if (theme) {
|
||||||
@@ -1257,18 +1272,6 @@ window.clouds2dRender = function(canvas) {
|
|||||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Auto-render loop: keep the render in sync with rAF so the canvas
|
|
||||||
// always shows the latest frame.
|
|
||||||
window.clouds2dStart = function() {
|
|
||||||
if (window._clouds2dRaf) return;
|
|
||||||
function loop() {
|
|
||||||
var c = document.getElementById('vanta');
|
|
||||||
if (c) window.clouds2dRender(c);
|
|
||||||
window._clouds2dRaf = requestAnimationFrame(loop);
|
|
||||||
}
|
|
||||||
window._clouds2dRaf = requestAnimationFrame(loop);
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
@@ -2455,7 +2458,11 @@ window.weatherThemes = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function tick() {
|
function tick() {
|
||||||
|
try {
|
||||||
updateClockAndEvents();
|
updateClockAndEvents();
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Tick error:', e);
|
||||||
|
}
|
||||||
setTimeout(tick, 1000);
|
setTimeout(tick, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2721,7 +2728,7 @@ window.weatherThemes = {
|
|||||||
// Fetch location label from lat/lon if available
|
// Fetch location label from lat/lon if available
|
||||||
fetchLocationLabel();
|
fetchLocationLabel();
|
||||||
animate();
|
animate();
|
||||||
tick();
|
try { tick(); } catch (e) { console.error('Initial tick error:', e); }
|
||||||
|
|
||||||
fetchWeather();
|
fetchWeather();
|
||||||
setInterval(fetchWeather, 15 * 60 * 1000);
|
setInterval(fetchWeather, 15 * 60 * 1000);
|
||||||
|
|||||||
+6
-2
@@ -324,7 +324,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="vanta" style="background: #1c1b1f;"></div>
|
<div id="vanta" style="background: #1c1b1f;"></div>
|
||||||
<div id="debug-panel">
|
<div id="debug-panel">
|
||||||
<div class="debug-title expanded" onclick="toggleDebugPanel()"><span>DEBUG - Palette: <span id="dbg-palette-name">Ash</span></span><span class="debug-title-toggle" id="debug-toggle-icon">▼</span></div>
|
<div class="debug-title expanded" onclick="toggleDebugPanel()">\n<span>DEBUG - Palette: <span id="dbg-palette-name">Ash</span></span><span class="debug-title-toggle" id="debug-toggle-icon">▼</span></div>
|
||||||
<div id="debug-content" class="debug-content">
|
<div id="debug-content" class="debug-content">
|
||||||
<div class="debug-controls">
|
<div class="debug-controls">
|
||||||
<select id="dbg-theme-select" class="debug-select" onchange="setDebugTheme(this.value)">
|
<select id="dbg-theme-select" class="debug-select" onchange="setDebugTheme(this.value)">
|
||||||
@@ -1659,7 +1659,11 @@ window.weatherThemes = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function tick() {
|
function tick() {
|
||||||
|
try {
|
||||||
updateClockAndEvents();
|
updateClockAndEvents();
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Tick error:', e);
|
||||||
|
}
|
||||||
setTimeout(tick, 1000);
|
setTimeout(tick, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1925,7 +1929,7 @@ window.weatherThemes = {
|
|||||||
// Fetch location label from lat/lon if available
|
// Fetch location label from lat/lon if available
|
||||||
fetchLocationLabel();
|
fetchLocationLabel();
|
||||||
animate();
|
animate();
|
||||||
tick();
|
try { tick(); } catch (e) { console.error('Initial tick error:', e); }
|
||||||
|
|
||||||
fetchWeather();
|
fetchWeather();
|
||||||
setInterval(fetchWeather, 15 * 60 * 1000);
|
setInterval(fetchWeather, 15 * 60 * 1000);
|
||||||
|
|||||||
@@ -73,7 +73,7 @@
|
|||||||
* Mon Oct
|
* Mon Oct
|
||||||
*/
|
*/
|
||||||
window.CONFIG = {
|
window.CONFIG = {
|
||||||
debug: 1,
|
debug: 0,
|
||||||
theme: 'ash',
|
theme: 'ash',
|
||||||
units: 'metric',
|
units: 'metric',
|
||||||
renderer: 'clouds2d', // 'vanta' or 'clouds2d'
|
renderer: 'clouds2d', // 'vanta' or 'clouds2d'
|
||||||
|
|||||||
Vendored
+31
-28
@@ -4,6 +4,7 @@
|
|||||||
* fragment shader to produce animated cloud-like patterns.
|
* fragment shader to produce animated cloud-like patterns.
|
||||||
*
|
*
|
||||||
* Drop-in replacement for Vanta when CONFIG.renderer === 'clouds2d'.
|
* Drop-in replacement for Vanta when CONFIG.renderer === 'clouds2d'.
|
||||||
|
* Creates its own <canvas> overlay and attaches to the target element.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
@@ -90,7 +91,7 @@ var FRAG_SRC = [
|
|||||||
function init(canvas) {
|
function init(canvas) {
|
||||||
var gl = canvas.getContext('webgl', { antialias: false, alpha: false });
|
var gl = canvas.getContext('webgl', { antialias: false, alpha: false });
|
||||||
if (!gl) {
|
if (!gl) {
|
||||||
console.error('clouds2d: WebGL not available, using fallback');
|
console.error('clouds2d: WebGL not available');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var prog = makeProgram(gl, VERT_SRC, FRAG_SRC);
|
var prog = makeProgram(gl, VERT_SRC, FRAG_SRC);
|
||||||
@@ -119,22 +120,38 @@ function init(canvas) {
|
|||||||
|
|
||||||
function hex(r, g, b) { return [r/255, g/255, b/255]; }
|
function hex(r, g, b) { return [r/255, g/255, b/255]; }
|
||||||
|
|
||||||
window.clouds2dRender = function(canvas) {
|
window.clouds2dRender = function(target) {
|
||||||
|
if (!target || typeof target !== 'object') {
|
||||||
|
console.error('clouds2d: invalid target');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Get or create the WebGL canvas
|
||||||
|
var canvas = target.__clouds2dCanvas;
|
||||||
if (!canvas) {
|
if (!canvas) {
|
||||||
console.error('clouds2d: no canvas');
|
// Replace the target's content with a WebGL canvas
|
||||||
return;
|
canvas = document.createElement('canvas');
|
||||||
|
canvas.style.position = 'absolute';
|
||||||
|
canvas.style.top = '0';
|
||||||
|
canvas.style.left = '0';
|
||||||
|
canvas.style.width = '100%';
|
||||||
|
canvas.style.height = '100%';
|
||||||
|
// Empty the target and insert the canvas as the only child
|
||||||
|
while (target.firstChild) target.removeChild(target.firstChild);
|
||||||
|
target.appendChild(canvas);
|
||||||
|
target.style.position = 'relative';
|
||||||
|
target.__clouds2dCanvas = canvas;
|
||||||
}
|
}
|
||||||
if (!canvas._clouds2d) {
|
|
||||||
canvas._clouds2d = init(canvas);
|
if (!canvas.__state) {
|
||||||
if (!canvas._clouds2d) {
|
canvas.__state = init(canvas);
|
||||||
// Fallback: render the body background color via 2D canvas
|
if (!canvas.__state) return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
var s = canvas._clouds2d;
|
var s = canvas.__state;
|
||||||
var gl = s.gl;
|
var gl = s.gl;
|
||||||
var w = canvas.width, h = canvas.height;
|
canvas.width = target.clientWidth || window.innerWidth;
|
||||||
gl.viewport(0, 0, w, h);
|
canvas.height = target.clientHeight || window.innerHeight;
|
||||||
|
gl.viewport(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
gl.clearColor(0, 0, 0, 1);
|
gl.clearColor(0, 0, 0, 1);
|
||||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||||
@@ -144,14 +161,12 @@ window.clouds2dRender = function(canvas) {
|
|||||||
gl.enableVertexAttribArray(s.aPos);
|
gl.enableVertexAttribArray(s.aPos);
|
||||||
gl.vertexAttribPointer(s.aPos, 2, gl.FLOAT, false, 0, 0);
|
gl.vertexAttribPointer(s.aPos, 2, gl.FLOAT, false, 0, 0);
|
||||||
|
|
||||||
gl.uniform2f(s.uRes, w, h);
|
gl.uniform2f(s.uRes, canvas.width, canvas.height);
|
||||||
gl.uniform1f(s.uTime, performance.now() / 1000);
|
gl.uniform1f(s.uTime, performance.now() / 1000);
|
||||||
|
|
||||||
// Default theme colors (used until renderWeather() sets the right ones)
|
|
||||||
var sky = [0.39, 0.54, 0.78];
|
var sky = [0.39, 0.54, 0.78];
|
||||||
var cloud = [0.43, 0.43, 0.43];
|
var cloud = [0.43, 0.43, 0.43];
|
||||||
var light = [0.89, 0.87, 0.82];
|
var light = [0.89, 0.87, 0.82];
|
||||||
// Pull from the same source as Vanta does
|
|
||||||
var cfg = window.CONFIG || {};
|
var cfg = window.CONFIG || {};
|
||||||
var theme = (cfg.currentWeatherTheme || (cfg.weatherThemes && cfg.weatherThemes[cfg.theme])) || null;
|
var theme = (cfg.currentWeatherTheme || (cfg.weatherThemes && cfg.weatherThemes[cfg.theme])) || null;
|
||||||
if (theme) {
|
if (theme) {
|
||||||
@@ -167,16 +182,4 @@ window.clouds2dRender = function(canvas) {
|
|||||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Auto-render loop: keep the render in sync with rAF so the canvas
|
|
||||||
// always shows the latest frame.
|
|
||||||
window.clouds2dStart = function() {
|
|
||||||
if (window._clouds2dRaf) return;
|
|
||||||
function loop() {
|
|
||||||
var c = document.getElementById('vanta');
|
|
||||||
if (c) window.clouds2dRender(c);
|
|
||||||
window._clouds2dRaf = requestAnimationFrame(loop);
|
|
||||||
}
|
|
||||||
window._clouds2dRaf = requestAnimationFrame(loop);
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
})();
|
||||||
Reference in new issue
Block a user