История npm‑библиотеки morphing‑scroll
- вторник, 22 сентября 2026 г. в 00:00:06
Хочу рассказать о своей npm‑библиотеке — это мой второй и самый сложный npm‑проект на момент написания статьи. Глубоко в код погружаться не буду — поделюсь историей создания и мотивацией.

Примерно в 2015 году я получил свою первую работу UI/UX‑дизайнером. Рисовал интерфейсы мобильных и десктопных приложений в популярном тогда Sketch, на выделенном мне Mac, и был этому искренне рад. Шло всё неплохо: я соблюдал гайдлайны iOS и Android, а когда идея приложения во мне откликалась, старался уделить дизайну больше внимания — наполнял интерфейс своими иллюстрациями (я художник по образованию) и необычными решениями отдельных элементов. Иногда выходило вполне достойно.
Но был один элемент, изменения которого не принимались никогда, — полоса прокрутки. Разработчики, которым я передавал макет, очень не любили, когда её трогали, и прямо об этом говорили. Скролл, а особенно его бегунок, был тёмным лесом, в который лучше не заходить. И когда я в очередной раз слышал «это сделать нельзя», я начинал чувствовать себя тем самым парнем, который весело рисует облачка на кнопках под звуки поп‑исполнителей, пока разработчик страдает, перенося художественный замысел в код. Именно так макет UI и финальная реализация прощаются друг с другом.

Шло время, и постепенно я начал не только рисовать макеты, но и верстать их на React. Так я всё глубже погружался в разработку — и однажды встретился со своим старым врагом: скроллом в браузере. Тогда я и ощутил на себе тот урон, который терпели тысячи разработчиков, разбивая труды дизайнеров о скалы невозможного.
Но что же с ним не так, о каких ограничениях речь? Поговорим о тех самых проклятьях, обрекающих на страдания невинных разработчиков.

Когда‑то давно у всех был один язык, все друг друга понимали, и о кроссплатформенных бедах никто не слышал. Потом разработчики браузеров, при отсутствие единой спецификации и стандартов, начали делать одно и то же, но по‑разному. Как итог один и тот же элемент выглядит немного иначе в зависимости от того, чем вы открыли страницу.

Кастомизации этот элемент почти не поддаётся, а то, что есть, изменениями назвать трудно: цвет через CSS да три варианта ширины. Есть ещё ::-webkit-scrollbar, но и это лишь покраска: поставить вместо бегунка свой элемент — с анимацией или реакцией на движение, просто нельзя. Дизайнеру же остаётся только чувство беспомощности. Странно, правда? Элемент, который пользователь видит на каждой странице, почти нельзя оформить.

И вот, имея при себе знания JS и React, в 2023 году я решил дать бой этим бедствиям. Я не до конца понимал, какой длинный и тернистый путь меня ждёт, но награда того стоила: где‑то вдалеке виднелся тот самый UI мечты, где вместо скучного серого бегунка можно поставить свой — любой формы и стиля, светящийся при наведении, растущий при нажатии, какой угодно. Да хоть фаербол вместо бегунка — я должен иметь возможность это сделать. Зачем эти ограничения?!

Цель ясна, а вот способа её реализации пока не было. Хотелось, чтобы библиотека работала сразу, из коробки. С моими знаниями я мог написать React‑компонент, и это было неплохим решением, но оставался вопрос стилизации: стили по умолчанию должны были приезжать вместе с компонентом. Можно было бы при монтировании рендерить в HTML тег <style>, но это ненадёжно, значит, стили должны жить внутри компонента — и ответ оказался предельно простым: инлайн‑стили. Я не фанат инлайн‑стилей, но здесь они разом решали целый пласт проблем: стилизация сразу оказывалась на месте и именно там, где нужно. Это заставило меня хорошо продумать элементы скролла и попутно ответить на вопросы реализации. Было также ясно, что без TypeScript я не справлюсь: в какой‑то момент просто не смогу поддерживать проект. Ну что ж, стек ясен — делаем.

В первой версии я пытался собрать странное смешение: браузерный скролл со скрытым бегунком и моим элементом вместо него. Он умел только вертикальную прокрутку и горизонтальную, которая по сути была повёрнутой вертикальной. Очень быстро я упёрся во множество ограничений и понял, что писать придётся всё самому — и обработку событий прокрутки, и анимацию движения. А ещё нужно было учесть прокрутку по горизонтали и вертикали одновременно. Стало ясно, что текущая реализация несерьёзна и больше похожа на первые шаги, чтобы нащупать направление. Что делать дальше, я уже понимал, но немного выгорел и взял паузу, чтобы всё обдумать и вернуться со свежими силами.

Сев за вторую версию, я уже лучше понимал, что библиотека должна уметь и что для этого нужно. Я глубже погрузился в разработку и в создание внутренних механик, и у меня начало получаться. Но чем дольше я работал, тем больше идей приходило в голову, и список «что можно сделать ещё» вышел внушительным. Например, я понял, что написанное легко превращает компонент не только в скролл, но и в слайдер, — и добавил такой режим, а с ним стрелки и отдельную анимацию перелистывания. А по опыту работы со списками я знал, что нужны и ленивая отрисовка (объект появляется, когда до него докрутили, и остаётся), и виртуализация (в DOM живёт только то, что видно).
В какой‑то момент самым сложным стало не наличие механик, а то, как они уживаются друг с другом.
Простой пример. На странице есть горизонтальная лента карточек, а сама страница прокручивается вертикально. Пользователь ставит на ленту палец и ведёт — кому достаётся жест? Если палец пошёл вбок — ленте, если вниз — странице, и решать это нужно по первым же пикселям движения, пока человек ничего не заметил. А если он тянет не карточки, а бегунок ленты? Тогда жест принадлежит только ей, и страница не должна сдвинуться ни на пиксель. По отдельности каждое правило простое, но мышь, палец, колесо, клавиатура, вложенные скроллы и слайдеры начинают спорить друг с другом — и больше всего времени уходило именно на их регулировку.
Всё это сводило с ума и требовало огромного количества проверок и тестов. Стоило мне решить, что сделано всё, что можно, как я придумывал что‑то классное, что немедленно хотелось добавить, — и это начинало раздражать. Так библиотека разрослась, а API перестал быть цельным. Я снова выгорел и взял паузу.

К третьей версии подступиться было трудно: всего накопилось много, но надо было наводить порядок и переделывать API. Тут я подключил к разработке ИИ, чтобы ничего не упустить при переделке и покрыть код тестами — иначе, чиня одно, я неизбежно ломал другое. И вот на горизонте забрезжил финальный API, который меня устраивал, а тесты были готовы. Добавилось и несколько новых фич: плитка из элементов разных размеров, бесконечная прокрутка и список, который начинается справа, — для языков, где читают справа налево.
Добавлю, что название выбрано не случайно: компонент может вести себя совершенно по‑разному и даже вовсе не походить на скролл — всё зависит от вашего воображения.
На момент написания статьи вышла третья версия библиотеки, и API стабилен.

Всё получилось, но по дороге библиотека заставила меня ответить на множество вопросов о том, как скролл должен себя вести и что уметь.
Читая эту статью, можно задаться вопросом: зачем столько трудов ради одного элемента и кому это нужно? Берите библиотеку, если хотите сделать что‑то необычное, а не то, что даёт браузер по умолчанию. Ещё она пригодится разработчикам игр — там стандартных решений почти не бывает.
От себя добавлю: если вас когда‑то пугали злым драконом, к встрече с которым вы были не готовы из‑за недостаточной квалификации, но который всё время попадается на пути, — попробуйте бросить ему вызов при следующей встрече. Как минимум вы приобретёте новые знания и навыки. Как максимум — сделаете мир немного дружелюбнее или просто интереснее.
Отдельно хочу поблагодарить коллег за терпение, пока я внедрял эту библиотеку на проде.
Посмотреть и почитать документацию, а так же собрать свой скролл можно [на странице morphing-scroll].
Удачи и успехов в разработке!
PS: тот самый [фаербол вместо бегунка]
