From 3605551f1653edb9229613bba3a6b2568d628352 Mon Sep 17 00:00:00 2001 From: Valentin Silyutin Date: Mon, 2 Feb 2026 18:22:51 +0400 Subject: [PATCH] @advdominion/scroll-to --- packages/scroll-to/README.md | 33 +++++++++++++++++++++++++++ packages/scroll-to/index.js | 40 +++++++++++++++++++++++++++++++++ packages/scroll-to/package.json | 14 ++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 packages/scroll-to/README.md create mode 100644 packages/scroll-to/index.js create mode 100644 packages/scroll-to/package.json diff --git a/packages/scroll-to/README.md b/packages/scroll-to/README.md new file mode 100644 index 0000000..357d98a --- /dev/null +++ b/packages/scroll-to/README.md @@ -0,0 +1,33 @@ +# scroll-to + +Плавный скролл до элемента с поддержкой нативного события `scrollend` и гибкой настройкой отступов. + +## Использование + +```js +import { scrollTo } from '@advdominion/scroll-to'; + +// Базовый скролл до элемента +await scrollTo(document.querySelector('#example')); + +// Скролл с отступом 100px (например, для фиксированной шапки) +await scrollTo(document.querySelector('#example'), 100, true); + +// Скролл с отступом в 10% от высоты экрана +await scrollTo(document.querySelector('#example'), 10); +``` + +## Параметры + +| Параметр | Тип | По умолчанию | Описание | +| :--- | :--- | :--- | :--- | +| **$el** | `HTMLElement` | — | DOM-элемент, к которому нужно прокрутить страницу | +| **offset** | `number` | `0` | Величина отступа сверху от целевого элемента | +| **offsetInPx** | `boolean` | `false` | Тип отступа: `true` — в пикселях, `false` — в процентах от высоты вьюпорта | + +## Как это работает + +1. **Точность:** Использует нативное событие `scrollend`, которое срабатывает строго после завершения анимации прокрутки. +2. **Безопасность:** Если браузер не поддерживает `scrollend` или анимация прервана, сработает автоматический fallback через 1500 мс. +3. **Плавность:** Использует нативный `behavior: 'smooth'`. +4. **Очистка данных:** Автоматически удаляет слушатели событий и таймеры после завершения или отмены скролла. diff --git a/packages/scroll-to/index.js b/packages/scroll-to/index.js new file mode 100644 index 0000000..2f8cd23 --- /dev/null +++ b/packages/scroll-to/index.js @@ -0,0 +1,40 @@ +export const scrollTo = ($el, offset = 0, offsetInPx = false) => { + return new Promise((resolve) => { + if (!$el) { + console.warn('Элемент не найден'); + resolve(); + return; + } + + const elementPosition = $el.getBoundingClientRect().top + window.scrollY; + const topOffset = offsetInPx ? offset : (window.innerHeight * offset) / 100; + const targetScrollTop = elementPosition - topOffset; + + // Если элемент уже в нужной позиции + if (Math.abs(window.scrollY - targetScrollTop) < 1) { + resolve(); + return; + } + + let resolved = false; // На случай, если scrollend и fallback запустятся одновременно + let fallbackTimeout; // eslint-disable-line prefer-const + + const done = () => { + if (resolved) return; + resolved = true; + + window.removeEventListener('scrollend', done); + clearTimeout(fallbackTimeout); + + resolve(); + }; + + window.addEventListener('scrollend', done, { once: true }); + fallbackTimeout = setTimeout(done, 1500); + + window.scrollTo({ + top: targetScrollTop, + behavior: 'smooth', + }); + }); +}; diff --git a/packages/scroll-to/package.json b/packages/scroll-to/package.json new file mode 100644 index 0000000..103cda4 --- /dev/null +++ b/packages/scroll-to/package.json @@ -0,0 +1,14 @@ +{ + "name": "@advdominion/scroll-to", + "version": "1.0.0", + "type": "module", + "main": "index.js", + "repository": { + "type": "git", + "url": "https://gitea.optiweb.ru/public/frontend.git" + }, + "license": "MIT", + "publishConfig": { + "access": "public" + } +}