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

smooth scroll

Запись от mr_dramm размещена 14.03.2023 в 12:14
Показов 2622 Комментарии 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 вне форума
 
Новые блоги и статьи
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
Фото: Daniel Greenwood
kumehtar 13.11.2025
Расскажи мне о Мире, бродяга
kumehtar 12.11.2025
— Расскажи мне о Мире, бродяга, Ты же видел моря и метели. Как сменялись короны и стяги, Как эпохи стрелою летели. - Этот мир — это крылья и горы, Снег и пламя, любовь и тревоги, И бескрайние. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru