Отображать динамический HTML-контент, такой как epub / ebook, без преобразования HTML в формат epub?

Я хочу создать отзывчивый, оптимизированный для мобильных устройств опыт чтения, подобный читалке epub / ebook, такой как приложение Kindle или iBooks, с использованием динамического HTML в качестве источника.

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

Доступно множество JS-библиотек, которые могут создавать «слайд-шоу» или «карусель» предварительно определенных слайдов (с использованием div или других элементов контейнера). Но я хочу, чтобы текст и содержимое html динамически перетекали в соответствии с областью просмотра любого устройства и при этом оставались читабельными ... точно так же, как пользовательский интерфейс epub / ebook, например приложение Kindle или iBooks. Таким образом, для той же статьи на телефоне будет намного больше «страниц», чем на планшете или рабочем столе, и эти «страницы» нужно будет динамически создавать / корректировать, если / когда размер области просмотра изменится ( как переключение с книжной на альбомную на мобильном устройстве).

Вот пример программы для чтения файлов .epub на javascript: epub.js

... обратите внимание на отзывчивое поведение. Когда вы изменяете размер области просмотра, весь текст перетекает, чтобы соответствовать доступному пространству, увеличивая или уменьшая общее количество «страниц». Проблема в том, что epub.js требует в качестве источника файла .epub.

Я хочу тот же пользовательский интерфейс и функциональность для html-страницы.

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

Я понимаю, что могу использовать сценарий преобразования для преобразования моей html-страницы в файл .epub, а затем использовать epub.js для рендеринга этого файла в браузере, но это кажется очень беспорядочным и неуклюжим. Было бы намного лучше имитировать или имитировать взаимодействие с пользователем .epub reader с html в качестве прямого источника, отображая / имитируя отзывчивый пользовательский интерфейс электронной книги на стороне клиента.

Кто-нибудь знает, существует ли что-то подобное уже, или как я мог бы сделать это сам?

Важнейшей функцией является динамическое / отзывчивое переформатирование текста. Когда размеры области просмотра уменьшаются, текст / контент необходимо переформатировать на следующую «страницу», чтобы избежать необходимости в вертикальной прокрутке. Я не знаю, как это сделать эффективно. Если бы я сам написал код, я мог бы использовать что-то вроде плагина jQuery Columnize, установив для всех столбцов значение width: 100vw; height: 100vh, чтобы каждый столбец был похож на «страницу», а затем выяснил, как создать интерфейс пролистывания между этими «страницами». .

Любая помощь высоко ценится!

взгляните на github.com/internetarchive/bookreader

Saeed 02.09.2018 09:09

Это действительно широкий вопрос, он недостаточно конкретный или технический, чтобы его можно было разместить здесь. Таким образом, он не заслуживает ответа и, возможно, должен исчезнуть? В любом случае вам нужно искать скрипты / библиотеки с «адаптивной пагинацией». С 2012 года, вот ответ, я бы начал именно с этого. Используйте JS для анализа «длинного» HTML-контента и его динамического разделения в соответствии с размером области просмотра. Оттуда выясните элементы управления для анимации страниц (пролистывание, стрелки и т. д.). Если вы также имели в виду динамическую связь с сервером, это еще одна баня червей.

MarsAndBack 02.09.2018 19:00

@ user2655393 вы нашли решение?

Eric 29.06.2020 16:59
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
11
3
2 875
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Взгляните на репозиторий это на GitHub. В противном случае вы можете создать одностраничный веб-сайт с множеством разделов, каждый из которых имеет высоту области просмотра, используя только CSS (демонстрация):

.section { height: 100vh; }

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

var curr_el_index = 0;
var els_length = $(".container").length;

$(".next_section").on("click", function(e) {
  curr_el_index++;
  if (curr_el_index >= els_length) {
    curr_el_index = 0;
  }
  $("html, body").animate({
    scrollTop: $(".container").eq(curr_el_index).offset().top
  }, 300);
  return false;
});

$(".previous_section").on("click", function(e) {
  curr_el_index--;
  if (curr_el_index < 0) {
    curr_el_index = els_length - 1;
  }
  $("html, body").animate({
    scrollTop: $(".container").eq(curr_el_index).offset().top
  }, 300);
  return false;
});
  * {
    border: 0;
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    -moz-box-sizing: border-box;
    -webkit-box-sizing: border-box;
  }
  body {
    background-color: #1a1a1a;
  }
  section {
    height: 100vh;
    background-color: #eee;
    border: 2px solid red;
    font-size: 6vw;
  }
<script src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section class = "container">Section 1 <a href = "#" class = "previous_section">Previous</a> <a href = "#" class = "next_section">Next</a></section>
<section class = "container">Section 2 <a href = "#" class = "previous_section">Previous</a> <a href = "#" class = "next_section">Next</a></section>
<section class = "container">Section 3 <a href = "#" class = "previous_section">Previous</a> <a href = "#" class = "next_section">Next</a></section>
<section class = "container">Section 4 <a href = "#" class = "previous_section">Previous</a> <a href = "#" class = "next_section">Next</a></section>
<section class = "container">Section 5 <a href = "#" class = "previous_section">Previous</a> <a href = "#" class = "next_section">Next</a></section>

РЕДАКТИРОВАТЬ # 1

Идея алгоритма, который исходит от мой код, который использует тот же плагин jQuery:

  1. Создайте макет своего ридера, скопировав в него весь текст
  2. Используйте плагин jQuery это для проверки текста внутри области просмотра (демонстрация)
  3. Подсчитайте количество символов / СЛОВ с меткой «Onscreen» в область просмотра (Рекомендации)
  4. Разделите весь текст на list, содержащий столько символов / СЛОВ, сколько есть в метке "Onscreen"
  5. Создайте section для каждого элемента полученного list, заполнив каждый section с относительным текстом; количество элементов list дает вам количество страниц (sections) всего текста. Вы можете перемещаться между sections, как указано выше
  6. При событии resize повторите [2-5] шагов алгоритма.

Ваше здоровье

Мне такой вариант нравится, пригодится

Arun Prasad E S 03.09.2018 07:33

Я думаю, что лучше всего объяснил намерение в своем комментарии к @ravi ниже.

Ja Superior 05.09.2018 03:13

Также посмотрите выше на комментарии, оставленные на OP

Ja Superior 05.09.2018 03:15

Вы можете попробовать точки привязки прокрутки CSS к тексту со столбцами

https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Scroll_Snap_Points

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

Обновлять Я хочу попытаться сделать текст полностью плавным, используя css. Ручка:
https://codepen.io/ericc3141/pen/RYZEpr

body {
    scroll-snap-type: mandatory;
    scroll-snap-points-x: repeat(100%);
}

#columns-test {
    height: 80vh;
    columns: 90vw auto;
}

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

Ja Superior 05.09.2018 03:10

@JaSuperior Обновлено с примером. Никакого алгоритма не требуется, потому что макет обрабатывается CSS и браузером.

Eric 06.09.2018 04:37

Теперь, когда я действительно пробую это сделать, кажется, что точки привязки не очень хорошо поддерживаются. Я полагаю, что, вероятно, есть и другие решения, плавающие вокруг ...

Eric 06.09.2018 04:42

Идея состоит в том, чтобы иметь div, который будет содержать весь текст (назовем этот div #epub_container). Тогда у вас будет div с тем же размером области просмотра страницы (назовем его #displayer), и он будет содержать #epub_container.

#displayer будет иметь css overflow:hidden. Поэтому, когда сайт загружается, он будет показывать только первую страницу, потому что остальная часть #epub_container будет скрыта. Затем вам понадобится навигатор по страницам для увеличения / уменьшения номера страницы. Когда номер страницы изменится, мы переместим верхнее смещение #epub_container в зависимости от этого.

Это функция jQuery:

function move_to_page() {
    var height = window.innerHeight;
    var width = window.innerWidth;

    var $displayer = $('#displayer');
    var offset = $displayer.offset();
    $displayer.height(height - offset.top - 5);

    var $epub = $('#epub_container');
    var offset_top = offset.top - $displayer.height() * m_page;
    $epub.offset({top: offset_top, left: offset.left});
}

JSFiddle

Обновлено: вызовите move_to_page() после перекомпоновки текста, чтобы пересчитать страницы.

Image

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

Динамический разрыв страницы - JQuery

Здесь вы можете установить высоту страницы в соответствии с высотой вашего окна просмотра. С остальным вы можете справиться с помощью javascript и css.

Возникает вопрос: «Как программно определить, где находятся нужные места». И я, и человек, задающий вопрос, ищем алгоритм, который может оценить текст и определить, где должны быть разрывы, чтобы, если страница изменит размер в зависимости от области просмотра или ориентации, текст будет соответственно перекомпонован и изменен. Алгоритм, представленный по вашей ссылке, этого не делает.

Ja Superior 05.09.2018 03:07

@JaSuperior, Вы правильно прочитали и поняли связанную страницу? В нем есть сценарий и примеры, которые помогут вам разобраться в алгоритме. Фактически, это действительно дает вам правильное место, если длина страницы составляет 960 пикселей и 100 пикселей. Вы должны прочитать все ответы и опробовать прикрепленные к ним скрипки. Этот форум предназначен для вас, а не для выполнения вашей работы.

Ravi 06.09.2018 20:33

Ибо тот, кто проголосовал против этого ответа, должен любезно указать причину. Голоса против без причины - это отстой.

Ravi 06.09.2018 20:36

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

Ja Superior 07.09.2018 03:53
Ответ принят как подходящий

Это становится очень трудным, если html-страница сложная, например, с точно расположенными элементами или изображениями. Однако, если (как в примере с epub.js) контент состоит только из заголовков и абзацев, это возможно.

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

Процесс преобразования содержимого в страницы

Предположим, у вас есть весь ваш контент в одной длинной строке. Начните с разделения всего содержимого на массив слов и тегов. Это не так просто, как разделение пробелами, поскольку пробелы между < и > следует игнорировать (вы хотите сохранить имена классов и т. д. В каждом теге). Также теги должны быть разделены, даже если между тегом и словом нет пробелов.

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

Вам нужны две переменные, pageStart и pageEnd, чтобы отслеживать, какие индексы в массиве являются началом и концом текущей страницы.

Начиная с индекса в pageStart, вы добавляете элементы из массива в качестве содержимого на страницу, проверяя после каждого добавления, переполняется ли содержимое. Когда они переполняются, вы берете индекс, который вы используете, минус 1, как индекс для pageEnd.

Сохранение тегов между разрывами страниц

Теперь, если все в порядке, тогда это должно хорошо заполнить страницу. Если вы хотите перейти на следующую страницу, установите новый pageStart как pageEnd + 1 и повторите процесс. Однако есть некоторые проблемы, которые вы, возможно, захотите исправить.

Во-первых, что произойдет, если страница выйдет за край в середине абзаца? Строго говоря, закрывающий тег </p> не требуется в HTML, поэтому нам не о чем беспокоиться. Но как насчет начала следующей страницы? В нем не будет открывающего тега, и это серьезная проблема. Итак, мы должны убедиться, что мы проверяем, начинается ли содержимое страницы с тега, и если это не так, мы получаем ближайший открывающий тег до текущего pageStart (просто сделайте шаг назад по массиву от pageStart) и добавим его перед остальное содержание.

Во-вторых, как показано в примере, если абзац продолжается на следующей странице, последняя строка текущей страницы все еще выравнивается. Вам нужно проверить, находится ли pageEnd в середине абзаца, и если да, добавить syle = "text-align-last:justify;" в открывающий тег этого абзаца.

Пример реализации

Ручка, показывающая все это в действии, находится по адресу https://codepen.io/anon/pen/ZMJMZZ.

HTML-страница содержит все содержимое в одном длинном элементе. Контент берется прямо из контейнера #page и преобразуется в страницы в зависимости от размера #page. Я не реализовал выравнивание последней строки, если в абзаце происходит разрыв страницы. Измените размер элемента #page в CSS и посмотрите, как изменяется сам контент - обратите внимание, что, поскольку размер страницы фиксирован, вам придется использовать щелчок вперед и назад, чтобы вызвать пересчет. Как только вы привязываете размер страницы к размеру окна, пересчет страниц на лету просто включает добавление слушателя события изменения размера к окну, которое вызывает fillPage.

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

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

samu101108 06.09.2018 05:17

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

Ja Superior 06.09.2018 06:48

@JaSuperior массив рассчитывается только один раз. Для очень больших блоков контента начальное время загрузки может быть заметным, и вы можете рассмотреть возможность динамической загрузки с помощью Ajax. После загрузки производительность при смене страниц должна быть такой же, как и алгоритм, просто проходящий через один и тот же массив. Вместо этого производительность между переключениями страниц зависит от того, сколько контента может поместиться на странице.

jla 06.09.2018 07:29

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

Ja Superior 06.09.2018 10:43

Что ж, я пока не занимаюсь производительностью ... хотя это решение не самое отзывчивое. Приведенное выше решение (jsfiddle.net/samu101108/WTPzn/240) лучше всего для отзывчивости, и это вопрос размещения кнопок навигации, и это будет сделано ...

samu101108 06.09.2018 12:12

@JaSuperior Повторная инициализация не требуется. Вначале весь контент разбивается на массив тегов и слов. Изменение размера страницы путем изменения размера окна или переориентации устройства не требует пересчета содержимого массива, необходимо только определить, какой раздел массива отображать на странице, что сравнительно дешево. В пером выше splitInput(), который превращает контент в массив, запускается только на window.onload.

jla 06.09.2018 12:12

Вот один лайнер для разделения содержимого (с использованием регулярного выражения вместо настраиваемой функции разделения) let splittedContent = htmlString.match(/<[^>]*>|[^<\s]+/gm). В остальном я все еще работаю над его оптимизацией (для контента более 10 тысяч слов требуется больше нескольких секунд)

benuuts 21.01.2020 14:23

Другие вопросы по теме