6395
редагувань
Admin (обговорення | внесок) Немає опису редагування |
Admin (обговорення | внесок) Немає опису редагування |
||
| Рядок 48: | Рядок 48: | ||
var $items = $lbox.find('.l-box-item'); | var $items = $lbox.find('.l-box-item'); | ||
var HEADER_OFFSET = | var HEADER_OFFSET = 80; | ||
var DURATION = 320; // фіксована тривалість, мс | |||
var isAnimating = false; | var isAnimating = false; | ||
var rafId = null; | |||
function getTargetEl(target) { | function getTargetEl(target) { | ||
if (target === 'top') return null; | if (target === 'top') return null; | ||
var el = document.getElementById(target); | var el = document.getElementById(target); | ||
if (!el) return null; | if (!el) return null; | ||
var $ | var $heading = $(el).closest('h1, h2, h3, h4'); | ||
return $heading.length ? $heading[0] : el; | return $heading.length ? $heading[0] : el; | ||
} | } | ||
| Рядок 65: | Рядок 65: | ||
var el = getTargetEl(target); | var el = getTargetEl(target); | ||
if (!el) return null; | if (!el) return null; | ||
var top = el.getBoundingClientRect().top + window.pageYOffset - HEADER_OFFSET; | |||
// не даємо вийти за межі документа | |||
var max = document.documentElement.scrollHeight - window.innerHeight; | |||
return Math.max(0, Math.min(top, max)); | |||
} | } | ||
function easeOutCubic(t) { return 1 - Math.pow(1 - t, 3); } | |||
function scrollToTarget(target) { | |||
if (rafId) cancelAnimationFrame(rafId); | |||
var start = window.pageYOffset; | |||
var end = getTargetTop(target); | |||
if (end === null) return; | |||
if (Math.abs(end - start) < 2) { isAnimating = false; return; } | |||
isAnimating = true; | isAnimating = true; | ||
var startTime = null; | |||
if ( | function frame(now) { | ||
if (startTime === null) startTime = now; | |||
var elapsed = now - startTime; | |||
var | var t = Math.min(1, elapsed / DURATION); | ||
if ( | |||
// Переміряємо ціль на льоту — layout міг зсунутись | |||
var currentEnd = getTargetTop(target); | |||
if (currentEnd === null) currentEnd = end; | |||
window.scrollTo(0, start + (currentEnd - start) * easeOutCubic(t)); | |||
if (t < 1) { | |||
rafId = requestAnimationFrame(frame); | |||
} else { | |||
rafId = null; | |||
isAnimating = false; | isAnimating = false; | ||
updateActive(); | updateActive(); | ||
} | } | ||
} | } | ||
rafId = requestAnimationFrame(frame); | |||
} | } | ||
function updateActive() { | function updateActive() { | ||
if (isAnimating) return; | if (isAnimating) return; | ||
var scrollY = window.pageYOffset | var scrollY = window.pageYOffset; | ||
var current = null; | var current = null; | ||
$items.each(function () { | $items.each(function () { | ||
var | var top = getTargetTop($(this).data('target')); | ||
if (top !== null && top <= scrollY + 10) current = $(this); | |||
if (top !== null && top | |||
}); | }); | ||
| Рядок 110: | Рядок 123: | ||
$items.on('click', function () { | $items.on('click', function () { | ||
// Блокуємо повторний клік на вже активному пункті | |||
if ($(this).hasClass('active')) return; | |||
if (isAnimating) return; | |||
var target = $(this).data('target'); | var target = $(this).data('target'); | ||
$items.removeClass('active'); | $items.removeClass('active'); | ||
| Рядок 128: | Рядок 145: | ||
$(window).on('scroll', function () { | $(window).on('scroll', function () { | ||
if (!ticking) { | if (!ticking) { | ||
requestAnimationFrame(function () { | |||
updateActive(); | updateActive(); | ||
ticking = false; | ticking = false; | ||
| Рядок 138: | Рядок 155: | ||
$(window).on('resize', updateActive); | $(window).on('resize', updateActive); | ||
$(document).on('mcc:content-loaded', updateActive); | $(document).on('mcc:content-loaded', updateActive); | ||
$(window).on('load', updateActive); | $(window).on('load', updateActive); | ||
if (document.fonts && document.fonts.ready) { | if (document.fonts && document.fonts.ready) { | ||