الهيكل العام للكود
هذا الكود يعرض تأثيرًا بصريًا تفاعليًا يُسمى "الاضطراب" (Turbulence)، حيث يتم رسم جزيئات تتحرك بشكل عشوائي وتتفاعل مع بعضها البعض ومع العناصر الموجودة على الصفحة. سأشرح الكود بالتفصيل:
HTML
<div class="turbulence-container">
<div id="turbulence">
<div class="blocks">
<div class="block">
Блок 1
</div>
<div class="block">
Блок 2
</div>
<div class="block">
Блок 3
</div>
</div>
</div>
</div>
الغرض: إنشاء هيكل الصفحة.
turbulence-container
: حاوية رئيسية للتأثير.turbulence
: منطقة الاضطراب.blocks
: يحتوي على عدة عناصر (block
) يمكن التفاعل معها.
css
.turbulence-container {
position: relative;
margin: 20px 0;
box-shadow: 0 4px 12px rgba(0,0,0,0.2), 0 16px 20px rgba(0,0,0,0.2);
}
.blocks {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
position: relative;
z-index: 1;
padding: 100px 0;
}
.block {
margin: 20px;
padding: 100px 50px;
border: 6px solid #BFE2FF;
border-radius: 10px;
background-color: #fff;
text-align: center;
text-transform: uppercase;
font-family: Verdana, sans-serif;
font-size: 20px;
font-weight: bold;
color: #337AB7;
cursor: pointer;
box-shadow: 0 4px 12px rgba(0,0,0,0.2), 0 16px 20px rgba(0,0,0,0.2);
}
الغرض: تنسيق العناصر.
turbulence-container
: تنسيق الحاوية الرئيسية.blocks
: تنسيق العناصر الداخلية.block
: تنسيق كل عنصر فردي.
JavaScript
المتغيرات العامة
const nbParticles = 1000; // عدد الجزيئات
const lifeTime = 1000; // متوسط عمر الجزيء
let canv, ctx;
let maxx, maxy;
let dimx, dimy;
let eddies;
let particles;
let requestID;
let hueShift;
الغرض: تعريف المتغيرات العامة.
nbEddies
: عدد الدوامات (Eddies).nbParticles
: عدد الجزيئات.lifeTime
: متوسط عمر الجزيء.
دوال المساعدة
function alea (min, max) {
if (typeof max == 'undefined') return min * mrandom();
return min + (max - min) * mrandom();
}
function intAlea (min, max) {
if (typeof max == 'undefined') {
max = min; min = 0;
}
return mfloor(min + (max - min) * mrandom());
}
الغرض: إنشاء أرقام عشوائية.
alea
: إنشاء رقم عشوائي بين min وmax.intAlea
: إنشاء عدد صحيح عشوائي بين min وmax.
إنشاء الدوامات (Eddies)
function createEddy () {
return {
x: alea (dimx),
y: alea (dimy),
coeffR: 0.001 * (alea(0.7, 1.3)),
radius: 150 + alea(-50, 50),
coeffA1: 10000 * alea(0.8, 1.2),
coeffA2: 0.01 * alea(0.8, 1.2),
dir: (mrandom() > 0.5) ? 1 : -1
}
}
function createEddies() {
eddies = [];
for (let k = 0; k < nbEddies; ++k) {
eddies.push(createEddy());
}
}
الغرض: إنشاء دوامات عشوائية.
createEddy
: إنشاء دوامة واحدة بخصائص عشوائية.createEddies
: إنشاء قائمة الدوامات.
إنشاء الجزيئات (Particles)
function createParticle() {
return {
x: alea (-100, dimx + 100),
y: alea (-100, dimy + 100),
sat: `${intAlea(50, 101)}%`,
light: `${intAlea(30, 80)}%`,
TTL: alea(lifeTime * 0.8, lifeTime * 1.2)
}
}
function createParticles() {
particles = [];
for (let k = 0; k < nbParticles; ++k) {
particles.push(createParticle());
}
particles.forEach (part => {
part.TTL = intAlea(lifeTime);
});
}
الغرض: إنشاء جزيئات عشوائية.
createParticle
: إنشاء جزيء واحد بخصائص عشوائية.createParticles
: إنشاء قائمة الجزيئات.
تحريك الجزيئات
function move() {
let part, prev, dx, dy, s, c, r, rv, av, deltar;
let mindeltar;
for (let k = 0; k < nbParticles; ++k) {
part = particles[k];
if (part.TTL <= 0) {
part = createParticle();
particles[k] = part;
}
prev = {x: part.x, y: part.y};
mindeltar = 10000;
eddies.forEach ((eddy, ke) => {
dx = prev.x - eddy.x;
dy = prev.y - eddy.y;
r = mhypot(dx, dy);
if (r < 0.001) r = 0.001;
s = dy / r;
c = dx / r;
deltar = r - eddy.radius;
av = eddy.coeffA2 * mexp (- deltar * deltar / eddy.coeffA1) * eddy.dir;
rv = - deltar * eddy.coeffR;
part.x += rv * c - av * r * s;
part.y += rv * s + av * r * c;
})
--part.TTL;
let speed = mhypot (prev.x - part.x, prev.y - part.y) ;
let hue = mmin (speed * 100, 300);
hue = (hue + hueShift) % 360;
ctx.beginPath();
ctx.moveTo (prev.x, prev.y);
ctx.lineTo (part.x, part.y);
ctx.strokeStyle = `hsl(${hue},${part.sat},${part.light})`; // لون الجزيء
ctx.stroke();
}
}
الغرض: تحريك الجزيئات ورسمها.
- يتم تحديث موضع كل جزيء بناءً على الدوامات.
- يتم رسم خط بين الموضع السابق والحالي للجزيء.
بدء الرسوم المتحركة
function startOver() {
maxx = bcontainer.offsetWidth;
maxy = bcontainer.offsetHeight;
dimx = maxx;
dimy = maxy;
canv.style.left = (maxx - dimx) / 2 + 'px';
canv.style.top = (maxy - dimy) / 2 + 'px';
canv.width = dimx;
canv.height = dimy;
ctx.lineWidth = 1.5;
ctx.imageSmoothingEnabled = false;
hueShift = intAlea(360);
createEddies();
createParticles();
if (typeof requestID == 'number') window.cancelAnimationFrame(requestID);
(function animate () {
move();
requestID = window.requestAnimationFrame(animate);
})();
}
الغرض: تهيئة وإعادة بدء الرسوم المتحركة.
- يتم ضبط أبعاد لوحة الرسم (
<canvas>
). - يتم إنشاء الدوامات والجزيئات.
- يتم بدء حلقة الرسوم المتحركة.
التفاعل مع العناصر
function clickCanvas() {
startOver();
}
{
canv = document.createElement('canvas');
canv.style.position = "absolute";
bcontainer.appendChild(canv);
ctx = canv.getContext('2d');
}
startOver();
window.addEventListener('resize',startOver);
let block = document.getElementsByClassName('block');
for (var i = 0; i < block.length; i++) {
block[i].addEventListener('click', clickCanvas);
// block[i].addEventListener('mouseenter', clickCanvas);
}
الغرض: إضافة تفاعل مع العناصر.
- يتم إعادة بدء الرسوم المتحركة عند النقر على العناصر (
block
).
النتيجة النهائية
- يتم رسم جزيئات تتحرك بشكل عشوائي وتتفاعل مع الدوامات.
- يمكن إعادة بدء الرسوم المتحركة بالنقر على العناصر (
block
).