Лентите за напредъка на четенето, като този, който можете да намерите в моя блог в горната част на единични публикации, са хубаво малко допълнение за предоставяне на подробна информация за това докъде е напреднал читателят в текущата публикация. Лентата за превъртане не е многозначителна в това отношение; включва цялата ви страница, което означава, че заглавката, коментарите, долният колонтитул и т.н. са част от индикацията.
Създаването на лента за прогрес на четене, която ви казва действителния напредък само на текущото съдържание на публикация в React, е доста лесно — особено с кукички, които правят нашия компонент още по-малък.
Компонентът ReadingProgress
Нашият компонент ReadingProgress
ще направи следните неща:
- използвайте куката, която ще отговаря за четенето и настройването на напредъка ни при четене
- използвайте куката, която ще отговаря за обработката на събитието за превъртане и правилно актуализирайте нашата лента за напредък при превъртане
- върнете лентата за прогрес на четене с правилната ширина
Така че нека се потопим направо в изпълнението:
const ReadingProgress = ({ target }) => { const [readingProgress, setReadingProgress] = useState(0); return <div className={`reading-progress-bar`} style={{width: `${readingProgress}%` }} /> };
Това е основата за нашия компонент. readingProgress
ще се използва като ширина (в проценти) за нашата лента за напредък. Единствената опора за нашия компонент е target
, която ще бъде препратка към нашия DOM контейнер на публикацията - повече за това след няколко минути.
Първо нека внедрим нашия слушател, който ще актуализира нашата лента за напредък при събития на превъртане:
const scrollListener = () => { if (!target.current) { return; } const element = target.current; const totalHeight = element.clientHeight - element.offsetTop; const windowScrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0; if (windowScrollTop === 0) { return setReadingProgress(0); } if (windowScrollTop > totalHeight) { return setReadingProgress(100); } console.log(windowScrollTop); setReadingProgress((windowScrollTop / totalHeight) * 100); };
windowScrollTop
опитва куп различни стойности, които коригират undefined
стойности за някои браузъри (напр. Safari).
Има един проблем с тази реализация: 100% напредък при четене се постига само ако сме превъртели покрай нашата цел. Малко вероятно е това да е вярно (освен че превъртате един ред, след като сте приключили с четенето на един ред, което би ви направило наистина странно) — така че трябва леко да коригираме начина, по който се изчислява напредъкът ни в четенето:
const totalHeight = element.clientHeight - element.offsetTop - window.innerHeight;
Това трябва да доведе до по-точен резултат по отношение на това кога лентата трябва да покаже завършена.
След това ще поставим нашия слушател в useEffect
кука, което прави целия ни компонент да изглежда така:
const ReadingProgress = ({ target }) => { const [readingProgress, setReadingProgress] = useState(0); const scrollListener = () => { if (!target.current) { return; } const element = target.current; const totalHeight = element.clientHeight - element.offsetTop - (window.innerHeight); const windowScrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0; if (windowScrollTop === 0) { return setReadingProgress(0); } if (windowScrollTop > totalHeight) { return setReadingProgress(100); } setReadingProgress((windowScrollTop / totalHeight) * 100); }; useEffect(() => { window.addEventListener("scroll", scrollListener); return () => window.removeEventListener("scroll", scrollListener); }); return <div className={`reading-progress-bar`} style={{width: `${readingProgress}%`}} />; };
Върнатата функция от нашата кука useEffect
е основно това, което се случва, когато компонентът е демонтиран (вижте Ефекти с почистване в документите).
Не на последно място трябва да използваме нашия компонент някъде. В този момент ще трябва да създадем реф на нашия целеви контейнер и просто да го предадем на нашия ReadingProgress
компонент:
function App() { const target = React.createRef(); return ( <> <ReadingProgress target={target} /> <div className={`post`} ref={target}>post content</div> </> ); }
Вижте документите за допълнителна информация относно createRef
Сега вашата лента за прогрес на четене трябва да работи перфектно - с изключение на това, че не можете да я видите, защото няма височина. Коригирайте това, като добавите малко CSS:
.reading-progress-bar { position: sticky; height: 5px; top: 0; background-color: #ff0000; }
Готово! Сега вашите читатели вече не се губят в чистата безкрайна дължина на вашите публикации и винаги знаят кога ще свърши.
За да видите напълно работещ пример, можете да погледнете тази кодова писалка: https://codepen.io/nehalist/pen/agRNYZ
Пакети на трети страни
Има някои пакети на трети страни, които се справят точно с този проблем. Доколкото разбрах, повечето от тях са остарели и/или вече не се поддържат – но това, което е още по-уместно в този момент: наистина ли имате нужда от зависимост от трета страна за наистина прост компонент с около 30 реда код? Е, честно казано, не мисля така.
Заключение
Както видяхте, прилагането на лента за прогрес на четене в React е доста лесно. Благодарение на куките можем да внедрим този компонент като много малък функционален компонент с малко или никакви допълнителни разходи.
Ако имате някакви подобрения по отношение на внедряването или мислите, че това може да се направи по-добре, моля, уведомете ме в коментарите!
Ако сте харесали тази публикация, не се колебайте да оставите 👏, последвайте ме в Twitter и се абонирайте за моя бюлетин. Първоначално публикувано на https://nehalist.io на 9 юли 2019 г.