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

smooth scroll

Запись от mr_dramm размещена 14.03.2023 в 12:14
Показов 3010 Комментарии 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 вне форума
 
Новые блоги и статьи
Модель по догадкам
anaschu 25.08.2026
Прошло две недели. Я уже рассказывал, как разговаривал с сотрудниками у сортировки и как понял, что главная ветка — не про приёмку, а про отбор. Но тогда я думал, что понял механику. На этой неделе я. . .
Запись в регистр сведений независимо от заполненности табличной части
Maks 25.08.2026
Реализация из решения ниже выполнена на нетиповом документе с несколькими табличными частями, разработанного в КА2. Задача: Обеспечить запись документа в регистр сведений независимо от. . .
Ноутбук Альфария
kumehtar 24.08.2026
Встретился тут в сети ноутбук Альфария, примарха Альфа-Легиона. Хотя возможно, это ноутбук Омегона, разумеется. Ну как вам?
Мастера простых решений
DevAlt 23.08.2026
В сишарп стэках winforms, да и wpf существует сложная система связывания источниках данных и элементов формы(текстовые поля и метки), опирается все это на технологию событий и мета. . .
Цена ошибки
DevAlt 23.08.2026
Человек я беспокойный и потому заинтересовался OCaml, в чате форсили функторы модулей как суперфичу. Пытаясь отдуплить концепт, наткнулся на тутор с простым примером. А главный принцип обучения от. . .
Сегодня суббота, 22.08.2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины.
zorxor 22.08.2026
Сегодня суббота, 22. 08. 2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины. Кто Я, откуда Я пришел и куда Я иду? Эти вопросы не оставляют меня ни на секунду. Жизнь на планете Земля. . .
Жизня: рисунок укладки багажа, сделанный клодом
anaschu 21.08.2026
Сделал 15 снимков, он по снимкам сделал схему.
Был там один разговор по поводу свободы в материальном мире.
kumehtar 19.08.2026
Суть: рассматривается живое существо, оказавшееся внутри довольно странной системы (этого мира) и пытающееся обустроить в ней свой кусок пространства. Жизнь действительно предъявляет каждому. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru