Форум программистов, компьютерный форум, киберфорум
mr_dramm
Войти
Регистрация
Восстановить пароль

smooth scroll

Запись от mr_dramm размещена 14.03.2023 в 12:14
Показов 3024 Комментарии 15
Метки css, html, javascript

Несложный smooth scroll

css анимация лучше работает чем js


Пример на css transform

горизонтальный codepen

Кликните здесь для просмотра всего текста

PHP/HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
<!DOCTYPE html>
<html>
 
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title></title>
    <style>
    * {
        padding: 0;
        margin: 0;
        box-sizing: border-box;
    }
 
    .container {
        overflow: hidden;
        display: flex;
        flex-direction: column;
    }
 
    .scrollWrapper {
        flex: 1;
        background-color: gray;
    }
 
    .hScroolBox {
        flex: 0;
        overflow-x: auto;
        height: 20px;
    }
 
    .hiddenScrollBar {
        height: 1px;
    }
 
    .box {
        position: relative;
        font-size: 4rem;
        font-weight: 900;
        transition: transform 1s;
        display: flex;
    }
 
    .item {
        height: 150px;
        min-width: 400px;
        border: solid;
        text-align: center;
    }
 
    header {
        display: flex;
        height: 100px;
        justify-content: center;
        align-items: center;
        gap: 1rem;
        font-size: 1.2rem;
    }
    </style>
</head>
 
<body>
    <header>
        <span>goto section </span>
        <select class="selectSection">
            <option disabled>Choose section</option>
        </select>
    </header>
    <div class="container">
        <div class="scrollWrapper">
            <div class="box">
            </div>
        </div>
        <div class="hScroolBox">
            <div class="hiddenScrollBar"></div>
        </div>
    </div>
    <script>
    const hScroolBox = document.querySelector(".hScroolBox");
    const hiddenScrollBar = document.querySelector(".hiddenScrollBar");
    const container = document.querySelector(".container");
    const box = document.querySelector(".box");
    const selectSection = document.querySelector(".selectSection");
 
 
    for (let i = 0; i < 20; i++) {
        box.insertAdjacentHTML("beforeend", `<div class="item">${i}</div>`);
        selectSection.insertAdjacentHTML("beforeend", `<option>${i}</option >`);
    }
 
    function throttle(cb, delay = 1000) {
        let shouldWait = false;
        let waitingArgs;
        const timeoutFunc = () => {
            if (waitingArgs === null) {
                shouldWait = false;
            } else {
                Array.isArray(waitingArgs) ?
                    cb(...waitingArgs) : cb(waitingArgs);
                waitingArgs = null;
                setTimeout(timeoutFunc, delay);
            }
        };
        return (...args) => {
            if (shouldWait) {
                waitingArgs = args;
                return;
            }
            cb(...args);
            shouldWait = true;
            setTimeout(timeoutFunc, delay);
        };
    }
 
    const scroll = throttle((amount) => {
        box.style.transform = `translateX(${-amount}px)`;
    }, 300);
 
    selectSection.addEventListener("change", () => {
        const value = selectSection.value;
        scrollTo(box.children[value].offsetLeft);
    });
 
    container.addEventListener("wheel", (e) => {
        const scrollAmount = document.documentElement.clientWidth / 5;
        if (e.wheelDeltaY > 0) {
            hScroolBox.scrollLeft  -= scrollAmount;
        } else {
            hScroolBox.scrollLeft  += scrollAmount;
        }
    });
    hScroolBox.addEventListener("scroll", () => scroll(hScroolBox.scrollLeft));
 
    function scrollTo(amount) {
        hScroolBox.scrollLeft  = amount;
    }
 
    function scrollMore(amount) {
        hScroolBox.scrollLeft  += amount;
    }
 
    hiddenScrollBar.style.width = box.children[box.children.length - 1].offsetLeft + box.children[box.children.length - 1].offsetWidth  + "px";
    </script>
</body>
 
</html>


вертикальный codepen

Кликните здесь для просмотра всего текста

PHP/HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
<!DOCTYPE html>
<html>
 
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title></title>
    <style>
    * {
        padding: 0;
        margin: 0;
        box-sizing: border-box;
    }
 
    .container {
        height: calc(100vh - 100px);
        overflow: hidden;
        display: flex;
    }
 
    .scrollWrapper {
        flex: 1;
        background-color: gray;
    }
 
    .vScroolBox {
        flex: 0;
        overflow: auto;
        flex-basis: 20px;
    }
 
    .hiddenScrollBar {
        width: 1px;
    }
 
    .box {
        position: relative;
        font-size: 4rem;
        font-weight: 900;
        transition: transform 1s;
    }
 
    .item {
        height: 400px;
        border: solid;
        text-align: center;
    }
 
    header {
        display: flex;
        height: 100px;
        justify-content: center;
        align-items: center;
        gap: 1rem;
        font-size: 1.2rem;
    }
    </style>
</head>
 
<body>
    <header>
        <span>goto section </span>
        <select class="selectSection">
            <option disabled>Choose section</option>
        </select>
    </header>
    <div class="container">
        <div class="scrollWrapper">
            <div class="box">
            </div>
        </div>
        <div class="vScroolBox">
            <div class="hiddenScrollBar"></div>
        </div>
    </div>
    <script>
    const vScroolBox = document.querySelector(".vScroolBox");
    const hiddenScrollBar = document.querySelector(".hiddenScrollBar");
    const container = document.querySelector(".container");
    const box = document.querySelector(".box");
    const selectSection = document.querySelector(".selectSection");
 
 
    for (let i = 0; i < 20; i++) {
        box.insertAdjacentHTML("beforeend", `<div class="item">${i}</div>`);
        selectSection.insertAdjacentHTML("beforeend", `<option>${i}</option >`);
    }
 
    function throttle(cb, delay = 1000) {
        let shouldWait = false;
        let waitingArgs;
        const timeoutFunc = () => {
            if (waitingArgs === null) {
                shouldWait = false;
            } else {
                Array.isArray(waitingArgs) ?
                    cb(...waitingArgs) : cb(waitingArgs);
                waitingArgs = null;
                setTimeout(timeoutFunc, delay);
            }
        };
        return (...args) => {
            if (shouldWait) {
                waitingArgs = args;
                return;
            }
            cb(...args);
            shouldWait = true;
            setTimeout(timeoutFunc, delay);
        };
    }
 
    const scroll = throttle((amount) => {
        box.style.transform = `translateY(${-amount}px)`;
    }, 300);
 
    selectSection.addEventListener("change", () => {
        const value = selectSection.value;
        scrollTo(box.children[value].offsetTop);
    });
 
    container.addEventListener("wheel", (e) => {
        const scrollAmount = document.documentElement.clientHeight / 5;
        if (e.wheelDeltaY > 0) {
            vScroolBox.scrollTop -= scrollAmount;
        } else {
            vScroolBox.scrollTop += scrollAmount;
        }
    });
    vScroolBox.addEventListener("scroll", () => scroll(vScroolBox.scrollTop));
 
    function scrollTo(amount) {
        vScroolBox.scrollTop = amount;
    }
 
    function scrollMore(amount) {
        vScroolBox.scrollTop += amount;
    }
 
    hiddenScrollBar.style.height = box.getBoundingClientRect().height + "px";
    </script>
</body>
 
</html>


Пример на requestAnimationFrame
работает хуже чем css анимация

codepen
github gist
Кликните здесь для просмотра всего текста

PHP/HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
<!-- https://codepen.io/den4ik_rus/pen/yLxKoea -->
<!DOCTYPE html>
<html>
 
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title></title>
    <style>
    * {
        padding: 0;
        margin: 0;
        box-sizing: border-box;
    }
 
    .container {
        height: calc(100vh - 100px);
        overflow: hidden;
        display: flex;
    }
 
    .scrollWrapper {
        flex: 1;
        background-color: gray;
    }
 
    .vScroolBox {
        flex: 0;
        overflow: auto;
        flex-basis: 20px;
    }
 
    .hiddenScrollBar {
        width: 1px;
    }
 
    .box {
        position: relative;
        font-size: 4rem;
        font-weight: 900;
    }
 
    .item {
        height: 400px;
        border: solid;
        text-align: center;
    }
 
    header {
        display: flex;
        height: 100px;
        justify-content: center;
        align-items: center;
        gap: 1rem;
        font-size: 1.2rem;
    }
    </style>
</head>
 
<body>
    <header>
        <span>goto section </span>
        <select class="selectSection">
            <option disabled>Choose section</option>
        </select>
    </header>
    <div class="container">
        <div class="scrollWrapper">
            <div class="box">
            </div>
        </div>
        <div class="vScroolBox">
            <div class="hiddenScrollBar"></div>
        </div>
    </div>
    <script>
    const vScroolBox = document.querySelector(".vScroolBox");
    const hiddenScrollBar = document.querySelector(".hiddenScrollBar");
    const container = document.querySelector(".container");
    const box = document.querySelector(".box");
    const selectSection = document.querySelector(".selectSection");
 
    const state = { isScroll: false };
 
    for (let i = 0; i < 20; i++) {
        box.insertAdjacentHTML("beforeend", `<div class="item">${i}</div>`);
        selectSection.insertAdjacentHTML("beforeend", `<option>${i}</option >`);
    }
 
    selectSection.addEventListener("change", () => {
        const value = selectSection.value;
        scrollTo(box.children[value].offsetTop);
    });
 
    function throttle(cb, delay = 1000) {
        let shouldWait = false;
        let waitingArgs;
        const timeoutFunc = () => {
            if (waitingArgs === null) {
                shouldWait = false;
            } else {
                cb(...waitingArgs);
                waitingArgs = null;
                setTimeout(timeoutFunc, delay);
            }
        };
        return (...args) => {
            if (shouldWait) {
                waitingArgs = args;
                return;
            }
            cb(...args);
            shouldWait = true;
            setTimeout(timeoutFunc, delay);
        };
    }
 
    const duration = 300;
 
    const step = () => {
        if (!state.isScroll) return;
        state.progress = (performance.now() - state.startTime) / duration;
 
        if (state.progress > 0.9999) {
            state.progress = 1;
            state.isScroll = false;
        }
        // box.style.top = -state.currY + "px";
        // https://www.paulirish.com/2012/why-moving-elements-with-translate-is-better-than-posabs-topleft/
        state.currY = state.prevY + state.progress * state.diffY;
        box.style.transform = `translateY(${-state.currY}px)`;
        window.requestAnimationFrame(step);
    };
 
    const scroll = throttle((e) => {
        state.startTime = performance.now();
        state.prevY = state.currY;
        state.nextY = vScroolBox.scrollTop;
        state.diffY = state.nextY - state.prevY;
        if (!state.isScroll) {
            state.isScroll = true;
            window.requestAnimationFrame(step);
        }
    }, 100);
 
    state.currY = vScroolBox.scrollTop;
    state.prevY = vScroolBox.scrollTop;
 
    vScroolBox.addEventListener("scroll", scroll);
    container.addEventListener("wheel", (e) => {
        scroll();
        const scrollAmount = document.documentElement.clientHeight / 5;
        if (e.wheelDeltaY > 0) {
            vScroolBox.scrollTop -= scrollAmount;
        } else {
            vScroolBox.scrollTop += scrollAmount;
        }
    });
 
    function scrollTo(amount) {
        scroll();
        vScroolBox.scrollTop = amount;
    }
 
    function scrollMore(amount) {
        scroll();
        vScroolBox.scrollTop += amount;
    }
    hiddenScrollBar.style.height = box.getBoundingClientRect().height + "px";
    </script>
</body>
 
</html>
Метки css, html, javascript
Размещено в ui
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 15
Комментарии
  1. Старый комментарий
    Аватар для Алексей1153
    сглаженная прокрутка только раздражает. Зачем ею гордиться? О неё нужно максимально избавляться
    Запись от Алексей1153 размещена 14.03.2023 в 12:37 Алексей1153 вне форума
  2. Старый комментарий
    Аватар для mr_dramm
    Спасибо Алексей, очень хороший вопрос, я постараюсь в ближайшее время продемонстрировать свой редактор markdown (Этот редактор используется у меня на сайте для написания статей), в нем будет использоваться плавная прокрутка для того чтобы синхронизировать markdown текст и результат в html без прыжков, так как есть небольшая разница в тексте написанном в markdown и html, и также хочу сделать чтобы когда пользователь ставил курсор на редактируемый блок этот блок тоже был по центру, и чтобы его сделать по центру будет использоваться плавная прокрутка. Еще раз спасибо за вопрос =)
    Запись от mr_dramm размещена 14.03.2023 в 12:51 mr_dramm вне форума
  3. Старый комментарий
    Аватар для mr_dramm
    Хотя если честно в последнее время, у меня все меньше желания стараться что то куда-то выкладовать... фидбека примерно 0
    Запись от mr_dramm размещена 14.03.2023 в 13:06 mr_dramm вне форума
  4. Старый комментарий
    Аватар для Алексей1153
    да это и не вопрос, в общем то.

    попробуй делать браузерные игры, люди любят игры Будет фидбэк
    Запись от Алексей1153 размещена 14.03.2023 в 13:24 Алексей1153 вне форума
  5. Старый комментарий
    Запись от mr_dramm размещена 14.03.2023 в 15:59 mr_dramm вне форума
  6. Старый комментарий
    Да, transform лучше position в плане производительности

    [url]https://developer.mozilla.org/ru/docs/Web/Performance/Animation_performance_and_frame_rate[/url]

    [QUOTE]Browsers are optimized to handle CSS animations, and handle animating properties that do not trigger a reflow (and therefore also a repaint) very well. To improve performance, the node being animated can be moved off the main thread and onto the GPU. Properties that will lead to compositing include 3D transforms (transform: translateZ(), rotate3d(), etc.), animating transform and opacity, position: fixed, will-change, and filter. Some elements, including <video>, <canvas> and <iframe>, are also on their own layer. When an element is promoted as a layer, also known as composited, animating transform properties is done in the GPU, resulting in improved performance, especially on mobile.[/QUOTE]

    [url]https://developer.mozilla.org/en-US/docs/Learn/Performance/CSS[/url]

    Демка, кстати, фее... раздражает такое поведение скролла. :p
    Запись от KingdaKa размещена 15.03.2023 в 06:59 KingdaKa вне форума
  7. Старый комментарий
    Аватар для mr_dramm
    ведем статистику:
    раздражает (2): Алексей1153, KingdaKa



    Все остальные не стесняемся комментировать.
    Продолжайте пожалуйста, я еще не получал столько много полезной информации для анализа.
    Запись от mr_dramm размещена 15.03.2023 в 08:00 mr_dramm вне форума
  8. Старый комментарий
    Продолжайте пожалуйста, я еще не получал столько много полезной информации для анализа.
    Ну, если это не ирония, то мне тоже не понравилось поведение скроллируемого текста в примере https://codepen.io/den4ik_rus/pen/yLxKoea

    Ощущение такое, что текст перед остановкой "подпрыгивает". Как-то не привычно и, собственно, перемотка - это не та операция, которая должна переключать внимание на себя.
    Запись от sqltd1 размещена 15.03.2023 в 14:44 sqltd1 вне форума
  9. Старый комментарий
    Аватар для Алексей1153
    mr_dramm раздражает это из-за несогласованности между движением колеса и положением текста. Для каких-нибудь наркоманов, может быть, норм. А для нормальных людей - это раздражение
    Запись от Алексей1153 размещена 15.03.2023 в 15:12 Алексей1153 вне форума
  10. Старый комментарий
    Аватар для mr_dramm
    > перед остановкой "подпрыгивает"

    да это возможно решить уменьшая скорось при приближении к концу прокрутки и делая интервалы расстояний между срабатыванием функции step меньше например, сейчас они постоянные. Чем больше высота блока текста тем верочтнее что такие скачки будут. Можно добавить Easing functions для окончания прокручивания которая будет влиять на переменную progress тем самым меняя проходимые расстояния по Y в конце.

    Сейчас чтобы немного уменьшить скачки можно поменять условие

    JavaScript
    1
    
    if (state.progress < 0.98) {
    на

    JavaScript
    1
    
    if (state.progress < 0.9999) {
    > раздражает это из-за несогласованности между движением колеса и положением текста

    немного подробностей не помешает, в какой момент в начале движения или в конце?

    Большое спасибо за отзывы, ждите обновления!
    Запись от mr_dramm размещена 15.03.2023 в 16:00 mr_dramm вне форума
  11. Старый комментарий
    Аватар для Алексей1153
    >>немного подробностей не помешает, в какой момент в начале движения или в конце

    это в целом. Подробностей рассказать не смогу Это такие тактильные ощущения
    Запись от Алексей1153 размещена 15.03.2023 в 16:02 Алексей1153 вне форума
  12. Старый комментарий
    Аватар для mr_dramm
    Обновление, нет небольшлй задержки перед стартом скрола и перед остановкой скрола не подпрыгивает
    Запись от mr_dramm размещена 19.03.2023 в 10:39 mr_dramm вне форума
  13. Старый комментарий
    впечатления от https://codepen.io/den4ik_rus/pen/yLxKoea

    эффект подпрыгивания устранен

    все более или менее нормально, но, на нажатия кнопок вверх\вниз\pgUp\pgDn начинает реагировать только после выполнения goto section.

    При захвате скроллбара и попытке скроллить, страница становится "инерционной" и останавливается не в момент остановки скроллбара, а позже. Это противоречит ожиданиям (по кр.мере моим - я перематываю что-то, увидел интересный фрагмент и остановился, а он уехал куда-то).

    удачи.
    Запись от sqltd1 размещена 26.03.2023 в 07:50 sqltd1 вне форума
  14. Старый комментарий
    Я не понял в чем прикол. Но лучше обычного [B]привычного[/B] скролла ничего нет.
    Лучшее - враг хорошего.
    Запись от KingdaKa размещена 27.03.2023 в 02:41 KingdaKa вне форума
  15. Старый комментарий
    Запись от mr_dramm размещена 30.07.2023 в 17:23 mr_dramm вне форума
 
Новые блоги и статьи
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр.
Jin X 06.09.2026
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр. Работая с форумом и нейросетями в браузере часто хочется что-то подкорректировать или добавить какого-то функционала. Ниже прикреплён. . .
Программа опроса у.з. расходомера SLS-720F
Argus19 02.09.2026
Программа опроса у. з. расходомера SLS-720F Программа опрашивает один раз в минуту три ультразвуковых расходомера SLS-720F через интерфейс RS-485 по протоколу Modbus RTU. Опрашиваются регистры. . .
Hyper-V: Компьютер должен поддерживать доверенный платформенный модуль 2.0.
Maks 31.08.2026
При установке Windows 11 на виртуальную машину Hyper-V 2-го поколения вылезла такая ошибка: Решение: в параметрах виртуальной машины, в разделе "Безопасность" (Security) активировать флаг. . .
Архитектура биовида Стива в Майнкрафте: Зачем бонобо кубический каннибализм
anaschu 30.08.2026
Кубический Вагинокапитализм в Minecraft: Математический инвариант ОДУ и рок Стивов-бонобо Главная задача разработанной «Модели Всего» — наглядно продемонстрировать наличие системной «судьбы». . .
Оттачиваю умение писать js программы.
russiannick 30.08.2026
Проектом выходного дня стало написание Книги шифров Виженера. Итогом стала версия 200, синий туман. Синий туман назван так, потому что замораживает текст под собой. Нажатие синих кнопок управляют. . .
мат медиц модель 30. презентация проекта
anaschu 27.08.2026
хоп хоп хоп хидахоп, а я кладую))
Как у меня протекала болезнь
zorxor 27.08.2026
Здравствуйте, друзья! Эта запись блога предназначена именно для вас - для моих дорогих друзей, которые знали меня лично. Чтобы ответить на вопрос - а что же со мной произошло на самом деле? Я учился. . .
Нашел вот забавное видео о измерениях. Лучшее что я видел на эту тему
kumehtar 26.08.2026
ILETXiw9bMQ Основная суть и тезисы по измерениям: 0D (Нулевое измерение): точка, не имеющая длины, ширины, высоты или объема. Объект не может перемещаться в 0D. 1D (Первое измерение):. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru