PageSourceSearch

https://msaood.com/assets/js/ambient-fx.js

js msaood.com collected 2026-10-02 01:36:26 UTC 5,709 bytes, 190 lines download raw bytes

1/**
2 * ambient-fx.js — Universal Background Particles & Fluid Magnetic Cursor
3 * By: Masoud Abdullah (msaood.com)
4 */
5(function () {
6  // 1. حقن التنسيقات الخاصة بالتأثيرات تلقائياً
7  const style = document.createElement('style');
8  style.id = 'ambient-fx-styles';
9  style.textContent = `
10    #ambientCanvas {
11      position: fixed;
12      inset: 0;
13      pointer-events: none;
14      z-index: 0;
15      opacity: 0.45;
16    }
17    .custom-cursor {
18      position: fixed;
19      top: 0;
20      left: 0;
21      width: 24px;
22      height: 24px;
23      border: 1.5px solid var(--red-soft, #ff4444);
24      border-radius: 50%;
25      pointer-events: none;
26      z-index: 99999;
27      transform: translate(-50%, -50%);
28      transition: width 0.2s cubic-bezier(0.34, 1.56, 0.64, 1), 
29                  height 0.2s cubic-bezier(0.34, 1.56, 0.64, 1), 
30                  background-color 0.2s ease,
31                  border-color 0.2s ease;
32      mix-blend-mode: difference;
33    }
34    .custom-cursor-dot {
35      position: fixed;
36      top: 0;
37      left: 0;
38      width: 5px;
39      height: 5px;
40      background-color: var(--red-soft, #ff4444);
41      border-radius: 50%;
42      pointer-events: none;
43      z-index: 100000;
44      transform: translate(-50%, -50%);
45    }
46    .custom-cursor.cursor-hover {
47      width: 48px;
48      height: 48px;
49      background-color: rgba(255, 68, 68, 0.2);
50      border-color: var(--red-soft, #ff4444);
51    }
52    @media (max-width: 900px) {
53      .custom-cursor, .custom-cursor-dot {
54        display: none !important;
55      }
56    }
57  `;
58  document.head.appendChild(style);
59
60  // 2. تشغيل التأثيرات عند تحميل محتوى الصفحة
61  function initAmbientFX() {
62    // حقن كانفاس الخلفية
63    if (!document.getElementById('ambientCanvas')) {
64      const canvas = document.createElement('canvas');
65      canvas.id = 'ambientCanvas';
66      document.body.prepend(canvas);
67      initParticleMesh(canvas);
68    }
69
70    // حقن المؤشر التفاعلي لأجهزة سطح المكتب
71    if (window.innerWidth > 900 && !document.getElementById('customCursor')) {
72      const cursor = document.createElement('div');
73      cursor.className = 'custom-cursor';
74      cursor.id = 'customCursor';
75
76      const dot = document.createElement('div');
77      dot.className = 'custom-cursor-dot';
78      dot.id = 'customCursorDot';
79
80      document.body.appendChild(cursor);
81      document.body.appendChild(dot);
82      initFluidCursor(cursor, dot);
83    }
84  }
85
86  // محرك رسم شبكة الجزيئات (Particle Mesh Engine)
87  function initParticleMesh(canvas) {
88    const ctx = canvas.getContext('2d');
89    let width = (canvas.width = window.innerWidth);
90    let height = (canvas.height = window.innerHeight);
91
92    window.addEventListener('resize', () => {
93      width = canvas.width = window.innerWidth;
94      height = canvas.height = window.innerHeight;
95    }, { passive: true });
96
97    const particles = [];
98    const count = Math.min(Math.floor(window.innerWidth / 35), 35);
99
100    for (let i = 0; i < count; i++) {
101      particles.push({
102        x: Math.random() * width,
103        y: Math.random() * height,
104        vx: (Math.random() - 0.5) * 0.35,
105        vy: (Math.random() - 0.5) * 0.35,
106        radius: Math.random() * 1.8 + 1
107      });
108    }
109
110    function render() {
111      ctx.clearRect(0, 0, width, height);
112      ctx.fillStyle = 'rgba(17, 18, 15, 0.15)';
113      ctx.strokeStyle = 'rgba(17, 18, 15, 0.05)';
114
115      for (let i = 0; i < particles.length; i++) {
116        const p = particles[i];
117        p.x += p.vx;
118        p.y += p.vy;
119
120        if (p.x < 0) p.x = width;
121        if (p.x > width) p.x = 0;
122        if (p.y < 0) p.y = height;
123        if (p.y > height) p.y = 0;
124
125        ctx.beginPath();
126        ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);
127        ctx.fill();
128
129        for (let j = i + 1; j < particles.length; j++) {
130          const p2 = particles[j];
131          const dist = Math.hypot(p.x - p2.x, p.y - p2.y);
132          if (dist < 110) {
133            ctx.lineWidth = 1 - dist / 110;
134            ctx.beginPath();
135            ctx.moveTo(p.x, p.y);
136            ctx.lineTo(p2.x, p2.y);
137            ctx.stroke();
138          }
139        }
140      }
141      requestAnimationFrame(render);
142    }
143    render();
144  }
145
146  // محرك مؤشر الفأرة المغناطيسي (Fluid Cursor)
147  function initFluidCursor(cursor, dot) {
148    let mouseX = window.innerWidth / 2;
149    let mouseY = window.innerHeight / 2;
150    let cursorX = mouseX;
151    let cursorY = mouseY;
152
153    window.addEventListener('mousemove', (e) => {
154      mouseX = e.clientX;
155      mouseY = e.clientY;
156      dot.style.left = `${mouseX}px`;
157      dot.style.top = `${mouseY}px`;
158    }, { passive: true });
159
160    function render() {
161      cursorX += (mouseX - cursorX) * 0.2;
162      cursorY += (mouseY - cursorY) * 0.2;
163      cursor.style.left = `${cursorX}px`;
164      cursor.style.top = `${cursorY}px`;
165      requestAnimationFrame(render);
166    }
167    render();
168
169    // إضافة تأثير التكبير عند التمرير على العناصر التفاعلية
170    const interactiveSelectors = 'a, button, input, textarea, select, .interactive-el, [role="button"]';
171    
172    document.addEventListener('mouseover', (e) => {
173      if (e.target.closest(interactiveSelectors)) {
174        cursor.classList.add('cursor-hover');
175      }
176    });
177
178    document.addEventListener('mouseout', (e) => {
179      if (e.target.closest(interactiveSelectors)) {
180        cursor.classList.remove('cursor-hover');
181      }
182    });
183  }
184
185  if (document.readyState === 'loading') {
186    document.addEventListener('DOMContentLoaded', initAmbientFX);
187  } else {
188    initAmbientFX();
189  }
190})();

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.