mobile adapt and seasons UPD
This commit is contained in:
parent
c9df959c75
commit
668edc8123
1 changed files with 258 additions and 94 deletions
|
|
@ -1,37 +1,70 @@
|
|||
/** @license
|
||||
DHTML Snowstorm! JavaScript-based snow for web pages
|
||||
Исправленная версия с фрактальными снежинками
|
||||
Версия 2.2 с адаптацией под мобильные и сезонным ограничением
|
||||
-----------------------------------------------------------
|
||||
Версия 2.1 с работающим снегопадом
|
||||
Автоматически работает только с 1 декабря по 31 января
|
||||
*/
|
||||
|
||||
var fractalSnowstorm = function(window, document) {
|
||||
// Конфигурация
|
||||
// Проверяем сезон - снег только с 1 декабря по 31 января
|
||||
function isSnowSeason() {
|
||||
const now = new Date();
|
||||
const month = now.getMonth() + 1; // 1-12
|
||||
const day = now.getDate();
|
||||
|
||||
// 1 декабря - 31 января
|
||||
if (month === 12 || month === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Если февраль, но сегодня 1-е февраля, еще можно оставить снег
|
||||
if (month === 2 && day === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Если не сезон для снега, не запускаемся
|
||||
if (!isSnowSeason()) {
|
||||
console.log('Снегопад активен только с 1 декабря по 31 января. Сейчас не сезон.');
|
||||
return {
|
||||
start: function() { console.log('Снегопад доступен только зимой!'); },
|
||||
stop: function() {},
|
||||
isSeason: function() { return false; }
|
||||
};
|
||||
}
|
||||
|
||||
// Конфигурация с адаптацией для мобильных
|
||||
const isMobile = /iPhone|iPad|iPod|Android|BlackBerry|Windows Phone/i.test(navigator.userAgent);
|
||||
const mobileMultiplier = isMobile ? 0.5 : 1; // В 2 раза меньше на мобильных
|
||||
|
||||
const config = {
|
||||
autoStart: true,
|
||||
excludeMobile: true,
|
||||
flakesMax: 80,
|
||||
flakesMaxActive: 60,
|
||||
animationInterval: 33, // ~30 FPS
|
||||
excludeMobile: false, // Теперь работаем и на мобильных
|
||||
flakesMax: isMobile ? 40 : 80, // Меньше снежинок на мобильных
|
||||
flakesMaxActive: isMobile ? 30 : 60,
|
||||
animationInterval: 33,
|
||||
useGPU: true,
|
||||
snowColor: "#ffffff",
|
||||
useTwinkleEffect: true,
|
||||
useMeltEffect: true,
|
||||
snowStick: true,
|
||||
followMouse: false, // Упростим для начала
|
||||
snowStick: !isMobile, // На мобильных не прилипает (для производительности)
|
||||
followMouse: false,
|
||||
freezeOnBlur: true,
|
||||
flakeWidth: 24,
|
||||
flakeHeight: 24,
|
||||
vMaxX: 2,
|
||||
vMaxY: 3, // Увеличим скорость падения
|
||||
flakeWidth: Math.round(24 * mobileMultiplier),
|
||||
flakeHeight: Math.round(24 * mobileMultiplier),
|
||||
vMaxX: 2 * mobileMultiplier,
|
||||
vMaxY: 3 * mobileMultiplier,
|
||||
zIndex: 999999,
|
||||
targetElement: null,
|
||||
flakeBottom: null,
|
||||
usePositionFixed: true,
|
||||
windVariation: 0.3,
|
||||
rotationSpeed: 0.02,
|
||||
fractalTypes: 6,
|
||||
complexity: 2
|
||||
windVariation: 0.3 * mobileMultiplier,
|
||||
rotationSpeed: 0.02 * mobileMultiplier,
|
||||
fractalTypes: isMobile ? 4 : 6, // Проще фракталы на мобильных
|
||||
complexity: isMobile ? 1 : 2, // Меньшая сложность на мобильных
|
||||
isMobile: isMobile
|
||||
};
|
||||
|
||||
// Глобальные переменные
|
||||
|
|
@ -64,12 +97,12 @@ var fractalSnowstorm = function(window, document) {
|
|||
constructor(type, x, y) {
|
||||
this.type = type || Math.floor(Math.random() * config.fractalTypes);
|
||||
this.x = x || Math.random() * screenWidth;
|
||||
this.y = y || -Math.random() * 500; // Начинаем выше экрана
|
||||
this.y = y || -Math.random() * 500;
|
||||
this.vx = 0;
|
||||
this.vy = 0;
|
||||
this.rotation = Math.random() * Math.PI * 2;
|
||||
this.rotationSpeed = (Math.random() - 0.5) * config.rotationSpeed;
|
||||
this.size = 8 + Math.random() * 16;
|
||||
this.size = (8 + Math.random() * 16) * mobileMultiplier;
|
||||
this.branchCount = 6 + this.type * 2;
|
||||
this.complexity = Math.max(1, Math.min(5, config.complexity));
|
||||
this.opacity = 0.8 + Math.random() * 0.2;
|
||||
|
|
@ -101,9 +134,15 @@ var fractalSnowstorm = function(window, document) {
|
|||
top: 0;
|
||||
width: ${config.flakeWidth}px;
|
||||
height: ${config.flakeHeight}px;
|
||||
${config.isMobile ? 'image-rendering: -webkit-optimize-contrast; image-rendering: crisp-edges;' : ''}
|
||||
`;
|
||||
|
||||
this.ctx = this.canvas.getContext('2d');
|
||||
this.ctx = this.canvas.getContext('2d', { alpha: true });
|
||||
|
||||
// Для мобильных: более простая обработка
|
||||
if (config.isMobile) {
|
||||
this.ctx.imageSmoothingEnabled = false;
|
||||
}
|
||||
|
||||
this.element = document.createElement('div');
|
||||
this.element.style.cssText = `
|
||||
|
|
@ -116,6 +155,7 @@ var fractalSnowstorm = function(window, document) {
|
|||
height: ${config.flakeHeight}px;
|
||||
transform: translate(${this.x}px, ${this.y}px);
|
||||
opacity: ${this.opacity};
|
||||
${config.isMobile ? 'will-change: transform, opacity;' : ''}
|
||||
`;
|
||||
|
||||
this.element.appendChild(this.canvas);
|
||||
|
|
@ -139,6 +179,12 @@ var fractalSnowstorm = function(window, document) {
|
|||
// Очищаем canvas
|
||||
ctx.clearRect(0, 0, config.flakeWidth, config.flakeHeight);
|
||||
|
||||
// Для мобильных: более простые снежинки
|
||||
if (config.isMobile && this.type > 2) {
|
||||
this.drawSimpleFlake(ctx, centerX, centerY, currentRadius);
|
||||
return;
|
||||
}
|
||||
|
||||
// Сохраняем состояние
|
||||
ctx.save();
|
||||
ctx.translate(centerX, centerY);
|
||||
|
|
@ -149,7 +195,77 @@ var fractalSnowstorm = function(window, document) {
|
|||
const alpha = this.opacity * (this.melting ? (1 - this.meltProgress) : 1);
|
||||
const color = hexToRgba(config.snowColor, alpha);
|
||||
|
||||
// Рисуем фрактальную снежинку
|
||||
// Для мобильных: более простая отрисовка
|
||||
if (config.isMobile) {
|
||||
this.drawMobileFlake(ctx, currentRadius, color);
|
||||
} else {
|
||||
this.drawDesktopFlake(ctx, currentRadius, color);
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
drawSimpleFlake(ctx, centerX, centerY, radius) {
|
||||
// Простая снежинка для мобильных
|
||||
ctx.beginPath();
|
||||
ctx.arc(centerX, centerY, radius * 0.8, 0, Math.PI * 2);
|
||||
|
||||
const alpha = this.opacity * (this.melting ? (1 - this.meltProgress) : 1);
|
||||
const color = hexToRgba(config.snowColor, alpha);
|
||||
|
||||
ctx.fillStyle = color;
|
||||
ctx.fill();
|
||||
|
||||
// Добавляем простые лучи
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const angle = (i * Math.PI) / 3;
|
||||
const x1 = centerX + Math.cos(angle) * radius * 0.5;
|
||||
const y1 = centerY + Math.sin(angle) * radius * 0.5;
|
||||
const x2 = centerX + Math.cos(angle) * radius * 1.5;
|
||||
const y2 = centerY + Math.sin(angle) * radius * 1.5;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x1, y1);
|
||||
ctx.lineTo(x2, y2);
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeStyle = color;
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
drawMobileFlake(ctx, radius, color) {
|
||||
// Упрощенная версия для мобильных
|
||||
const branches = 6;
|
||||
|
||||
for (let i = 0; i < branches; i++) {
|
||||
const angle = (i * 2 * Math.PI) / branches;
|
||||
|
||||
// Основная ветвь
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, 0);
|
||||
ctx.lineTo(Math.cos(angle) * radius, Math.sin(angle) * radius);
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeStyle = color;
|
||||
ctx.stroke();
|
||||
|
||||
// Противоположная ветвь
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, 0);
|
||||
ctx.lineTo(Math.cos(angle + Math.PI) * radius, Math.sin(angle + Math.PI) * radius);
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeStyle = color;
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
// Центральный круг
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, radius * 0.15, 0, Math.PI * 2);
|
||||
ctx.fillStyle = color;
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
drawDesktopFlake(ctx, radius, color) {
|
||||
// Полная версия для десктопа
|
||||
const branches = this.branchCount;
|
||||
const maxDepth = this.complexity;
|
||||
|
||||
|
|
@ -181,27 +297,23 @@ var fractalSnowstorm = function(window, document) {
|
|||
const angle = (i * 2 * Math.PI) / branches;
|
||||
|
||||
// Основная ветвь
|
||||
drawBranch(0, 0, currentRadius, angle, 0, 1.5);
|
||||
drawBranch(0, 0, radius, angle, 0, 1.5);
|
||||
|
||||
// Противоположная ветвь
|
||||
drawBranch(0, 0, currentRadius, angle + Math.PI, 0, 1.5);
|
||||
drawBranch(0, 0, radius, angle + Math.PI, 0, 1.5);
|
||||
}
|
||||
|
||||
// Центральный круг
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, currentRadius * 0.15, 0, Math.PI * 2);
|
||||
ctx.arc(0, 0, radius * 0.15, 0, Math.PI * 2);
|
||||
ctx.fillStyle = color;
|
||||
ctx.fill();
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
setVelocities() {
|
||||
// Базовые скорости
|
||||
this.vx = windX + (Math.random() - 0.5) * config.vMaxX;
|
||||
this.vy = windY + Math.random() * config.vMaxY * 0.5 + 1.5; // Гарантируем падение вниз
|
||||
this.vy = windY + Math.random() * config.vMaxY * 0.5 + 1.5;
|
||||
|
||||
// Добавляем случайные колебания
|
||||
this.vx += (Math.random() - 0.5) * config.windVariation;
|
||||
}
|
||||
|
||||
|
|
@ -212,8 +324,8 @@ var fractalSnowstorm = function(window, document) {
|
|||
this.x += this.vx;
|
||||
this.y += this.vy;
|
||||
|
||||
// Обновляем вращение
|
||||
this.rotation += this.rotationSpeed;
|
||||
// Обновляем вращение (медленнее на мобильных)
|
||||
this.rotation += this.rotationSpeed * (config.isMobile ? 0.5 : 1);
|
||||
|
||||
// Обновляем мерцание
|
||||
if (this.twinkle) {
|
||||
|
|
@ -239,10 +351,11 @@ var fractalSnowstorm = function(window, document) {
|
|||
}
|
||||
}
|
||||
|
||||
// Случайное таяние в воздухе
|
||||
// Случайное таяние в воздухе (реже на мобильных)
|
||||
const meltChance = config.isMobile ? 0.0002 : 0.0005;
|
||||
if (config.useMeltEffect &&
|
||||
!this.melting &&
|
||||
Math.random() < 0.0005 &&
|
||||
Math.random() < meltChance &&
|
||||
Date.now() - this.created > 5000) {
|
||||
this.melting = true;
|
||||
}
|
||||
|
|
@ -256,7 +369,7 @@ var fractalSnowstorm = function(window, document) {
|
|||
}
|
||||
}
|
||||
|
||||
// Обновляем отображение
|
||||
// Обновляем отображение (реже перерисовываем на мобильных)
|
||||
this.updateDisplay();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -267,8 +380,11 @@ var fractalSnowstorm = function(window, document) {
|
|||
this.element.style.transform = `translate(${this.x}px, ${this.y - scrollTop}px) rotate(${this.rotation}rad)`;
|
||||
this.element.style.opacity = this.opacity * (this.melting ? (1 - this.meltProgress) : 1);
|
||||
|
||||
// Перерисовываем фрактал если нужно
|
||||
if (this.melting || this.twinkle) {
|
||||
// Перерисовываем фрактал только если нужно (реже на мобильных)
|
||||
const shouldRedraw = this.melting ||
|
||||
(this.twinkle && Math.random() < (config.isMobile ? 0.1 : 0.3));
|
||||
|
||||
if (shouldRedraw) {
|
||||
this.drawFractal();
|
||||
}
|
||||
}
|
||||
|
|
@ -279,10 +395,20 @@ var fractalSnowstorm = function(window, document) {
|
|||
this.vy = 0;
|
||||
this.rotationSpeed = 0;
|
||||
|
||||
// Плавное исчезновение
|
||||
// На мобильных сразу убираем прилипшие снежинки
|
||||
if (config.isMobile) {
|
||||
setTimeout(() => {
|
||||
if (this.element && this.element.parentNode) {
|
||||
this.element.parentNode.removeChild(this.element);
|
||||
}
|
||||
}, 2000);
|
||||
return;
|
||||
}
|
||||
|
||||
// На десктопе плавное исчезновение
|
||||
setTimeout(() => {
|
||||
if (this.element) {
|
||||
let opacity = this.element.style.opacity || 1;
|
||||
let opacity = parseFloat(this.element.style.opacity) || 1;
|
||||
const fadeInterval = setInterval(() => {
|
||||
opacity -= 0.02;
|
||||
this.element.style.opacity = opacity;
|
||||
|
|
@ -307,7 +433,11 @@ var fractalSnowstorm = function(window, document) {
|
|||
this.active = true;
|
||||
this.created = Date.now();
|
||||
this.updateDisplay();
|
||||
this.drawFractal();
|
||||
|
||||
// Перерисовываем только если не на мобильных (для производительности)
|
||||
if (!config.isMobile) {
|
||||
this.drawFractal();
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
|
|
@ -345,11 +475,10 @@ var fractalSnowstorm = function(window, document) {
|
|||
|
||||
function randomizeWind() {
|
||||
windX = (Math.random() - 0.5) * config.vMaxX;
|
||||
// Гарантируем небольшое движение по горизонтали
|
||||
if (Math.abs(windX) < 0.3) {
|
||||
windX = windX < 0 ? -0.3 : 0.3;
|
||||
}
|
||||
windY = 0; // Вертикальный ветер не нужен, снег падает сам
|
||||
windY = 0;
|
||||
}
|
||||
|
||||
function createSnowflake() {
|
||||
|
|
@ -370,15 +499,14 @@ var fractalSnowstorm = function(window, document) {
|
|||
function animate(currentTime) {
|
||||
if (disabled) return;
|
||||
|
||||
// Вычисляем дельту времени для плавной анимации
|
||||
const deltaTime = lastTime ? (currentTime - lastTime) / 1000 : 0.016;
|
||||
lastTime = currentTime;
|
||||
|
||||
updateScreenSize();
|
||||
updateScroll();
|
||||
|
||||
// Обновляем ветер (медленные изменения)
|
||||
if (Math.random() < 0.01) {
|
||||
// Медленнее меняем ветер на мобильных
|
||||
if (Math.random() < (config.isMobile ? 0.005 : 0.01)) {
|
||||
windX += (Math.random() - 0.5) * 0.2;
|
||||
windX = Math.max(-config.vMaxX, Math.min(config.vMaxX, windX));
|
||||
}
|
||||
|
|
@ -395,15 +523,16 @@ var fractalSnowstorm = function(window, document) {
|
|||
}
|
||||
}
|
||||
|
||||
// Добавляем новые снежинки если нужно
|
||||
// Добавляем новые снежинки если нужно (реже на мобильных)
|
||||
const addChance = config.isMobile ? 0.2 : 0.3;
|
||||
if (activeCount < config.flakesMaxActive &&
|
||||
snowflakes.length < config.flakesMax &&
|
||||
Math.random() < 0.3) {
|
||||
Math.random() < addChance) {
|
||||
createSnowflake();
|
||||
}
|
||||
|
||||
// Удаляем неактивные снежинки (редко)
|
||||
if (Math.random() < 0.01) {
|
||||
// Удаляем неактивные снежинки (реже на мобильных)
|
||||
if (Math.random() < (config.isMobile ? 0.005 : 0.01)) {
|
||||
snowflakes = snowflakes.filter(flake =>
|
||||
flake.element && flake.element.parentNode &&
|
||||
(flake.active || parseFloat(flake.element.style.opacity) > 0)
|
||||
|
|
@ -423,7 +552,6 @@ var fractalSnowstorm = function(window, document) {
|
|||
|
||||
function handleResize() {
|
||||
updateScreenSize();
|
||||
// При ресайзе немного меняем ветер
|
||||
windX *= 0.9;
|
||||
}
|
||||
|
||||
|
|
@ -445,29 +573,29 @@ var fractalSnowstorm = function(window, document) {
|
|||
const publicAPI = {
|
||||
start: function() {
|
||||
if (active) {
|
||||
console.log('Snowstorm уже запущен');
|
||||
console.log('Снегопад уже запущен');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Запуск снегопада...');
|
||||
if (!isSnowSeason()) {
|
||||
console.log('Снегопад доступен только с 1 декабря по 31 января');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Запуск зимнего снегопада...', config.isMobile ? '(мобильная версия)' : '(десктоп версия)');
|
||||
|
||||
// Определяем целевой элемент
|
||||
targetElement = config.targetElement || document.body;
|
||||
|
||||
// Проверяем, что целевой элемент существует
|
||||
if (!targetElement) {
|
||||
console.error('Не могу найти целевой элемент');
|
||||
return;
|
||||
}
|
||||
|
||||
// Инициализация
|
||||
updateScreenSize();
|
||||
randomizeWind();
|
||||
|
||||
// Создаем начальные снежинки
|
||||
createInitialSnowflakes();
|
||||
|
||||
// Настраиваем обработчики событий
|
||||
if (config.followMouse) {
|
||||
window.addEventListener('mousemove', handleMouseMove);
|
||||
}
|
||||
|
|
@ -475,13 +603,12 @@ var fractalSnowstorm = function(window, document) {
|
|||
window.addEventListener('scroll', handleScroll);
|
||||
document.addEventListener('visibilitychange', handleVisibilityChange);
|
||||
|
||||
// Запускаем анимацию
|
||||
active = true;
|
||||
disabled = false;
|
||||
lastTime = 0;
|
||||
timer = requestAnimationFrame(animate);
|
||||
|
||||
console.log(`Снегопад запущен. Снежинок: ${snowflakes.length}, Экран: ${screenWidth}x${screenHeight}`);
|
||||
console.log(`Снегопад запущен. Снежинок: ${snowflakes.length}, Размер: ${config.flakeWidth}px`);
|
||||
},
|
||||
|
||||
stop: function() {
|
||||
|
|
@ -489,28 +616,23 @@ var fractalSnowstorm = function(window, document) {
|
|||
|
||||
console.log('Остановка снегопада...');
|
||||
|
||||
// Останавливаем анимацию
|
||||
cancelAnimationFrame(timer);
|
||||
timer = null;
|
||||
|
||||
// Удаляем обработчики
|
||||
window.removeEventListener('mousemove', handleMouseMove);
|
||||
window.removeEventListener('resize', handleResize);
|
||||
window.removeEventListener('scroll', handleScroll);
|
||||
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
||||
|
||||
// Удаляем все снежинки
|
||||
snowflakes.forEach(flake => flake.destroy());
|
||||
snowflakes = [];
|
||||
|
||||
active = false;
|
||||
console.log('Снегопад остановлен');
|
||||
},
|
||||
|
||||
pause: function() {
|
||||
if (disabled) return;
|
||||
disabled = true;
|
||||
console.log('Снегопад приостановлен');
|
||||
},
|
||||
|
||||
resume: function() {
|
||||
|
|
@ -520,7 +642,6 @@ var fractalSnowstorm = function(window, document) {
|
|||
if (active) {
|
||||
timer = requestAnimationFrame(animate);
|
||||
}
|
||||
console.log('Снегопад возобновлен');
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
|
|
@ -534,7 +655,6 @@ var fractalSnowstorm = function(window, document) {
|
|||
updateConfig: function(newConfig) {
|
||||
Object.assign(config, newConfig);
|
||||
if (active) {
|
||||
// Пересоздаем снежинки при изменении конфига
|
||||
this.stop();
|
||||
setTimeout(() => this.start(), 100);
|
||||
}
|
||||
|
|
@ -549,22 +669,30 @@ var fractalSnowstorm = function(window, document) {
|
|||
totalFlakes: snowflakes.length,
|
||||
activeFlakes: snowflakes.filter(f => f.active).length,
|
||||
screenSize: { width: screenWidth, height: screenHeight },
|
||||
wind: { x: windX, y: windY }
|
||||
wind: { x: windX, y: windY },
|
||||
isMobile: config.isMobile,
|
||||
isSeason: isSnowSeason()
|
||||
};
|
||||
},
|
||||
|
||||
isSeason: function() {
|
||||
return isSnowSeason();
|
||||
}
|
||||
};
|
||||
|
||||
// Автозапуск
|
||||
if (config.autoStart) {
|
||||
// Автозапуск только в сезон
|
||||
if (config.autoStart && isSnowSeason()) {
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
console.log('DOM загружен, запускаю снегопад...');
|
||||
setTimeout(() => publicAPI.start(), 500);
|
||||
console.log('DOM загружен, запускаю зимний снегопад...');
|
||||
setTimeout(() => publicAPI.start(), 1000);
|
||||
});
|
||||
} else {
|
||||
console.log('DOM уже загружен, запускаю снегопад...');
|
||||
setTimeout(() => publicAPI.start(), 500);
|
||||
console.log('Запускаю зимний снегопад...');
|
||||
setTimeout(() => publicAPI.start(), 1000);
|
||||
}
|
||||
} else if (config.autoStart) {
|
||||
console.log('Сейчас не сезон для снегопада (только с 1 декабря по 31 января)');
|
||||
}
|
||||
|
||||
return publicAPI;
|
||||
|
|
@ -573,14 +701,23 @@ var fractalSnowstorm = function(window, document) {
|
|||
// Создаем глобальный объект для доступа
|
||||
window.fractalSnowstorm = fractalSnowstorm;
|
||||
|
||||
// Простая версия для быстрого запуска (если сложная не работает)
|
||||
window.simpleSnowfall = function() {
|
||||
console.log('Запуск простого снегопада...');
|
||||
// Альтернативная простая версия (тоже с сезонным ограничением)
|
||||
window.createSimpleSnow = function() {
|
||||
// Проверяем сезон
|
||||
const now = new Date();
|
||||
const month = now.getMonth() + 1;
|
||||
if (!(month === 12 || month === 1 || (month === 2 && now.getDate() === 1))) {
|
||||
console.log('Простой снегопад доступен только зимой');
|
||||
return { stop: function() {} };
|
||||
}
|
||||
|
||||
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
|
||||
const sizeMultiplier = isMobile ? 0.4 : 1;
|
||||
|
||||
const config = {
|
||||
flakes: 100,
|
||||
speed: 3,
|
||||
size: 20,
|
||||
flakes: isMobile ? 50 : 150,
|
||||
speed: 2 * sizeMultiplier,
|
||||
size: 15 * sizeMultiplier,
|
||||
color: '#ffffff',
|
||||
zIndex: 999999
|
||||
};
|
||||
|
|
@ -595,21 +732,25 @@ window.simpleSnowfall = function() {
|
|||
height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: ${config.zIndex};
|
||||
overflow: hidden;
|
||||
`;
|
||||
document.body.appendChild(container);
|
||||
|
||||
// Создаем простые снежинки
|
||||
for (let i = 0; i < config.flakes; i++) {
|
||||
const flake = document.createElement('div');
|
||||
const flakeSize = config.size * (0.5 + Math.random() * 0.5);
|
||||
const opacity = 0.3 + Math.random() * 0.5;
|
||||
|
||||
flake.style.cssText = `
|
||||
position: absolute;
|
||||
width: ${config.size}px;
|
||||
height: ${config.size}px;
|
||||
width: ${flakeSize}px;
|
||||
height: ${flakeSize}px;
|
||||
background: ${config.color};
|
||||
border-radius: 50%;
|
||||
opacity: ${0.3 + Math.random() * 0.7};
|
||||
filter: blur(${Math.random() * 3}px);
|
||||
top: -${config.size}px;
|
||||
opacity: ${opacity};
|
||||
filter: blur(${Math.random() * 2}px);
|
||||
top: -${flakeSize}px;
|
||||
left: ${Math.random() * 100}%;
|
||||
`;
|
||||
|
||||
|
|
@ -620,11 +761,16 @@ window.simpleSnowfall = function() {
|
|||
x: Math.random() * window.innerWidth,
|
||||
y: Math.random() * -window.innerHeight,
|
||||
speed: config.speed * (0.5 + Math.random()),
|
||||
sway: (Math.random() - 0.5) * 2
|
||||
sway: (Math.random() - 0.5) * 1.5 * sizeMultiplier,
|
||||
size: flakeSize,
|
||||
rotation: Math.random() * Math.PI * 2,
|
||||
rotationSpeed: (Math.random() - 0.5) * 0.02
|
||||
});
|
||||
}
|
||||
|
||||
// Анимация
|
||||
let animationId = null;
|
||||
|
||||
function animate() {
|
||||
const width = window.innerWidth;
|
||||
const height = window.innerHeight;
|
||||
|
|
@ -632,31 +778,49 @@ window.simpleSnowfall = function() {
|
|||
flakes.forEach(flake => {
|
||||
flake.y += flake.speed;
|
||||
flake.x += flake.sway * Math.sin(flake.y * 0.01);
|
||||
flake.rotation += flake.rotationSpeed;
|
||||
|
||||
// Если снежинка ушла за нижний край, возвращаем ее наверх
|
||||
// Если снежинка ушла за нижний край
|
||||
if (flake.y > height) {
|
||||
flake.y = -config.size;
|
||||
flake.y = -flake.size;
|
||||
flake.x = Math.random() * width;
|
||||
}
|
||||
|
||||
// Если снежинка ушла за боковые края, возвращаем с другой стороны
|
||||
if (flake.x > width) flake.x = 0;
|
||||
if (flake.x < 0) flake.x = width;
|
||||
// Если снежинка ушла за боковые края
|
||||
if (flake.x > width + flake.size) flake.x = -flake.size;
|
||||
if (flake.x < -flake.size) flake.x = width + flake.size;
|
||||
|
||||
flake.element.style.transform = `translate(${flake.x}px, ${flake.y}px)`;
|
||||
flake.element.style.transform = `
|
||||
translate(${flake.x}px, ${flake.y}px)
|
||||
rotate(${flake.rotation}rad)
|
||||
`;
|
||||
|
||||
// Легкое мерцание
|
||||
if (Math.random() < 0.01) {
|
||||
flake.element.style.opacity = 0.3 + Math.random() * 0.4;
|
||||
}
|
||||
});
|
||||
|
||||
requestAnimationFrame(animate);
|
||||
animationId = requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
animate();
|
||||
|
||||
return {
|
||||
stop: function() {
|
||||
container.remove();
|
||||
if (animationId) {
|
||||
cancelAnimationFrame(animationId);
|
||||
}
|
||||
if (container.parentNode) {
|
||||
container.parentNode.removeChild(container);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Если фрактальный снег не работает, можно вызвать простой вариант:
|
||||
// window.simpleSnowfall();
|
||||
// Автоматический запуск простой версии если фрактальная не доступна
|
||||
if (window.fractalSnowstorm && window.fractalSnowstorm.isSeason && window.fractalSnowstorm.isSeason()) {
|
||||
console.log('Зимний сезон - снегопад будет запущен автоматически');
|
||||
} else {
|
||||
console.log('Сейчас не зимний сезон для снегопада');
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue