PageSourceSearch

https://cropflow.com.br/js/auth-bg.js

js cropflow.com.br collected 2026-10-01 03:53:11 UTC 8,310 bytes, 216 lines download raw bytes

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.