Skip to content

Commit d98d4a0

Browse files
committed
feat: улучшена логика попапа и автоматический сбор аналитики
1 parent 2917e04 commit d98d4a0

File tree

2 files changed

+114
-52
lines changed

2 files changed

+114
-52
lines changed

src/components/CookieConsent.astro

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ const addNoscriptPixel = (): void => {
5555
};
5656

5757
const yandexMetrika = (): void => {
58-
if (!hasConsented() || yandexInitialized) return;
58+
if (yandexInitialized) return;
5959
yandexInitialized = true;
6060
addNoscriptPixel();
6161

@@ -96,7 +96,7 @@ const addMailruNoscriptPixel = (): void => {
9696
};
9797

9898
const mailruCounter = (): void => {
99-
if (!hasConsented() || mailruInitialized) return;
99+
if (mailruInitialized) return;
100100
mailruInitialized = true;
101101
addMailruNoscriptPixel();
102102

@@ -134,17 +134,13 @@ if (overlay) {
134134
cookieButton?.addEventListener('click', () => {
135135
overlay?.classList.remove('active');
136136
localStorage.setItem(COOKIE_AGREED_KEY, 'true');
137-
yandexMetrika();
138-
mailruCounter();
139-
140137
removeBodyPadding();
141138
});
142139

143140
document.addEventListener('DOMContentLoaded', () => {
144-
if (hasConsented()) {
145-
yandexMetrika();
146-
mailruCounter();
147-
}
141+
// Запускаем аналитику сразу при загрузке страницы
142+
yandexMetrika();
143+
mailruCounter();
148144
});
149145
</script>
150146

src/pages/index.astro

Lines changed: 109 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -309,17 +309,52 @@ import WORKDECOR from '../assets/img/work-decor.webp';
309309

310310
</Layout>
311311

312+
<!--
313+
Логика показа попапа консультации:
314+
315+
УСЛОВИЯ ПОКАЗА:
316+
1. Пользователь проскроллил ниже первого экрана (на высоту viewport)
317+
2. Пользователь не скроллит 2+ секунд
318+
3. Пользователь не кликает 3+ секунд
319+
4. Вкладка активна (не скрыта)
320+
5. Прошло 14 секунд с момента последней активности
321+
322+
ТАЙМЕРЫ:
323+
- 2 сек - пауза в скролле (если пользователь перестал скроллить)
324+
- 3 сек - пауза в кликах (если пользователь перестал кликать)
325+
- 14 сек - основной таймер показа попапа
326+
327+
ОТМЕНА ТАЙМЕРОВ:
328+
- При любом скролле
329+
- При любом клике
330+
- При скрытии вкладки
331+
- При изменении размера окна
332+
- После показа попапа (один раз за сессию)
333+
334+
ЦЕЛЬ: Показывать попап только когда пользователь активно читает контент,
335+
но не мешать взаимодействию с интерфейсом.
336+
-->
312337
<script>
313338
document.addEventListener('DOMContentLoaded', () => {
314-
const section = document.querySelector<HTMLElement>('#features');
315-
if (!section) return;
316-
317339
let timerId: number | null = null;
318340
let hasTriggered = false;
341+
let isScrolling = false;
342+
let scrollTimeout: number | null = null;
343+
let isClicking = false;
344+
let clickTimeout: number | null = null;
319345

320346
const startTimer = () => {
321-
if (hasTriggered || timerId) return;
322-
timerId = window.setTimeout(triggerPopup, 3000) ;
347+
if (hasTriggered || timerId || isClicking) return;
348+
349+
// Проверяем, что пользователь проскроллил хотя бы на высоту экрана
350+
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
351+
const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
352+
353+
if (scrollTop < viewportHeight) {
354+
return; // Не показываем попап на первом экране
355+
}
356+
357+
timerId = window.setTimeout(triggerPopup, 14000); // 14 секунд
323358
};
324359

325360
const cancelTimer = () => {
@@ -336,58 +371,89 @@ document.addEventListener('DOMContentLoaded', () => {
336371
window.dispatchEvent(new CustomEvent('open-contact-popup'));
337372

338373
cancelTimer();
339-
observer.disconnect();
340374
window.removeEventListener('scroll', onScroll);
341375
window.removeEventListener('resize', onResize);
342376
window.removeEventListener('visibilitychange', onVisibilityChange);
343-
window.removeEventListener('hashchange', onHashChange);
377+
document.removeEventListener('click', onClick);
344378
};
345379

346-
347-
const debounce = <T extends (...args: any[]) => void>(fn: T, wait: number) => {
348-
let timeout: number | null = null;
349-
return (...args: Parameters<T>) => {
350-
if (timeout) clearTimeout(timeout);
351-
timeout = window.setTimeout(() => fn(...args), wait);
352-
};
380+
const onScroll = () => {
381+
isScrolling = true;
382+
383+
// Отменяем предыдущий таймер скролла
384+
if (scrollTimeout) {
385+
clearTimeout(scrollTimeout);
386+
}
387+
388+
// Запускаем новый таймер - если через 2 секунды скролл остановится
389+
scrollTimeout = window.setTimeout(() => {
390+
isScrolling = false;
391+
// Если вкладка активна и пользователь не скроллит и не кликает - запускаем основной таймер
392+
if (!document.hidden && !isClicking) {
393+
startTimer();
394+
}
395+
}, 2000);
353396
};
354397

355-
const onScrollEnd = debounce(() => {
356-
const rect = section.getBoundingClientRect();
357-
const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
358-
const near = !(rect.bottom < -200 || rect.top > viewportHeight + 200);
398+
const onClick = () => {
399+
isClicking = true;
400+
401+
// Отменяем основной таймер при клике
402+
cancelTimer();
403+
404+
// Отменяем предыдущий таймер клика
405+
if (clickTimeout) {
406+
clearTimeout(clickTimeout);
407+
}
408+
409+
// Запускаем новый таймер - если через 3 секунды не будет кликов
410+
clickTimeout = window.setTimeout(() => {
411+
isClicking = false;
412+
// Если вкладка активна и пользователь не скроллит - запускаем основной таймер
413+
if (!document.hidden && !isScrolling) {
414+
startTimer();
415+
}
416+
}, 3000);
417+
};
359418

360-
if (near) startTimer();
361-
else cancelTimer();
362-
}, 150);
419+
const onResize = () => {
420+
// При изменении размера окна отменяем таймеры
421+
cancelTimer();
422+
if (scrollTimeout) {
423+
clearTimeout(scrollTimeout);
424+
scrollTimeout = null;
425+
}
426+
if (clickTimeout) {
427+
clearTimeout(clickTimeout);
428+
clickTimeout = null;
429+
}
430+
};
363431

364-
const onScroll = () => onScrollEnd();
365-
const onResize = () => onScrollEnd();
366432
const onVisibilityChange = () => {
367-
if (document.hidden) cancelTimer();
368-
else onScrollEnd();
369-
};
370-
const onHashChange = () => {
371-
cancelTimer();
372-
onScrollEnd();
433+
if (document.hidden) {
434+
// Если вкладка скрыта - отменяем все таймеры
435+
cancelTimer();
436+
if (scrollTimeout) {
437+
clearTimeout(scrollTimeout);
438+
scrollTimeout = null;
439+
}
440+
if (clickTimeout) {
441+
clearTimeout(clickTimeout);
442+
clickTimeout = null;
443+
}
444+
} else {
445+
// Если вкладка стала активной и пользователь не скроллит и не кликает - запускаем таймер
446+
if (!isScrolling && !isClicking) {
447+
startTimer();
448+
}
449+
}
373450
};
374451

375-
const observer = new IntersectionObserver(
376-
(entries) => {
377-
const entry = entries[0];
378-
if (entry.isIntersecting && !hasTriggered) startTimer();
379-
else cancelTimer();
380-
},
381-
{ threshold: 0.0, rootMargin: '400px 0px 400px 0px' }
382-
);
383-
384-
observer.observe(section);
385-
window.addEventListener('scroll', onScroll, { passive: true });
452+
// Запускаем отслеживание скролла и кликов
453+
window.addEventListener('scroll', onScroll);
386454
window.addEventListener('resize', onResize);
387455
window.addEventListener('visibilitychange', onVisibilityChange);
388-
window.addEventListener('hashchange', onHashChange);
389-
390-
onScrollEnd();
456+
document.addEventListener('click', onClick);
391457
});
392458

393459
</script>

0 commit comments

Comments
 (0)