PageSourceSearch

https://www.vecurity.net/assets/js/animated-background.js

js vecurity.net collected 2026-10-04 06:54:37 UTC 7,963 bytes, 276 lines download raw bytes

1// Animated Background with Light Rays, Stars, and Shooting Stars
2(() => {
3  function initCanvas() {
4    const canvas = document.getElementById('bg-rays');
5    if (!canvas) return;
6    const ctx = canvas.getContext('2d', { alpha: true, desynchronized: true });
7
8    // Performance / vibe knobs
9    const FPS = 24;
10    const DPR_CAP = 1.6;
11    const STAR_DENSITY = 0.00006;
12    const RAYS = 9;
13    const RAY_SOFTNESS = 0.20;
14    const RAY_INTENSITY = 0.22;
15    const DRIFT_SPEED = 0.12;
16    const TWINKLE = 0.35;
17
18    // Colors (muted purple/grey)
19    const PUR = [160, 120, 255];
20    const PUR2 = [120, 80, 210];
21    const GRY = [220, 220, 235];
22
23    const clamp = (v, a, b) => Math.max(a, Math.min(b, v));
24    const mix = (a, b, t) => a + (b - a) * t;
25    const rgba = (rgb, a) => `rgba(${rgb[0]},${rgb[1]},${rgb[2]},${a})`;
26
27    let DPR = Math.min(DPR_CAP, window.devicePixelRatio || 1);
28    let W = 0, H = 0;
29    let running = true;
30    let initialized = false;
31    let rayIntensity = 0;
32    let targetIntensity = 0;
33    let animationStarted = false;
34
35    document.addEventListener("visibilitychange", () => {
36      running = !document.hidden;
37    });
38
39    function resize() {
40      const section = canvas.parentElement;
41      if (!section) return;
42      
43      // Get computed styles to ensure accurate dimensions
44      const computedStyle = window.getComputedStyle(section);
45      const paddingTop = parseFloat(computedStyle.paddingTop) || 0;
46      const paddingBottom = parseFloat(computedStyle.paddingBottom) || 0;
47      
48      // Force layout recalculation
49      section.offsetHeight;
50      
51      const rect = section.getBoundingClientRect();
52      DPR = Math.min(DPR_CAP, window.devicePixelRatio || 1);
53      
54      // Ensure minimum stable dimensions
55      const width = Math.max(rect.width, window.innerWidth, 800);
56      const height = Math.max(rect.height, section.scrollHeight, paddingTop + paddingBottom + 400, 500);
57      
58      W = canvas.width = Math.floor(width * DPR);
59      H = canvas.height = Math.floor(height * DPR);
60      canvas.style.width = width + "px";
61      canvas.style.height = height + "px";
62
63      buildStars();
64      buildRays();
65      
66      if (!initialized) {
67        initialized = true;
68        requestAnimationFrame(loop);
69      }
70    }
71    
72    // Multiple event listeners for stability
73    addEventListener("resize", resize, { passive: true });
74    addEventListener("load", resize, { passive: true });
75    
76    // Intersection Observer to detect when section is visible and trigger animation
77    const observer = new IntersectionObserver((entries) => {
78      entries.forEach(entry => {
79        if (entry.isIntersecting && !animationStarted) {
80          targetIntensity = RAY_INTENSITY;
81          animationStarted = true;
82          setTimeout(resize, 100);
83        }
84      });
85    }, { threshold: 0.3 });
86    observer.observe(canvas.parentElement);
87
88  // Stars
89  let stars = [];
90  let shootingStars = [];
91  function rand(n) {
92    const s = Math.sin(n) * 10000;
93    return s - Math.floor(s);
94  }
95
96  function buildStars() {
97    const count = Math.floor(W * H / (DPR * DPR) * STAR_DENSITY);
98    stars = new Array(count);
99    for (let i = 0; i < count; i++) {
100      const r = rand(i * 12.9898 + 78.233);
101      const z = rand(i * 93.9898 + 12.233);
102      const size = (0.6 + 1.8 * Math.pow(z, 2.2)) * DPR;
103      stars[i] = {
104        x: rand(i * 1.3 + 9.1) * W,
105        y: rand(i * 2.1 + 3.7) * H,
106        z,
107        size,
108        tw: rand(i * 7.7 + 5.5) * Math.PI * 2
109      };
110    }
111  }
112
113  // Rays
114  let rays = [];
115  function buildRays() {
116    rays = [];
117    const cx = W * 0.5;
118    const cy = H * 0.5;
119    for (let i = 0; i < RAYS; i++) {
120      const a = mix(-0.55, 0.55, (i + 0.5) / RAYS);
121      const w = mix(0.10, 0.22, rand(i * 4.2 + 1.1));
122      const len = mix(0.75, 1.10, rand(i * 2.6 + 9.7)) * Math.max(W, H);
123      const phase = rand(i * 8.8 + 2.2) * Math.PI * 2;
124      const tintMix = rand(i * 5.5 + 0.9);
125      const col = tintMix < 0.35 ? PUR : (tintMix < 0.70 ? PUR2 : GRY);
126
127      rays.push({ cx, cy, a, w, len, phase, col });
128    }
129  }
130
131  // Rendering
132  function drawBackgroundBase() {
133    ctx.clearRect(0, 0, W, H);
134  }
135
136  function drawRays(time) {
137    const t = time * 0.001;
138    
139    // Animate ray intensity
140    if (rayIntensity < targetIntensity) {
141      rayIntensity += 0.0055;
142      if (rayIntensity > targetIntensity) rayIntensity = targetIntensity;
143    }
144
145    for (const r of rays) {
146      const wob = 0.15 * Math.sin(t * 0.25 + r.phase);
147      const width = r.w * (1 + 0.35 * Math.sin(t * 0.22 + r.phase * 1.3));
148      const ang = r.a + wob;
149
150      const g = ctx.createRadialGradient(r.cx, r.cy, 0, r.cx, r.cy, r.len);
151      g.addColorStop(0, rgba(r.col, rayIntensity * 0.85));
152      g.addColorStop(RAY_SOFTNESS, rgba(r.col, rayIntensity * 0.35));
153      g.addColorStop(1, rgba(r.col, 0));
154
155      ctx.save();
156      ctx.fillStyle = g;
157      ctx.beginPath();
158      ctx.moveTo(r.cx, r.cy);
159      ctx.arc(r.cx, r.cy, r.len, ang - width, ang + width);
160      ctx.closePath();
161      ctx.fill();
162      ctx.restore();
163    }
164  }
165
166  function drawStars(time) {
167    const t = time * 0.001;
168    const vx = Math.cos(t * 0.05) * DRIFT_SPEED * DPR;
169    const vy = Math.sin(t * 0.05) * DRIFT_SPEED * DPR;
170
171    for (let i = 0; i < stars.length; i++) {
172      const s = stars[i];
173      const sp = (0.15 + 1.05 * s.z);
174      s.x += vx * sp;
175      s.y += vy * sp;
176
177      if (s.x < -10) s.x = W + 10;
178      if (s.x > W + 10) s.x = -10;
179      if (s.y < -10) s.y = H + 10;
180      if (s.y > H + 10) s.y = -10;
181
182      const tw = 0.65 + TWINKLE * 0.35 * Math.sin(t * 0.9 + s.tw);
183      const a = (0.08 + 0.35 * Math.pow(s.z, 1.6)) * tw;
184
185      const tint = (i % 17 === 0) ? PUR : GRY;
186      ctx.fillStyle = rgba(tint, a);
187
188      ctx.beginPath();
189      ctx.arc(s.x, s.y, s.size, 0, Math.PI * 2);
190      ctx.fill();
191    }
192  }
193
194  function drawShootingStars(time) {
195    const t = time * 0.001;
196
197    // Create new shooting star occasionally
198    if (Math.random() < 0.008) {
199      shootingStars.push({
200        x: Math.random() * W * 0.3,
201        y: -20,
202        vx: Math.random() * 4 + 2,
203        vy: Math.random() * 3 + 3,
204        life: 1,
205        decay: 0.012
206      });
207    }
208
209    // Update and draw shooting stars
210    for (let i = shootingStars.length - 1; i >= 0; i--) {
211      const s = shootingStars[i];
212      s.x += s.vx * DPR;
213      s.y += s.vy * DPR;
214      s.life -= s.decay;
215
216      if (s.life <= 0 || s.y > H + 50) {
217        shootingStars.splice(i, 1);
218        continue;
219      }
220
221      // Draw trail
222      const g = ctx.createLinearGradient(s.x, s.y, s.x - s.vx * 50, s.y - s.vy * 50);
223      g.addColorStop(0, rgba(PUR, s.life * 1.2));
224      g.addColorStop(1, rgba(PUR, 0));
225
226      ctx.strokeStyle = g;
227      ctx.lineWidth = 3;
228      ctx.beginPath();
229      ctx.moveTo(s.x, s.y);
230      ctx.lineTo(s.x - s.vx * 50, s.y - s.vy * 50);
231      ctx.stroke();
232    }
233  }
234
235  function drawVignette() {
236    const v = ctx.createRadialGradient(W * 0.55, H * 0.45, 0, W * 0.55, H * 0.45, Math.max(W, H) * 0.78);
237    v.addColorStop(0, 'rgba(0,0,0,0)');
238    v.addColorStop(1, 'rgba(0,0,0,.42)');
239    ctx.fillStyle = v;
240    ctx.fillRect(0, 0, W, H);
241  }
242
243  // FPS cap loop
244  const frameInterval = 1000 / FPS;
245  let lastDraw = 0;
246
247  function loop(now) {
248    requestAnimationFrame(loop);
249    if (!running) return;
250    if (now - lastDraw < frameInterval) return;
251    lastDraw = now;
252
253    drawBackgroundBase();
254    drawRays(now);
255    drawStars(now);
256    drawShootingStars(now);
257    drawVignette();
258  }
259
260    // Initial setup with stable timing
261    resize();
262    // Additional resize calls with delays to catch late layout changes
263    setTimeout(resize, 100);
264    setTimeout(resize, 300);
265    setTimeout(resize, 500);
266  }
267
268  // Enhanced initialization timing
269  if (document.readyState === 'loading') {
270    document.addEventListener('DOMContentLoaded', () => {
271      setTimeout(initCanvas, 50);
272    });
273  } else {
274    setTimeout(initCanvas, 50);
275  }
276})();

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.