1/* CropFlow â plano de fundo do login. 2 Cena procedural: talhões vistos de altitude com varredura de sensoriamento remoto. 3 Substitui o vÃdeo de 8,7 MB. Zero requisição de rede, zero metadata de IA. */ 4(function () { 5 'use strict'; 6 7 var cvs = document.getElementById('authBgCanvas'); 8 if (!cvs || !cvs.getContext) return; 9 10 var ctx = cvs.getContext('2d', { alpha: false }); 11 var reduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; 12 13 var W = 0, H = 0, dpr = 1; 14 var tile = null, tileW = 0, tileH = 0; // textura de talhões, desenhada uma única vez 15 var raf = null, t0 = null, last = 0; 16 17 // Deriva do "sobrevoo": lento o bastante para não competir com o formulário. 18 var DRIFT_X = 7.5, DRIFT_Y = 4.2; // px/s 19 var SWEEP_PERIOD = 19000; // ms por passada do sensor 20 var FPS_CAP = 24; // deriva é lenta; 24 basta e poupa bateria em campo 21 22 // PRNG fixo: a cena é idêntica em todo carregamento, então vira identidade visual 23 // da marca e não um ruÃdo diferente a cada visita. 24 function rng(seed) { 25 var s = seed >>> 0; 26 return function () { 27 s ^= s << 13; s >>>= 0; 28 s ^= s >> 17; 29 s ^= s << 5; s >>>= 0; 30 return s / 4294967296; 31 }; 32 } 33 34 // Paleta NDVI dessaturada â verde de lavoura, palha e solo exposto. 35 var PLOTS = [ 36 [26, 54, 36], [31, 64, 41], [22, 46, 31], [37, 71, 45], 37 [44, 62, 36], [58, 61, 34], [46, 44, 28], [30, 40, 28] 38 ]; 39 40 /* Subdivisão recursiva do plano em talhões: gera lotes de tamanhos 41 irregulares, como parcelamento real de fazenda, em vez de um grid uniforme. */ 42 function subdivide(x, y, w, h, depth, rand, out) { 43 var min = 120; 44 if (depth <= 0 || (w < min * 2 && h < min * 2) || rand() < 0.08) { 45 out.push({ x: x, y: y, w: w, h: h }); 46 return; 47 } 48 var cutV = w > h ? rand() < 0.82 : rand() < 0.18; 49 var f = 0.32 + rand() * 0.36; 50 if (cutV) { 51 var cw = Math.round(w * f); 52 subdivide(x, y, cw, h, depth - 1, rand, out); 53 subdivide(x + cw, y, w - cw, h, depth - 1, rand, out); 54 } else { 55 var ch = Math.round(h * f); 56 subdivide(x, y, w, ch, depth - 1, rand, out); 57 subdivide(x, y + ch, w, h - ch, depth - 1, rand, out); 58 } 59 } 60 61 function buildTile() { 62 // A textura cobre 1,5x a viewport e faz wrap; o loop nunca fica visÃvel. 63 tileW = Math.ceil(Math.max(W, 900) * 1.5); 64 tileH = Math.ceil(Math.max(H, 700) * 1.5); 65 66 tile = document.createElement('canvas'); 67 tile.width = tileW; 68 tile.height = tileH; 69 var c = tile.getContext('2d'); 70 var rand = rng(20260730); 71 72 c.fillStyle = '#060d09'; 73 c.fillRect(0, 0, tileW, tileH); 74 75 var plots = []; 76 subdivide(0, 0, tileW, tileH, 7, rand, plots); 77 78 // Leve rotação do conjunto: elimina o alinhamento perfeito com a tela, 79 // que é o que faz um grid parecer wireframe em vez de terra. 80 c.save(); 81 c.translate(tileW / 2, tileH / 2); 82 c.rotate(-0.14); 83 c.scale(1.35, 1.35); 84 c.translate(-tileW / 2, -tileH / 2); 85 86 for (var i = 0; i < plots.length; i++) { 87 var p = plots[i]; 88 var col = PLOTS[(rand() * PLOTS.length) | 0]; 89 var lum = 0.72 + rand() * 0.5; 90 var r = Math.min(255, col[0] * lum) | 0; 91 var g = Math.min(255, col[1] * lum) | 0; 92 var b = Math.min(255, col[2] * lum) | 0; 93 94 c.fillStyle = 'rgb(' + r + ',' + g + ',' + b + ')'; 95 c.fillRect(p.x, p.y, p.w, p.h); 96 97 // Linhas de plantio: a textura que faz o talhão ler como lavoura. 98 var gap = 5 + (rand() * 5 | 0); 99 var horiz = rand() < 0.5; 100 c.save(); 101 c.beginPath(); 102 c.rect(p.x, p.y, p.w, p.h); 103 c.clip(); 104 c.globalAlpha = 0.16 + rand() * 0.12; 105 c.strokeStyle = 'rgb(' + (r + 26) + ',' + (g + 30) + ',' + (b + 18) + ')'; 106 c.lineWidth = 1; 107 c.beginPath(); 108 if (horiz) { 109 for (var yy = p.y; yy < p.y + p.h; yy += gap) { 110 c.moveTo(p.x, yy + 0.5); c.lineTo(p.x + p.w, yy + 0.5); 111 } 112 } else { 113 for (var xx = p.x; xx < p.x + p.w; xx += gap) { 114 c.moveTo(xx + 0.5, p.y); c.lineTo(xx + 0.5, p.y + p.h); 115 } 116 } 117 c.stroke();
118 c.restore(); 119 120 // Carreador entre talhões. 121 c.globalAlpha = 0.5; 122 c.strokeStyle = 'rgba(12,20,14,0.85)'; 123 c.lineWidth = 2; 124 c.strokeRect(p.x + 1, p.y + 1, p.w - 2, p.h - 2); 125 c.globalAlpha = 1; 126 } 127 c.restore(); 128 } 129 130 function resize() { 131 dpr = Math.min(window.devicePixelRatio || 1, 2); 132 W = cvs.clientWidth || window.innerWidth; 133 H = cvs.clientHeight || window.innerHeight; 134 cvs.width = Math.max(1, Math.round(W * dpr)); 135 cvs.height = Math.max(1, Math.round(H * dpr)); 136 ctx.setTransform(dpr, 0, 0, dpr, 0, 0); 137 buildTile(); 138 draw(last || 0); 139 } 140 141 function draw(now) { 142 if (t0 === null) t0 = now; 143 var el = (now - t0) / 1000; 144 145 var ox = -(el * DRIFT_X) % tileW; if (ox > 0) ox -= tileW; 146 var oy = -(el * DRIFT_Y) % tileH; if (oy > 0) oy -= tileH; 147 148 for (var i = 0; i < 2; i++) { 149 for (var j = 0; j < 2; j++) { 150 ctx.drawImage(tile, ox + i * tileW, oy + j * tileH, tileW, tileH); 151 } 152 } 153 154 // Passada do sensor: faixa de luz varrendo o terreno na diagonal. 155 var ph = ((now - t0) % SWEEP_PERIOD) / SWEEP_PERIOD; 156 var span = W + H; 157 var sx = -H + ph * (span + H * 2); 158 var grad = ctx.createLinearGradient(sx - 260, 0, sx + 260, H); 159 grad.addColorStop(0.00, 'rgba(52,211,153,0)'); 160 grad.addColorStop(0.45, 'rgba(52,211,153,0.055)'); 161 grad.addColorStop(0.50, 'rgba(167,243,208,0.10)'); 162 grad.addColorStop(0.55, 'rgba(52,211,153,0.055)'); 163 grad.addColorStop(1.00, 'rgba(52,211,153,0)'); 164 ctx.fillStyle = grad; 165 ctx.fillRect(0, 0, W, H); 166 167 // Profundidade de campo + vinheta: joga o foco para o formulário. 168 var vig = ctx.createRadialGradient(W * 0.34, H * 0.42, 0, W * 0.34, H * 0.42, Math.max(W, H) * 0.85); 169 vig.addColorStop(0, 'rgba(3,9,6,0.15)'); 170 vig.addColorStop(1, 'rgba(2,6,4,0.82)'); 171 ctx.fillStyle = vig; 172 ctx.fillRect(0, 0, W, H); 173 } 174 175 function loop(now) { 176 raf = requestAnimationFrame(loop); 177 if (now - last < 1000 / FPS_CAP) return; 178 last = now; 179 draw(now); 180 } 181 182 function start() { 183 if (raf || reduced) return; 184 raf = requestAnimationFrame(loop); 185 } 186 187 function stop() { 188 if (raf) { cancelAnimationFrame(raf); raf = null; } 189 } 190 191 // Só anima enquanto a tela de login está de fato visÃvel: depois do login 192 // o canvas fica oculto e não deve consumir bateria em campo. 193 function sync() { 194 var view = document.getElementById('authView'); 195 var visible = view && view.style.display !== 'none' && !document.hidden; 196 if (!visible) { stop(); return; } 197 // #authView nasce display:none, então o primeiro resize mediu a janela e não 198 // o elemento. Ao ficar visÃvel, remede antes de animar. 199 if (cvs.clientWidth && Math.abs(cvs.clientWidth - W) > 1) resize(); 200 start(); 201 } 202 203 window.addEventListener('resize', resize, { passive: true }); 204 document.addEventListener('visibilitychange', sync); 205 window.cropflowAuthBgSync = sync; 206 207 // O app mostra/esconde #authView mexendo em style.display; observar o atributo 208 // evita ter que instrumentar o fluxo de login para ligar/desligar a animação. 209 var authView = document.getElementById('authView'); 210 if (authView && window.MutationObserver) { 211 new MutationObserver(sync).observe(authView, { attributes: true, attributeFilter: ['style'] }); 212 } 213 214 resize(); 215 if (reduced) draw(0); else sync(); 216})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.