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

smooth scroll

Запись от mr_dramm размещена 14.03.2023 в 12:14
Показов 2993 Комментарии 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 вне форума
 
Новые блоги и статьи
Лето уходит
kumehtar 17.08.2026
Мысли в слух
kumehtar 17.08.2026
Забавно, насколько сейчас стала доступна информация. Например о магии, духовном развитии, медитациях, и других подобных направлениях, ранее зачастую тайных, передаваемых от учителя к ученику. Хотя. . .
Перемещение строк из ТЧ в другой документ с учетом текущего пробега
Maks 17.08.2026
Реализация из решения ниже выполнена на примере нетипового документа "Автозапчасти", с ТЧ "Шины". За основу взят алгоритм отсюда: https:/ / www. cyberforum. ru/ blogs/ 359708/ 10838. html Задача: . . .
Саморегулирующийся социальный контракт для сервера cross-section.
Hrethgir 14.08.2026
С кодом конечно таких глубоких размышлений пока не было, впрочем я уже привык к алгоритмизации. Суть предмета записи: снова в диалоге с нейросетью (я взял пока себе ник для учётки админа - Rector). . . .
Часы электронные
Uhbif79 12.08.2026
Выкладываю программу часов. Программа позволяет: 1. Использовать системное время и дату, 2. Есть возможность вводить время и дату вручную. 3. Реализованы 2 будильника: начало и конец рабочего дня. . . .
Часы с будильником на основе класса QLCDNumber
Uhbif79 12.08.2026
Всем добрый день, выкладываю программу часов с будильником на основе класса QLCDNumber. Здесь я пробовал самостоятельно создавал классы, впервые столкнулся с видимостью переменной одного класса из. . .
Установка MinGW GCC 16.2 и CMake
8Observer8 10.08.2026
VK Видео: https:/ / vkvideo. ru/ video-240781534_456239017 YouTube: eY5-5PyI9NM Текстовая версия
Неделя из жизни имитационной модели склада: мои кривые руки растут, откуда надо
anaschu 10.08.2026
Неделя из жизни имитационной модели склада: как я почти написал неправильную логику и что с этим делать Работаю сейчас над учебно-рабочим проектом: строю в AnyLogic имитационную модель процессов. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru