, nebo jej dej do externího souboru document.addEventListener('DOMContentLoaded', () => { const canvas = document.getElementById('interactive-grid'); if (!canvas) return; const ctx = canvas.getContext('2d'); const TARGET_GRID_SPACING = 20; // Cílová vzdálenost mezi středy bodů (skutečná se dopočítá) const BASE_DOT_SIZE = 4; // Základní velikost tečky const INFLUENCE_RADIUS = 120; // Poloměr, ve kterém kurzor ovlivňuje tečky const EXTRA_HEIGHT = 100; // o kolik px má grid přesáhnout pod obrázek // Tyto proměnné budou přepsány v resize() let gridSpacingX = TARGET_GRID_SPACING; let gridSpacingY = TARGET_GRID_SPACING; let offsetX = 0; let offsetY = 0; class Dot { constructor(x, y) { this.x = x; this.y = y; this.updatePosition(); this.size = BASE_DOT_SIZE; // Mírně prolneme starý noise (pro zachování nějakých shluků) s čistou náhodou let n = Math.sin(x * 0.8) + Math.cos(y * 0.8); n += Math.sin(x * 0.3 - y * 0.4); this.noiseValue = (n + 2) / 4; // Hlavní hybatel nahodilosti: jak moc je tento konkrétní bod ochotný růst this.randomWeight = Math.random(); } updatePosition() { this.cx = offsetX + this.x * gridSpacingX; this.cy = offsetY + this.y * gridSpacingY; } update(mouseX, mouseY) { const dx = mouseX - this.cx; const dy = mouseY - this.cy; const dist = Math.sqrt(dx * dx + dy * dy); let targetSize = BASE_DOT_SIZE; if (dist < INFLUENCE_RADIUS) { // Blízkost od 0 (na okraji) do 1 (přímo na kurzoru) const proximity = Math.max(0, 1 - (dist / INFLUENCE_RADIUS)); // Zkombinujeme blízkost s náhodnou váhou bodu (částečně ovlivněno noise). // Tím docílíme toho, že i dost blízko myši zůstanou některé body malé. const combinedWeight = (this.randomWeight * 0.7) + (this.noiseValue * 0.3); const influence = proximity * combinedWeight; // Definice skokového zvětšení (schody) na základě vlivu (influence) a fixní blízkosti if (dist < 15) { targetSize = 30; // Úplný střed spojí vždy kousek k sobě } else if (influence > 0.45) { targetSize = 30; // Hodně ochotné body zformují velký blok } else if (influence > 0.3) { targetSize = 14; // Velký čtverec, ale neslitý } else if (influence > 0.15) { targetSize = 8; } else if (influence > 0.05) { targetSize = BASE_DOT_SIZE; } } this.size += (targetSize - this.size) * 0.15; } draw(ctx) { const halfSize = this.size / 2; // Zaokrouhlení na celé pixely pro maximální ostrost const drawX = Math.floor(this.cx - halfSize); const drawY = Math.floor(this.cy - halfSize); const drawSize = Math.max(1, Math.round(this.size)); ctx.fillRect(drawX, drawY, drawSize, drawSize); } } let dots = []; let mousePos = { x: -1000, y: -1000 }; let targetMousePos = { x: -1000, y: -1000 }; const resize = () => { // Vezmeme rozměry z nejbližší
nad canvasem (ta má definovanou výšku), // ne z přímého rodičovského
, který výšku nemá. const parent = canvas.closest('section'); const logicalWidth = parent ? parent.clientWidth : window.innerWidth; const baseHeight = parent ? parent.clientHeight : window.innerHeight; const logicalHeight = baseHeight + EXTRA_HEIGHT; // Podpora pro Retina / High DPI displeje pro ostré hrany const dpr = window.devicePixelRatio || 1; canvas.width = logicalWidth * dpr; canvas.height = logicalHeight * dpr; canvas.style.width = logicalWidth + 'px'; canvas.style.height = logicalHeight + 'px'; // Reset transformace před scale (aby se DPR nenásobilo při opakovaném resize) ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.scale(dpr, dpr); // Rezerva, aby se první/poslední tečka vešla celá (s ohledem na základní velikost) const padding = BASE_DOT_SIZE / 2; const usableWidth = logicalWidth - BASE_DOT_SIZE; const usableHeight = logicalHeight - BASE_DOT_SIZE; // Kolik mezer (ne bodů) se vejde při cílovém rozestupu const colsCount = Math.max(1, Math.round(usableWidth / TARGET_GRID_SPACING)); const rowsCount = Math.max(1, Math.round(usableHeight / TARGET_GRID_SPACING)); // Skutečný rozestup, aby se body přesně vešly mezi okraje gridSpacingX = usableWidth / colsCount; gridSpacingY = usableHeight / rowsCount; offsetX = padding; offsetY = padding; dots = []; for (let x = 0; x <= colsCount; x++) { for (let y = 0; y <= rowsCount; y++) { dots.push(new Dot(x, y)); } } }; // Měníme velikost gridu při změně okna window.addEventListener('resize', resize); // Přepočítáme i po úplném načtení (fonty, obrázky na pozadí), kdy už sekce zná svou výšku window.addEventListener('load', resize); resize(); const render = () => { ctx.clearRect(0, 0, canvas.width, canvas.height); mousePos.x += (targetMousePos.x - mousePos.x) * 0.2; mousePos.y += (targetMousePos.y - mousePos.y) * 0.2; // Nastavená barva Gridu dle tvoje zadání z minula ctx.fillStyle = '#587AE0'; for (const dot of dots) { dot.update(mousePos.x, mousePos.y); dot.draw(ctx); } requestAnimationFrame(render); }; render(); // Událost odchytáváme na okně, aby trackovala kurzor bez ztráty fokusu window.addEventListener('mousemove', (e) => { const rect = canvas.getBoundingClientRect(); targetMousePos = { x: e.clientX - rect.left, y: e.clientY - rect.top }; }); document.addEventListener('mouseleave', () => { targetMousePos = { x: -1000, y: -1000 }; }); }); } catch (e) { console.error('[Custom Script "grid"]', e); } })();
siment

Statement

We stand at the intersection of material science and industrial mastery, where laboratory innovation is transformed into certified construction reality.

Selective Paste Intrusion (SPI) is an advanced 3D concrete printing technology based on printing directly into a sand bed. This approach makes it possible to manufacture large-scale, highly complex geometries without the need for temporary support structures. SPI significantly reduces the cost of producing prototypes, mock-ups, or one-off components, making it an efficient solution for both experimental and customized applications with minimized material consumption and CO2 production.


Contact

Eduard Příhoda
projektový manažer výroby
[email protected]
+420 602 208 385

U Dálnice 604
250 90 Jirny Praha-východ


© 2026 SIMENT a.s.

Všechna práva vyhrazena.

Tento web běží na solidpixels.