hesabixWpTheme/assets/js/hero-clouds.js
2026-05-22 13:52:15 +03:30

150 lines
4.7 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* بک‌گراند ابرهای متحرک برای بخش Hero صفحه نخست
* شکل ابر: پایهٔ پهن + چند دایرهٔ روی هم (شکل ابر پفکی)
* @package Hesabix2
*/
(function () {
'use strict';
var container = document.getElementById('hero-clouds');
if (!container) return;
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
if (!ctx) return;
var clouds = [];
var animationId = null;
var dpr = Math.min(window.devicePixelRatio || 1, 2);
/**
* یک ابر با شکل طبیعی: یک بیضی پهن پایین + چند دایرهٔ روی هم برای قسمت پفکی
*/
function Cloud(options) {
this.x = options.x;
this.y = options.y;
this.size = options.size; // ضریب اندازه (۰.۲ تا ۰.۴)
this.speed = options.speed;
}
Cloud.prototype.draw = function (ctx, width, height) {
var cx = this.x * width;
var cy = this.y * height;
var baseW = width * this.size * 0.9;
var baseH = height * this.size * 0.22;
var r = Math.min(baseW, baseH) * 0.45;
ctx.save();
// سایهٔ ملایم زیر ابر (عمق و کنتراست)
ctx.shadowColor = 'rgba(0, 0, 0, 0.06)';
ctx.shadowBlur = 25;
ctx.shadowOffsetY = 8;
// رنگ ابر: سفید مات و کاملاً خوانا
ctx.fillStyle = 'rgba(255, 255, 255, 0.92)';
// پایهٔ ابر — بیضی پهن و کم‌ارتفاع
ctx.beginPath();
ctx.ellipse(cx, cy + baseH * 0.5, baseW * 0.5, baseH * 0.7, 0, 0, Math.PI * 2);
ctx.fill();
// سه‌چهار دایرهٔ روی هم برای قسمت پفکی بالا (شکل ابر واقعی)
var bumps = [
{ x: -0.32, y: -0.35, r: 0.38 },
{ x: 0, y: -0.5, r: 0.42 },
{ x: 0.32, y: -0.35, r: 0.38 },
{ x: -0.12, y: -0.55, r: 0.28 },
{ x: 0.14, y: -0.52, r: 0.26 }
];
bumps.forEach(function (b) {
ctx.beginPath();
ctx.arc(
cx + b.x * baseW,
cy + b.y * baseH,
b.r * baseW * 0.55,
0, Math.PI * 2
);
ctx.fill();
});
ctx.restore();
};
function resize() {
var parent = container.parentElement;
if (!parent) return;
var w = parent.offsetWidth;
var h = parent.offsetHeight;
canvas.width = w * dpr;
canvas.height = h * dpr;
canvas.style.width = w + 'px';
canvas.style.height = h + 'px';
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
if (clouds.length === 0) initClouds(w, h);
}
function initClouds() {
clouds = [];
var count = 5;
for (var i = 0; i < count; i++) {
clouds.push(new Cloud({
x: Math.random() * 1.1 - 0.05,
y: 0.15 + Math.random() * 0.55,
size: 0.18 + Math.random() * 0.15,
speed: (0.00012 + Math.random() * 0.00008) * (i % 2 === 0 ? 1 : -1)
}));
}
}
function tick() {
var parent = container.parentElement;
if (!parent) return;
var w = parent.offsetWidth;
var h = parent.offsetHeight;
ctx.clearRect(0, 0, w, h);
clouds.forEach(function (c) {
c.x += c.speed;
if (c.speed > 0 && c.x > 1.35) c.x = -0.35;
if (c.speed < 0 && c.x < -0.35) c.x = 1.35;
c.draw(ctx, w, h);
});
animationId = requestAnimationFrame(tick);
}
function start() {
if (!container.parentElement) return;
container.appendChild(canvas);
resize();
tick();
}
var ro = typeof ResizeObserver !== 'undefined'
? new ResizeObserver(function () { resize(); })
: null;
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function () {
start();
if (ro && container.parentElement) ro.observe(container.parentElement);
});
} else {
start();
if (ro && container.parentElement) ro.observe(container.parentElement);
}
window.addEventListener('resize', function () {
if (container.parentElement && canvas.parentNode) resize();
});
document.addEventListener('visibilitychange', function () {
if (document.hidden) {
if (animationId) {
cancelAnimationFrame(animationId);
animationId = null;
}
} else if (canvas.parentNode) {
tick();
}
});
})();