Появление элементов при прокрутке страницы: Появление элементов при скролле — Анимация при прокрутке страницы (JavaScript, CSS)

Содержание

Подгрузка контента при прокрутке — JavaScript — Дока

Кратко

Секция статьи «Кратко»

В вебе хорошим тоном считается не загружать в браузер ничего лишнего. Например, стили, скрипты и изображения, которые пользователю не понадобятся, можно считать лишними и не загружать. С контентом дело обстоит так же — по возможности мы хотим загружать только то, что пользователю понадобится «прямо сейчас».

В этой статье мы разберёмся, зачем нам это нужно и какие приёмы используются, чтобы этого добиться.

Лишний код

Секция статьи «Лишний код»

Сперва поймём, почему мы не хотим держать в проекте лишний код и загружать его в браузер. На это у нас несколько причин.

Трафик дорожает

Секция статьи «Трафик дорожает»

В первую очередь, ненужный код — это лишний сетевой трафик. Он может быть дорогим, особенно мобильный, особенно в Европе. Сайт или приложение, которые загружают ненужное (особенно изображения), могут оказаться в прямом смысле дорогими.

Время исполнения увеличивается

Секция статьи «Время исполнения увеличивается»

Это в меньшей степени относится к изображениям и в большей — к JS-файлам. Чем больше JS-кода браузеру необходимо распарсить и выполнить, тем больше времени это займёт. Потраченное время мы украдём у пользователей, пока они будут «ждать загрузки».

Особенно остро это будет досаждать людям с «медленными» устройствами: относительно старыми телефонами или компьютерами. На таких устройствах время исполнения может увеличиваться в разы.

Удобство работы уменьшается

Секция статьи «Удобство работы уменьшается»

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

Навигация внутри проекта и «загрузка проекта в голову» требуют времени и усилий. Чем больше проект, тем сложнее его охватить и понять. Лишний код только сбивает с мысли и не даёт сосредоточиться на важном.

Код и контент

Секция статьи «Код и контент»

Стратегий борьбы с лишним кодом много: код-сплиттинг и минификация, оптимизация изображений, кэширование, рефакторинг и удаление старого кода.

С контентом ситуация и похожа, и непохожа одновременно. С одной стороны, мы так же не хотим загружать то, что пользователю не нужно, с другой стороны — мы не знаем, что именно пользователю понадобится, и не можем предсказать, на какую страницу он решит перейти. Да и сам контент может постоянно меняться — как в соцсетях.

Поэтому из всех стратегий «сплиттинг» нам подходит больше всего.

💡

Сплиттингом мы будем называть передачу контента по кускам: когда первый кусок отдаётся автоматически, а остальные — по запросу пользователя.

Такая стратегия используется уже давно, и скорее всего вы уже встречались с её реализацией в виде пагинаторов.

Пагинаторы

Секция статьи «Пагинаторы»

📄

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

Когда сайты были проще, а AJAX и JSON ещё не были распространены, пагинаторы были единственным способом поделить большое количество контента на куски.

Их и сейчас можно встретить, например, на Google.com:

Пример пагинатора.

Когда же JSON и AJAX стали обычным делом, у разработчиков и дизайнеров появилось больше возможностей «делить» контент на части и показывать его пользователю. Одна из таких возможностей — это бесконечный скролл.

Бесконечная прокрутка

Секция статьи «Бесконечная прокрутка»

📜

Бесконечная прокрутка (ещё говорят бесконечный скролл) — это приём, когда при прокрутке страницы ближе к концу на сервер отправляется запрос за новой порцией контента, которая встраивается в конец «ленты».

Чаще всего такой приём можно увидеть в соцсетях: Twitter, Facebook, Instagram и прочих. Когда пользователь докручивает до «конца» страницы, браузер получает от сервера новую порцию постов, и лента становится бесконечной.

Давайте навестим нашу печально известную соцсеть Switter, знакомую нам по статье о безопасности веб-приложений, и поможем её разработчикам создать такую бесконечную загрузку. (Может быть, хотя бы это убережёт их акции от полного краха.)

Switter сейчас

Секция статьи «Switter сейчас»

Сейчас Switter выглядит как лента со свитами и пагинатор внизу.

Наша задача — реализовать бесконечную прокрутку. Нам стоит учесть:

  • Switter — маленькая соцсеть, и контент пользователя всё-таки может закончиться. Поэтому нужно предусмотреть ситуацию, когда мы больше не отправляем запросы за новыми порциями.
  • Если прокрутка будет работать на страницах, которые видны всем, пользователи могут захотеть поделиться конкретной страницей. Нам нужно сохранять текущую страницу в адресной строке, чтобы ей можно было поделиться.
  • Нам не хочется, чтобы пользователи постоянно «упирались» в дно страницы. Поэтому нужно, чтобы «контент уже ждал пользователя», а не наоборот.

Заглушка для сервера

Секция статьи «Заглушка для сервера»

Первым делом мы сделаем заглушку для сервера, чтобы имитировать запросы к нему. Для краткости мы скроем шаг с созданием.

Но если вам интересно, как всё устроено — добро пожаловать под капот :–)

Вначале создадим «базу данных».

Это объект поста, которые мы будем отдавать в качестве новой порции контента:

const post = {  title: 'Заголовок поста',  body:    'Текст поста в лучшей на свете социальной сети Switter. Все совпадения вымышлены и случайны.',  likes: 77,  reposts: 7,}
          
const post = {
  title: 'Заголовок поста',
  body:
    'Текст поста в лучшей на свете социальной сети Switter. Все совпадения вымышлены и случайны.',
  likes: 77,
  reposts: 7,
}

Теперь создадим «сервер API»:

// Метод posts возвращает Promise, имитируя асинхронное общение// между клиентом и сервером («запрос/ответ»).const server = {  // Аргумент page — курсор, номер страницы, которую надо загрузить.  // С этим номером мы определяем, какую порцию контента отправить.  // В нашем примере порции отличаться не будут, но в жизни  // курсор бы влиял на то, какой диапазон постов сервер бы доставал из БД.
posts(page = 1) { // В нашем случае, если текущая страница — 5-я, // мы считаем, что контент закончился. const finished = page >= 5 // Иначе сервер отправляет курсор next. // Он указывает, какая страница будет по счёту следующей. // Так клиент будет знать, стоит ли ему отправлять запрос // за новой порцией контента. const next = finished ? null : page + 1 // В качестве постов отправляем массив из 5 объектов post. const posts = Array(5).fill(post) return new Promise((resolve) => { // Таймаут имитирует сетевую «задержку». setTimeout(() => { resolve({ posts, next }) }, 150) }) },}
// Метод posts возвращает Promise, имитируя асинхронное общение // между клиентом и сервером («запрос/ответ»). const server = { // Аргумент page — курсор, номер страницы, которую надо загрузить. // С этим номером мы определяем, какую порцию контента отправить. // В нашем примере порции отличаться не будут, но в жизни // курсор бы влиял на то, какой диапазон постов сервер бы доставал из БД. posts(page = 1) { // В нашем случае, если текущая страница — 5-я, // мы считаем, что контент закончился. const finished = page >= 5 // Иначе сервер отправляет курсор next. // Он указывает, какая страница будет по счёту следующей. // Так клиент будет знать, стоит ли ему отправлять запрос // за новой порцией контента. const next = finished ? null : page + 1 // В качестве постов отправляем массив из 5 объектов post. const posts = Array(5).fill(post) return new Promise((resolve) => { // Таймаут имитирует сетевую «задержку». setTimeout(() => { resolve({ posts, next }) }, 150) }) }, }

Вызывать метод для получения новых постов posts() мы будем с помощью await:

const response = await server.posts()
          const response = await server.posts()

Клиент

Секция статьи «Клиент»

Когда сервер готов, мы можем приступать к разработке клиентской части. Первым делом спроектируем, как должно работать приложение.

Проектирование

Секция статьи «Проектирование»

Мы хотим подгружать новый контент, когда пользователь докручивает до конца страницы. Здесь можно выделить несколько задач:

  • Следить за положением прокрутки. Когда мы приближаемся к концу страницы, нужно запрашивать следующую порцию данных.
  • Уметь общаться с сервером. Нам нужно отправлять запросы и обрабатывать ответы.
  • Преобразовывать данные в вёрстку на странице и отображать её.
  • Не забыть о правильной обработке события прокрутки, чтобы ничего не тормозило.

Начнём с вёрстки.

Вёрстка и шаблоны

Секция статьи «Вёрстка и шаблоны»

Свит свёрстан как <article>, внутри есть заголовок, текст и кнопки «Нравится» и «Ресвитнуть»:

<article>  <h2>Заголовок поста</h2>  <p>    Текст поста в лучшей на свете социальной сети Switter. Все совпадения    вымышлены и случайны.
</p> <footer> <button type="button">❤️ 20</button> <button type="button">🔄 20</button> </footer></article> <article> <h2>Заголовок поста</h2> <p> Текст поста в лучшей на свете социальной сети Switter. Все совпадения вымышлены и случайны. </p> <footer> <button type="button">❤️ 20</button> <button type="button">🔄 20</button> </footer> </article>

Вёрстка нас устраивает. (<footer> можно заменить на <menu>, но в целом ок.)

Из этой вёрстки мы сделаем шаблон для будущих свитов, которые мы будем загружать с сервера. Шаблон нужен, потому что с сервера мы будем загружать только данные. Как эти данные должны отображаться, сервер не знает. Шаблон будет нужен именно для этого — чтобы браузер мог правильно отобразить данные на странице.

Данные с сервера будут заполнять шаблон, в итоге получится компонент свита.

<!--  Тег template используется, чтобы хранить куски кода,      которые не должен видеть пользователь. --><template>  <!--  Внутри повторяем разметку свита,        удаляя все тексты и числа.        Оставляем только «скелет» компонента. -->  <article>    <h3></h3>    <p></p>    <footer>      <button type="button">❤️ </button>      <button type="button">🔄 </button>    </footer>  </article></template>
          <!--  Тег template используется, чтобы хранить куски кода,
      которые не должен видеть пользователь. -->
<template>
  <!--  Внутри повторяем разметку свита,
        удаляя все тексты и числа.
        Оставляем только «скелет» компонента. -->
  <article>
    <h3></h3>
    <p></p>
    <footer>
      <button type="button">❤️ </button>
      <button type="button">🔄 </button>
    </footer>
  </article>
</template>

Если мы добавим этот код на страницу, то визуально ничего не изменится, но у нас появится возможность «штамповать» новые свиты с помощью JS-кода. Подробнее об этом мы поговорим чуть ниже.

Отслеживание положения скролла

Секция статьи «Отслеживание положения скролла»

Дальше нам потребуется написать функцию, которая будет определять, когда пора отправлять новый запрос.

function checkPosition() {  // Нам потребуется знать высоту документа и высоту экрана:  const height = document.body.offsetHeight  const screenHeight = window.innerHeight  // Они могут отличаться: если на странице много контента,  // высота документа будет больше высоты экрана (отсюда и скролл).  // Записываем, сколько пикселей пользователь уже проскроллил:  const scrolled = window.scrollY  // Обозначим порог, по приближении к которому  // будем вызывать какое-то действие.  // В нашем случае — четверть экрана до конца страницы:  const threshold = height - screenHeight / 4  // Отслеживаем, где находится низ экрана относительно страницы:  const position = scrolled + screenHeight  if (position >= threshold) {    // Если мы пересекли полосу-порог, вызываем нужное действие.
}} function checkPosition() { // Нам потребуется знать высоту документа и высоту экрана: const height = document.body.offsetHeight const screenHeight = window.innerHeight // Они могут отличаться: если на странице много контента, // высота документа будет больше высоты экрана (отсюда и скролл). // Записываем, сколько пикселей пользователь уже проскроллил: const scrolled = window.scrollY // Обозначим порог, по приближении к которому // будем вызывать какое-то действие. // В нашем случае — четверть экрана до конца страницы: const threshold = height - screenHeight / 4 // Отслеживаем, где находится низ экрана относительно страницы: const position = scrolled + screenHeight if (position >= threshold) { // Если мы пересекли полосу-порог, вызываем нужное действие. } }

Когда мы докрутим и пересечём порог, отправим запрос за новой порцией контента.

Теперь сделаем эту функцию обработчиком события прокрутки и изменения размера окна:

;(() => {  window. addEventListener('scroll', checkPosition)  window.addEventListener('resize', checkPosition)})()
          ;(() => {
  window.addEventListener('scroll', checkPosition)
  window.addEventListener('resize', checkPosition)
})()

Улучшение производительности

Секция статьи «Улучшение производительности»

Обработку прокрутки стоит немного притормаживать, чтобы она выполнялась чуть реже и таким образом меньше нагружала браузер.

Добавим функцию throttle():

function throttle(callee, timeout) {  let timer = null  return function perform(...args) {    if (timer) return    timer = setTimeout(() => {      callee(...args)      clearTimeout(timer)      timer = null    }, timeout)  }}
          function throttle(callee, timeout) {
  let timer = null
  return function perform(...args) {
    if (timer) return
    timer = setTimeout(() => {
      callee(...args)
      clearTimeout(timer)
      timer = null
    }, timeout)
  }
}

И теперь назначим обработчиком событий слегка заторможенную функцию:

;(() => {  window.
addEventListener('scroll', throttle(checkPosition, 250)) window.addEventListener('resize', throttle(checkPosition, 250))})() ;(() => { window.addEventListener('scroll', throttle(checkPosition, 250)) window.addEventListener('resize', throttle(checkPosition, 250)) })()

Запросы к серверу

Секция статьи «Запросы к серверу»

Дальше создадим функцию для запросов к серверу:

async function fetchPosts() {  const { posts, next } = await server.posts(nextPage)  // Делаем что-то с posts и next.}
          async function fetchPosts() {
  const { posts, next } = await server.posts(nextPage)
  // Делаем что-то с posts и next.
}

И используем её в checkPosition(). Так как fetchPosts() асинхронная, checkPosition() тоже станет асинхронной:

async function checkPosition() {  // . ..Старый код.  if (position >= threshold) {    await fetchPosts()  }}
          async function checkPosition() {
  // ...Старый код.
  if (position >= threshold) {
    await fetchPosts()
  }
}

Обработка данных от сервера

Секция статьи «Обработка данных от сервера»

В функции fetchPosts() мы получаем список постов, каждый из которых мы хотим добавить на страницу. Напишем функцию appendPost(), которая будет этим заниматься:

function appendPost(postData) {  // Если данных нет, ничего не делаем:  if (!postData) return  // Храним ссылку на элемент, внутрь которого  // добавим новые элементы-свиты:  const main = document.querySelector('main')  // Используем функцию composePost,  // которую напишем чуть позже —  // она превращает данные в HTML-элемент:  const postNode = composePost(postData)  // Добавляем созданный элемент в main:  main. append(postNode)}
          function appendPost(postData) {
  // Если данных нет, ничего не делаем:
  if (!postData) return
  // Храним ссылку на элемент, внутрь которого
  // добавим новые элементы-свиты:
  const main = document.querySelector('main')
  // Используем функцию composePost,
  // которую напишем чуть позже —
  // она превращает данные в HTML-элемент:
  const postNode = composePost(postData)
  // Добавляем созданный элемент в main:
  main.append(postNode)
}

Функция appendPost() использует внутри себя composePost(). Напишем и её тоже:

function composePost(postData) {  // Если ничего не передано, ничего не возвращаем:  if (!postData) return  // Обращаемся к шаблону, который создали ранее:  const template = document.getElementById('post_template')  // ...и вытаскиваем его содержимое.  // В нашем случае содержимым будет «скелет» свита, элемент article.   // Указываем, что нам необходимо его склонировать, а не использовать сам элемент,  // иначе он изменится сам, и мы не сможем сделать несколько свитов:  const post = template.content.cloneNode(true)  // Из postData получаем всю необходимую информацию:  const { title, body, likes, reposts } = postData  // Добавляем соответствующие тексты и числа в нужные места в «скелете»:  post.querySelector('h2').innerText = title  post.querySelector('p').innerText = body  post.querySelector('button:first-child').innerText += likes  post.querySelector('button:last-child').innerText += reposts  // Возвращаем созданный элемент,  // чтобы его можно было добавить на страницу:  return post}
          function composePost(postData) {
  // Если ничего не передано, ничего не возвращаем:
  if (!postData) return
  // Обращаемся к шаблону, который создали ранее:
  const template = document.getElementById('post_template')
  // ...и вытаскиваем его содержимое.
  // В нашем случае содержимым будет «скелет» свита, элемент article. 
  // Указываем, что нам необходимо его склонировать, а не использовать сам элемент,
  // иначе он изменится сам, и мы не сможем сделать несколько свитов:
  const post = template.content.cloneNode(true)
  // Из postData получаем всю необходимую информацию:
  const { title, body, likes, reposts } = postData
  // Добавляем соответствующие тексты и числа в нужные места в «скелете»:
  post.querySelector('h2').innerText = title
  post.querySelector('p').innerText = body
  post.querySelector('button:first-child').innerText += likes
  post.querySelector('button:last-child').innerText += reposts
  // Возвращаем созданный элемент,
  // чтобы его можно было добавить на страницу:
  return post
}

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

В реальном приложении нам бы потребовалось ещё повесить обработчики кликов по кнопкам в этом новом свите. Без обработчиков кнопки не будут ничего делать. Но для краткости эту часть в статье мы опустим.

Добавим обработку данных в fetchPosts():

async function fetchPosts() {  const { posts, next } = await server.posts(nextPage)  posts.forEach(appendPost)}
          async function fetchPosts() {
  const { posts, next } = await server.posts(nextPage)
  posts.forEach(appendPost)
}

Осталось лишь правильно обработать случаи, когда мы отправили запрос и ждём ответа, и когда контент закончился.

Обработка промежуточных и крайних случаев

Секция статьи «Обработка промежуточных и крайних случаев»

Если сейчас запустить приложение, то оно будет работать. Но при прокрутке к концу страницы вместо одной порции контента будет присылать несколько. (И никогда не закончит это делать 😁)

Чтобы решить эти проблемы, нужно завести переменные, которые будут следить за состоянием приложения:

// Какая страница следующая:let nextPage = 2// Если отправили запрос, но ещё не получили ответ,// не нужно отправлять ещё один запрос:let isLoading = false// Если контент закончился, вообще больше не нужно// отправлять никаких запросов:let shouldLoad = true
          // Какая страница следующая:
let nextPage = 2
// Если отправили запрос, но ещё не получили ответ,
// не нужно отправлять ещё один запрос:
let isLoading = false
// Если контент закончился, вообще больше не нужно
// отправлять никаких запросов:
let shouldLoad = true

Подправим функцию fetchPosts():

async function fetchPosts() {  // Если мы уже отправили запрос, или новый контент закончился,  // то новый запрос отправлять не надо:  if (isLoading || !shouldLoad) return  // Предотвращаем новые запросы, пока не закончится этот:  isLoading = true  const { posts, next } = await server. posts(nextPage)  posts.forEach(appendPost)  // В следующий раз запрашиваем страницу с номером next:  nextPage = next  // Если мы увидели, что контент закончился,  // отмечаем, что больше запрашивать ничего не надо:  if (!next) shouldLoad = false  // Когда запрос выполнен и обработан,  // снимаем флаг isLoading:  isLoading = false}
          async function fetchPosts() {
  // Если мы уже отправили запрос, или новый контент закончился,
  // то новый запрос отправлять не надо:
  if (isLoading || !shouldLoad) return
  // Предотвращаем новые запросы, пока не закончится этот:
  isLoading = true
  const { posts, next } = await server.posts(nextPage)
  posts.forEach(appendPost)
  // В следующий раз запрашиваем страницу с номером next:
  nextPage = next
  // Если мы увидели, что контент закончился,
  // отмечаем, что больше запрашивать ничего не надо:
  if (!next) shouldLoad = false
  // Когда запрос выполнен и обработан,
  // снимаем флаг isLoading:
  isLoading = false
}

Теперь функция работает правильно.

Что можно улучшить ещё

Секция статьи «Что можно улучшить ещё»

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

Изменение адресной строки
Секция статьи «Изменение адресной строки»

Хорошим тоном считается, когда мы можем дать ссылку на публичную страницу, и другой человек увидит всё в точности, как мы: сортировку контента, его количество, начало и конец. Для этого используется адресная строка и URL-страницы.

В нашем случае при изменении номера текущей страницы мы можем изменять часть адреса.

☝️

Для таких задач можно использовать History API.

Восстановление прокрутки при открытии страницы
Секция статьи «Восстановление прокрутки при открытии страницы»

При открытии страницы нам надо будет проверить адрес. Если там указано, что нам нужно «прокрутить» контент к какой-то странице, мы это сделаем программно.

Эту задачу часто решают на сервере. Это может быть проще и эффективнее, потому что сервер в таком случае заранее знает, какие данные нужно достать и отдать клиенту. Получается меньше запросов, и приложение для пользователя субъективно быстрее.

Результат

Секция статьи «Результат»

В результате мы получим переработанную страницу Switter, которая будет получать контент в тот момент, когда он нужен пользователю.

Открыть демо в новой вкладке

На практике

Секция статьи «На практике»

Саша Беспоясов советует

Секция статьи «Саша Беспоясов советует»

Используйте throttle() для подобных задач, чтобы улучшать производительность.

overscroll-behavior — CSS | MDN

CSS-свойство overscroll-behavior — это сокращение для свойств overscroll-behavior-x (en-US) и overscroll-behavior-y (en-US), которые позволяют управлять поведением прокрутки при достижении границы области прокрутки.

/* Значения-ключевые слова */
overscroll-behavior: auto; /* по умолчанию */
overscroll-behavior: contain;
overscroll-behavior: none;
/* Двойное значение */
overscroll-behavior: auto contain;
/* Глобальные значения */
overflow: inherit;
overflow: initial;
overflow: unset;

По умолчанию мобильные браузеры, как правило, обеспечивают эффект «отскока» или даже обновляют страницу при достижении её верхней или нижней части (или другой области прокрутки). Возможно, вы также замечали, что когда поверх страницы с прокруткой расположено диалоговое окно с прокруткой, то при достижении границы прокрутки диалогового окна нижележащая страница начинает прокручиваться. Это называется цепочкой прокрутки (scroll chaining, англ.)

В некоторых случаях такое поведение нежелательно. Вы можете использовать overscroll-behavior, чтобы избавиться от нежелательных цепочек прокрутки и поведения страниц браузера по принципу «потяните, чтобы обновить» (pull to refresh, англ.), характерного, например, для приложений Facebook и Twitter.

Начальное значениеauto
Применяется кне заменяемые блочные и inline-block элементы
Наследуетсянет
Обработка значениякак и у каждого из подсвойств этого свойства:
  • overscroll-behavior-x (en-US): как указано
  • overscroll-behavior-y (en-US): как указано
Animation typediscrete

Свойство overscroll-behavior задаётся в виде одного или двух ключевых слов, выбранных из списка значений ниже.

Два ключевых слова определяют значение overscroll-behavior по осям x и y соответственно. Если задано только одно значение, то предполагается, что и x, и y одинаковы.

Значения

auto

Поведение по умолчанию.

contain

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

none

Не происходит цепочки прокрутки в соседних областях прокрутки, и по умолчанию предотвращается достижение границы области прокрутки.

Формальный синтаксис

overscroll-behavior = 
[ (en-US) contain | (en-US) none | (en-US) auto ] (en-US){1,2} (en-US)

В нашем примере overscroll-поведения (см. также код по ссылке ) представлен полностраничный список поддельных контактов и диалоговое окно с чатом.

Обе эти области прокручиваются; обычно, если вы прокручиваете окно чата до тех пор, пока не достигнете границы прокрутки, нижележащее окно контактов тоже начнёт прокручиваться, что нежелательно. Предотвратить такое поведение можно, используя overscroll-behavior-y (overscroll-behavior также подойдет) для окна чата, как показано ниже:

.messages {
  height: 220px;
  overflow: auto;
  overscroll-behavior-y: contain;
}

Мы также хотим избавиться от стандартного overscroll-эффекта при прокрутке контактов вверх или вниз (например, Chrome на Android обновляет страницу при прокрутке за пределы верхней границы). Это можно предотвратить, установив для элемента <body> значение overscroll-behavior: none:

body {
  margin: 0;
  overscroll-behavior: none;
}

Пока CSSWG не опубликует свой собственный вариант, спецификация может быть найдена толькона Github в репозитории WICG.

SpecificationStatusComment
Unknown
Определение ‘overscroll-behavior’ в этой спецификации.
Неизвестно

BCD tables only load in the browser

with JavaScript enabled. Enable JavaScript to view data.
  • Take control of your scroll: customizing pull-to-refresh and overflow effects

Found a content problem with this page?

  • Edit the page on GitHub.
  • Report the content issue.
  • View the source on GitHub.

Want to get more involved? Learn

how to contribute.

This page was last modified on by MDN contributors.

scroll-behavior — CSS: каскадные таблицы стилей

Свойство CSS scroll-behavior задает поведение поля прокрутки, когда прокрутка активируется API-интерфейсами навигации или прокрутки CSSOM.

Обратите внимание, что на любые другие прокрутки, например, выполняемые пользователем, это свойство не влияет. Когда это свойство указано для корневого элемента, вместо этого оно применяется к области просмотра. Это свойство, указанное в элементе body , будет , а не распространяются на область просмотра.

Пользовательским агентам разрешено игнорировать это свойство.

 /* Значения ключевых слов */
поведение прокрутки: авто;
поведение прокрутки: гладкое;
/* Глобальные значения */
поведение прокрутки: наследовать;
поведение прокрутки: начальное;
поведение прокрутки: вернуться;
поведение прокрутки: обратный слой;
поведение прокрутки: не установлено;
 

Свойство scroll-behavior указано как одно из значений ключевого слова, перечисленных ниже.

Значения

авто

Поле прокрутки прокручивается мгновенно.

гладкая

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

Исходное значение авто
Применяется к полям прокрутки
Унаследован NO
Вычисленное значение Как указано
Тип анимации Discrete
16 Scroll-BEHHAVIOR =    
16 SCROL-BEHHAVIOR =    
16 SCROLL-BEHHAVIOR = 
16.  
smooth

Настройка поведения плавной прокрутки

HTML
 
<дел>
  <дел>1
  <дел>2
  <дел>3
CSS
 а {
  отображение: встроенный блок;
  ширина: 50 пикселей;
  текстовое оформление: нет;
}
навигация,
.scroll-контейнер {
  дисплей: блок;
  поле: 0 авто;
  выравнивание текста: по центру;
}
навигация {
  ширина: 339 пикселей;
  отступ: 5px;
  граница: 1px сплошной черный;
}
.scroll-контейнер {
  ширина: 350 пикселей;
  высота: 200 пикселей;
  переполнение-у: прокрутка;
  поведение прокрутки: гладкое;
}
.scroll-страница {
  дисплей: гибкий;
  выравнивание элементов: по центру;
  выравнивание содержимого: по центру;
  высота: 100%;
  размер шрифта: 5em;
}
 
Результат
Спецификация
Модуль CSS Overflow Level 3
# плавная прокрутка

Загрузка таблиц 90 только в браузере Включите JavaScript для просмотра данных.

Обнаружили проблему с содержанием этой страницы?

  • Отредактируйте страницу на GitHub.
  • Сообщить о проблеме с содержимым.
  • Просмотрите исходный код на GitHub.

Хотите принять участие? Узнать

как внести свой вклад.

Последний раз эта страница была изменена участниками MDN.

Как добавить эффекты прокрутки к элементам страниц: Справочный центр LearnWorlds

Доступность

Начальный уровень

Тренер для профессионалов

Учебный центр

Большой объем и корпоративный формат

Что такое параллаксная прокрутка?

Эффекты прокрутки/параллакса позволяют сделать школьные страницы более динамичными и интересными! Когда пользователь прокручивает страницу вниз, создается иллюзия глубины за счет того, что элементы страницы или фоновые изображения движутся медленнее, чем изображения переднего плана.

Параллаксная прокрутка может:

  • Привлеките внимание посетителей, передавая информацию с помощью ярких визуальных эффектов.
  • Сделайте школьные страницы стильными и профессиональными!
  • Увеличение времени посещения страницы, так как любопытство пользователя может привести к прокрутке всего дизайна.
  • Стимулируйте пользователей привлекательной графикой.
  • Подчеркните ключевую информацию и кнопки призыва к действию (CTA).

С помощью прокрутки параллакса вы можете придать своей школе уникальный внешний вид и создать динамичный пользовательский интерфейс!

Где найти настройки параллакса?

Каждый элемент имеет настройку Эффекты в правой боковой форме редактирования.   Эффекты прокрутки можно добавить в:

  • Виджеты.
  • Фоновые разделы (доступны для изображений, видео, встраивания и фонов SVG).

Как добавить эффекты прокрутки

1.  Перейдите на нужную страницу.

2. Нажмите на элемент, к которому вы хотите добавить эффект прокрутки/параллакса (раздел «Виджет» или «Фон»).

Фоны разделов

Щелкните «Редактировать раздел» → вкладка «Раздел» (первая вкладка боковой формы) → Эффекты прокрутки фона → Эффект прокрутки ( доступно для фона изображений, видео, встраивания и SVG).

а. Для изображений и SVG Разделы фона , вы можете выбрать Увеличить , Уменьшить , Параллакс , который похож на Показать с более плавным движением, Крупный план, который похож на Увеличить, но с более интенсивными результатами, Оттолкнуть, , который похож на Уменьшение, но с более интенсивными результатами, и Фиксированный .

б. Для разделов видео и встроенного фона можно выбрать один из следующих эффектов прокрутки: Увеличить , Уменьшить , Уменьшить , Уменьшить .

Виджеты

Нажмите на элемент, который вы хотите отредактировать и выберите один из следующих Типов под Tab 7

  • 9

    1111111111111111111111111111111111111111111111111111111111117.

  • Уменьшение масштаба
  • Увеличение масштаба
  • Увеличение масштаба
  • Slide in from the right
  • Slide in from the left
  • Slide out from the right
  • Slide out from the left
  • Rotate
  • 3. Choose устройства , где вы хотите, чтобы эффект прокрутки возник.

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

    4. Включите или отключите параметр Предварительный просмотр  при редактировании элемента на странице разработки.

    Как редактировать эффекты прокрутки виджета

    В зависимости от выбранного типа эффекта соответствующие параметры будут автоматически включаться/выключаться.

    • Перемещение по горизонтали = перемещение по оси X
    • Перемещение по вертикали = перемещение по оси Y0117
    • Поворот = поворот на определенные градусы 
    • Масштаб = масштаб x размер

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

    • Начальное состояние анимации = Когда элемент входит в нижнюю часть области просмотра браузера, когда пользователь начинает прокрутку
    • Состояние без анимации = Когда элемент входит в середину области просмотра браузера (это состояние можно отключить)
    • Состояние окончания анимации = Когда элемент покидает область просмотра в верхней части экрана при прокрутке

    В каждом из этих вариантов вы можете изменить положение или диапазон положения, в котором эффект действует, а также интенсивность эффекта:

    • Опция перемещения по горизонтали = интенсивность движения по оси x (px)
    • Опция перемещения по вертикали  = интенсивность движения по оси Y (px)
    • Опция затухания  = непрозрачность опции затухания (0-1)
    • Опция поворота  = вращение в градусах (-360° - 360°)
    • Опция масштабирования  = масштабируемость x размер (0-2, может быть выше; однако вам нужно будет ввести значение вручную)

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

    Эффекты параллакса, раздел

    Вы также можете добавить готовый раздел «Эффекты параллакса», выбрав один из предлагаемых нами вариантов, включая «Мультимедиа и текст», «Столбцы», «СТА», «Мобильное приложение», «Информационный бюллетень», «Предложение», «Фоновое изображение», «Вкладки» и «Счетчики»:

    Общие Примечания

    1.  При добавлении эффекта прокрутки в авторском режиме под выбранным элементом будет отображаться следующий значок:

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