Реализация каркасных экранов в React
Опубликовано: 2022-03-10Спиннеры и загрузчики традиционно были способом сообщить пользователям, что загрузка контента займет некоторое время. Хотя этот подход великолепен, он быстро устаревает в современной разработке. Скелетонные экраны становятся идеальной заменой традиционным загрузчикам, потому что они сосредоточены на прогрессе, а не на времени ожидания, что снижает разочарование во время загрузки.
В этой статье мы не будем рассматривать основы синтаксиса CSS React или JavaScript, поэтому вам не нужно быть экспертом ни в одном из этих языков, чтобы следовать ей.
Эксперты по пользовательскому интерфейсу и UX учат нас, что, пока пользователи ждут загрузки контента на страницу, мы должны удерживать их внимание.
Идея использования счетчиков для привлечения пользователей до загрузки контента великолепна; тем не менее, результат может быть далеко не идеальным, потому что большинству пользователей будет скучно смотреть на манекен анимированного спиннера, как на часы. Люк Вроблевски уточняет это.
Скелетные экраны обеспечивают лучший пользовательский опыт, сокращая время загрузки. Сосредоточив внимание на прогрессе, а не на времени ожидания, у пользователей создается иллюзия, что информация будет постепенно отображаться на экране. Билл Чанг в своем исследовании подтверждает это.
Что такое каркасный экран?
Скелетный экран — это версия пользовательского интерфейса, которая не содержит фактического содержимого; вместо этого он имитирует макет страницы, показывая ее элементы в форме, похожей на реальное содержимое, по мере того, как оно загружается и становится доступным (т. е. когда позволяет сетевая задержка).
Каркас экрана — это, по сути, каркас страницы с полями-заполнителями для текста и изображений.
Что уникального в каркасном экране?
Скелетный пользовательский интерфейс напоминает фактический пользовательский интерфейс страницы, поэтому пользователи поймут, как быстро загрузится веб-приложение или мобильное приложение, еще до того, как появится содержимое. Вот несколько причин, по которым вы, возможно, захотите рассмотреть возможность использования каркасных экранов в своем следующем проекте:
- имитировать макет страницы проще с экраном-скелетом,
- содержимое загружается постепенно (не все сразу).
Каркасные экраны также называют:
- призрачные элементы,
- заполнители контента,
- загрузчики контента.
Blockchain.com, YouTube, Facebook, Medium и другие крупные технологические компании отображают скелетные экраны, пока их контент загружается, чтобы улучшить UX.
Blockchain.com
Середина
Типы каркасных экранов
Существуют различные виды каркасных экранов. Основными из них являются текстовые заполнители и заполнители изображений (или цветов).
Большинство разработчиков предпочитают использовать текстовые заполнители в качестве каркаса пользовательского интерфейса на своих страницах, потому что их легко создать, и разработчику не требуются какие-либо подробности о сути фактического содержимого; вместо этого скелет имитирует пользовательский интерфейс.
Цветовые заполнители сложнее создать, потому что они требуют подробностей о содержании.
Некоторые популярные пакеты упрощают внедрение каркасных экранов в веб-приложениях. Давайте подробнее рассмотрим их обоих:
- Реагировать заполнитель
- Скелет загрузки React
Мы рассмотрим плюсы и минусы каждого пакета, прежде чем решить, какой из них использовать для нашего приложения.
Реагировать заполнитель
Плюсы
- Компоненты-заполнители используются для создания пользовательского каркаса пользовательского интерфейса.
- Поддерживается импульсная анимация (т.е. эффект движения элемента).
- Он поставляется с API на основе компонентов.
Минусы
- Компоненты каркаса поддерживаются отдельно, поэтому обновление стилей компонента может также потребовать обновления компонента скелета.
- Кривая обучения не является линейной, потому что существует несколько компонентов для разных нужд.
Ниже приведен пример скелетного компонента с использованием пакета react-placeholder
:
import { TextBlock, RectShape } from 'react-placeholder/lib/placeholders'; import ReactPlaceholder from 'react-placeholder'; const GhostPlaceholder = () => ( <div className='my-placeholder'> <RectShape color='gray' style={{width: 25, height: 70}} /> <TextBlock rows={6} color='blue'/> </div> ); <ReactPlaceholder ready={ready} customPlaceholder={<GhostPlaceholder />}> <MyComponent /> </ReactPlaceholder>
Импортируя TextBlock
и RectShape
из react react-placeholder/lib/placeholder
и ReactPlaceholder
из react react-placeholder
, мы создали функциональный компонент с именем GhostPlaceholder
. GhostPlaceholder
есть div, и внутри div мы использовали компонент RectShape, который описывает размеры прямоугольника, передает значение любого цвета и определяет стили прямоугольника.
Затем мы использовали компонент TextBlock
, чтобы установить значения для строк и цвета. Компонент TextBlock
определяет количество строк и цвет текста.
Мы передаем MyComponent
как дочерний элемент компонента ReactPlaceholder
, который получает компоненты ready
и GhostPlaceholder
в качестве значений для своих реквизитов ready
и customPlaceholder
.
MyComponent
будет загружаться, когда отображается пользовательский интерфейс каркасного экрана.
Чтобы узнать больше, проверьте документацию.
Скелет загрузки React
Плюсы
- Он основан на API и имеет один компонент с реквизитами для всех настроек.
- Его можно использовать как отдельный скелетный компонент, а также непосредственно внутри любого компонента, что делает его гибким.
- Он поддерживает темы и анимацию Pulse.
Минусы
- Его легко реализовать для простого пользовательского интерфейса скелета, но сложно для более сложных скелетов.
- Наличие отдельного скелетного компонента усложнит поддержку при изменении пользовательского интерфейса и стилей.
Ниже приведен пример React Loading Skeleton:
import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; const SkeletonComponent = () => ( <SkeletonTheme color="#202020" highlightColor="#444"> <section> <Skeleton height={50} width={50} /> </section> </SkeletonTheme> );
Мы импортировали Skeleton
и SkeletonTheme
из библиотеки react react-loading-skeleton
, а затем создали функциональный компонент, который отображает компонент SkeletonTheme
со свойствами color
и hightlightColor
.
Компонент SkeletonTheme
используется для создания тем (например, добавления цветовых эффектов в пользовательский интерфейс скелета).
Наконец, внутри секции мы определяем компонент Skeleton
с передачей свойств высоты и ширины и их соответствующих значений.
Создание пользовательского интерфейса Skeleton Screen в стиле YouTube
Давайте создадим каркасный экран, похожий на YouTube, используя React Loading Skeleton, чтобы показать, как работает каркасный пользовательский интерфейс.
Настроить реакцию
Самый простой способ настроить React — использовать Create React App, который является «официально поддерживаемым способом создания одностраничных приложений React. Он предлагает современную настройку сборки без настройки».
Мы будем использовать его для начальной загрузки приложения, которое будем создавать. В терминале выполните следующую команду:
npx create-react-app skeleton-screens && cd skeleton-screens
После завершения установки запустите сервер React, запустив npm start
:
Создайте пользовательский интерфейс YouTube без каркасного экрана
Во-первых, давайте введем фиктивные данные YouTube. Вместо фиктивных данных обычно используются реальные конечные точки, но в этом руководстве мы будем использовать фиктивные данные.
Создайте файл в папке src/
и назовите его data.js
, добавьте в него следующий код.
const dummyData= [ { section: "Recommended", channel: "CNN", items: [ { id: "fDObf2AeAP4", image: "https://img.youtube.com/vi/fDObf2AeAP4/maxresdefault.jpg", title: "75 million Americans ordered to stay home", views: "1.9M views", published: "3 days agos" }, { id: "3AzIgAa0Cm8", image: "https://img.youtube.com/vi/3AzIgAa0Cm8/maxresdefault.jpg", title: "Gupta: The truth about using chloroquine to fight coronavirus pandemic", views: "128K views", published: "4 hours ago" }, { id: "92B37aXykYw", image: "https://img.youtube.com/vi/92B37aXykYw/maxresdefault.jpg", title: "Willie Jones STUNS Simon Cowell In Pitch Perfect Performance of 'Your Man'!", views: "2.47 million views", published: "1 month ago" }, { id: "J6rVaFzOEP8", image: "https://img.youtube.com/vi/J6rVaFzOEP8/maxresdefault.jpg", title: "Guide To Becoming A Self-Taught Software Developer", views: "104K views", published: "17 days ago" }, { id: "Wbk8ZrfU3EM", image: "https://img.youtube.com/vi/Wbk8ZrfU3EM/maxresdefault.jpg", title: "Tom Hanks and Rita Wilson test positive for coronavirus", views: "600k views", published: "1 week ago" }, { id: "ikHpFgKJax8", image: "https://img.youtube.com/vi/ikHpFgKJax8/maxresdefault.jpg", title: "Faces Of Africa- The Jerry Rawlings story", views: "2.3 million views", published: "2014" } ] }, { section: "Breaking News", channel: "CGTN America", items: [ { id: "tRLDPy1A8pI", image: "https://img.youtube.com/vi/tRLDPy1A8pI/maxresdefault.jpg", title: "Is Trump blaming China for COVID-19? You decide.", views: "876k views", published: "9 days ago" }, { id: "2ulH1R9hlG8", image: "https://img.youtube.com/vi/2ulH1R9hlG8/maxresdefault.jpg", title: "Journalist still goes to office during pandemic, see her daily routine", views: "873 views", published: "3 hours ago" }, { id: "TkfQ9MaIgU", image: "https://img.youtube.com/vi/_TkfQ9MaIgU/maxresdefault.jpg", title: "How are small businesses going to survive the economic downturn of the COVID-19 era?", views: "283 views", published: "4 day ago" } ] } ]; export default dummyData;
Чтобы воспроизвести формат YouTube, мы создали фиктивные данные, содержащие массив объектов с такими свойствами, как идентификатор, изображение, заголовок, количество просмотров и дата публикации.
Далее давайте создадим наш пользовательский интерфейс YouTube. У нас будет три компонента:
Card | Содержит сведения о миниатюре видео, названии, количестве просмотров, дате публикации и канале. |
CardList | Возвращает все карты подряд. |
App | Монтирует наш объект dummyData , загружает каркас пользовательского интерфейса в течение двух секунд и возвращает компонент CardList . |
Внутри папки src
создайте папку и назовите ее components
. Внутри папки components
создайте файл Card.js
, добавьте в него следующий код:
import React from "react"; const Card = ({ item, channel }) => { return ( <li className="card"> <a href={`https://www.youtube.com/watch?v=${item.id}`} target="_blank" rel="noopener noreferrer" className="card-link" > <img src={item.image} alt={item.title} className="card-image" /> <img src={item.image} alt={item.title} className="channel-image" /> <h4 className="card-title">{item.title}</h4> <p className="card-channel"> <i>{channel}</i> </p> <div className="card-metrics"> {item.views} • {item.published} </div> </a> </li> ); }; export default Card;
Мы создали компонент Card
. Внутри него мы импортировали React
из react
и разобрали свойства item
и channel
, чтобы их можно было использовать в компоненте Card
. Каждый компонент элемента Card
, который отображает одно видео, будет показывать миниатюру, количество просмотров, дату публикации и название.
Компонент списка карт
Внутри папки components
создайте файл CardList.js и добавьте в него следующий код:
import React from "react"; import Card from "./Card"; const CardList = ({ list }) => { return ( <ul className="list"> {list.items.map((item, index) => { return <Card key={index} item={item} channel={list.channel} />; })} </ul> ); }; export default CardList;
В этот компонент мы импортировали созданный нами компонент Card
. Карточка принимает свойства item
и channel
, которые мы получаем путем сопоставления через list.items
. Затем мы экспортируем этот компонент как CardList
, потому что мы будем использовать его в нашем компоненте App
.
Примечание . Массив элементов, отображаемый в этом компоненте, является массивом объектов в наших dummyData
.
Компонент приложения
Внутри файла app.js в каталоге src/
удалите код, который там есть, и добавьте к нему следующее.
import React, { useState, useEffect } from "react"; import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]); const [loading, setLoading] = useState(false); useEffect(() => { setLoading(true); const timer = setTimeout(() => { setVideos(dummyData); setLoading(false); }, 5000); return () => clearTimeout(timer); }, []); return ( <div className="App"> { videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div> ); }; export default App;
В этом компоненте мы импортировали useState
и useEffect
вместе с React
и другими созданными нами файлами, которые потребуются в компоненте App
.
Поскольку наши данные являются фиктивными, нам нужно смоделировать их, как данные API, загрузив содержимое после двухсекундного тайм-аута с помощью метода JavaScript setTimeout
.
Затем в компоненте App
мы создаем состояние видео и устанавливаем его в пустой массив с помощью useState
.
Чтобы загрузить наши фиктивные данные, мы будем использовать хук useEffect
. В нашем хуке мы создаем переменный таймер, который содержит функцию setTimeout
()
. Внутри функции мы устанавливаем состояние видео в наш объект dummyData
и гарантируем, что данные загружаются через две секунды, и, наконец, мы отменяем таймер при размонтировании.
Наконец, мы сопоставляем наше состояние видео и возвращаем элемент section, который содержит list-section
и компонент CardList
с реквизитами списка.
Добавление CSS
До сих пор мы использовали множество классов без фактического CSS. Внутри папки src
удалите все в App.css
и замените его следующим кодом;
.App { max-width: 960px; margin: 0 auto; font-size: 16px; } .list { display: flex; justify-content: space-between; flex-wrap: wrap; list-style: none; padding: 0; } .section-title { margin-top: 30px; } .card { width: calc(33% - 10px); margin: 20px 0; } .card-link { color: inherit; text-decoration: none; } .card-image { width: 100%; } .channel-image { border-radius: 100%; padding: 0, 10px, 0, 0; width: 40px; height: 40px; } .card-title { margin-top: 10px; margin-bottom: 0; } .card-channel { margin-top: 5px; margin-bottom: 5px; font-size: 14px; } /* Tablets */ @media (max-width: 1000px) { .App { max-width: 600px; } .card { width: calc(50% - 22px); } } /* Mobiles \*/ @media (max-width: 640px) { .App { max-width: 100%; padding: 0 15px; } .card { width: 100%; } }
Давайте посмотрим, как выглядит наш пользовательский интерфейс YouTube без скелетного экрана. Вы можете видеть, что при загрузке страницы появляется белый экран на две секунды, а затем данные загружаются быстро.
Использование скелета загрузки React
В отличие от других библиотек, в которых вы должны тщательно создавать экран-скелет, чтобы он соответствовал размерам шрифта, высоте строк и полям вашего контента, компонент « Skeleton
» предназначен для использования непосредственно в ваших компонентах вместо загружаемого контента.
Давайте рассмотрим несколько причин, по которым мы выбрали React Loading Skeleton вместо других.
Тематика
React Loading Skeleton поддерживает создание тем. Таким образом, вы можете легко изменить цвета всех компонентов скелета с помощью SkeletonTheme
и передать значения в props
цвета.
Ниже приведен пример, показывающий, как это работает:
import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; <SkeletonTheme color="grey" highlightColor="#444"> <p> <Skeleton height={250} width={300} count={1} /> </p> </SkeletonTheme> <SkeletonTheme color="#990" highlightColor="#550"> <p> <Skeleton height={250} width={300} count={1} /> </p> </SkeletonTheme>
Продолжительность
В дополнение к height
, width
и color
мы также можем указать свойство duration
.
<Skeleton duration={2} />
Продолжительность по умолчанию равна 1.2
. Это определяет, сколько времени требуется для выполнения одного цикла скелетной анимации.
Чтобы узнать больше, ознакомьтесь с документацией.
Реализация пользовательского интерфейса Skeleton Screen
Теперь мы установим react-loading-skeleton
. Выполните следующую команду в своем терминале, чтобы установить пакет:
npm install react-loading-skeleton
Компонент скелета
Давайте создадим скелетный компонент для наших видеоданных. Внутри папки components
создайте файл SkeletonCard.js
и добавьте следующий код:
import React from "react"; import Skeleton from "react-loading-skeleton"; const SkeletonCard = () => { return ( <section> <h2 className="section-title"> <Skeleton height={30} width={300} /> </h2> <ul className="list"> {Array(9) .fill() .map((item, index) => ( <li className="card" key={index}> <Skeleton height={180} /> <h4 className="card-title"> <Skeleton circle={true} height={50} width={50} /> <Skeleton height={36} width={`80%`} /> </h4> <p className="card-channel"> <Skeleton width={`60%`} /> </p> <div className="card-metrics"> <Skeleton width={`90%`} /> </div> </li> ))} </ul> </section> ); }; export default SkeletonCard;
Мы создали неупорядоченный список. Внутри него мы использовали метод Array.fill()
. Поскольку у нас есть девять элементов фиктивных данных, мы использовали метод Array.fill()
для циклического перебора длины нашего объекта items
и заполнили его без значения индекса, тем самым сделав наш массив пустым . См. документацию Array.fill, чтобы узнать, как это работает.
Затем мы сопоставили наш пустой массив, чтобы вернуть список, содержащий свойства скелета, и мы указали значение каждого из свойств скелета.
Здесь height
означает длину каркасного прямоугольника, а width
— ширину, а circle
создает закругленную часть каркасного пользовательского интерфейса.
React Loading Skeleton поставляется с анимацией Pulse по умолчанию, что делает его удобным. Вы можете создать анимацию Pulse в соответствии с вашим проектом, но если вы спросите меня, я бы придерживался значения по умолчанию.
Наконец, доступен полный исходный код.
Теперь у нас есть полностью функциональный скелетный экранный пользовательский интерфейс. Наш пример показывает скелет в течение пяти секунд, прежде чем показать содержимое.
Давайте посмотрим на наш результат на данный момент:
Заключение
Скелетонные экраны значительно улучшают взаимодействие с пользователем, позволяя избежать разочарования от совершенно пустого экрана и давая пользователю представление о том, как будет выглядеть контент до его загрузки.
Если вам не нравится какой-либо из рассмотренных нами пакетов, вы можете создать свой собственный каркас пользовательского интерфейса, создав прямоугольники и круги, имитирующие макет страницы.
Пожалуйста, поделитесь своими отзывами и опытом в разделе комментариев ниже. Я хотел бы увидеть, что вы придумали!
Вспомогательный репозиторий для этой статьи доступен на Github.
использованная литература
- «Все, что вам нужно знать о скелетных экранах», Билл Чанг, UX Collective
- «Скелетная загрузка страниц с помощью React», Энтони Панаги, Octopus Wealth
- «Скелетные экраны с React и React Native», Крис Дельфин, Alligator.io
- «Реализация загрузки скелета в React», Адриан Бесе, DEV