Итак, требовалось: опубликовать книгу с иллюстрациями онлайн так, чтобы ее можно было дописывать и переписывать, и извещать об этом читателей. Быстрое и изящное решение под катом.
Изначально планировалось написать полноценную админку с WYSIWYG-редактором, у которого обязательно должна быть хорошая проверка орфографии, удобный интерфейс и возможность прицепить к записи анимацию. В планах было анимировать рисование карандашных набросков на полях. Забегая чуть вперед, скажу, что делали саму отрисовку с помощью vivus.
В общем, купили домен, но разработка серверной части никак не шла: получались какие-то громоздкие уродцы, у которых лишнего было больше, чем полезного. Может так бы все и осталось, если бы мне на глаза не попалось описание одного продукта (jekyll), где в преимуществах при прочих было отмечено отсутствие базы данных. Тут-то меня и осенило, что книге база данных
И завертелось: решено было хостить книгу на github, а страницы размечать markdown, благо для любимого автором word’а быстро нашелся плагин для сохранения в этом формате (Writage). Ну а для преобразования его в html подвернулась библиотека ShowDown
Достаточно быстро задуманное было реализовано. Книга состоит из файлов-страничек в формате .md и файла .json, в котором перечислено что за чем идет, какую анимацию грузить и надо ли вообще.
Вот так выглядит конфигурационный файл:
[{
"file": "имяфайла.md",
"hash": "хештег",
"animation": {
"svg": "анимация.svg",
"duration": 2000,//Продолжительность анимации в милисекундах
"style": "width:400px;height:300px;opacity:0.5;float:right;margin-right:20px;"//Стиль для блока анимации, чтобы было понятно где его размещать
},
{
"file": "имяфайла2.md",
"hash": "хештег2"
}]
Собственно, сами страницы загружаются асинхронно при прокрутке и по хештегу, чтобы пользователь мог продолжить читать с того же месте, где остановился. При загрузке страницы по хештегу подгружается еще и предыдущая запись, если она есть, чтобы не казалось, что это начало книги.
Синхронный Ajax браузер мне использовать не дал, но может оно и к лучшему. Делала так: сначала создаются блоки с id=хештег, потом отправляются запросы к непосредственным страницам. Таким образом, все запрошенные страницы встают на свое место, независимо от того, какой из запросов быстрее выполнится.
Вот функция загрузки страницы, которая принимает объект страницы из конфигурационного файла и опционально параметры: нужно ли изменять хеш страницы, добавить ли эту страницу после текущей или перед и нужно ли скроллить к новой странице после загрузки. Параметры дают возможность фоновой загрузки страниц.
function loadPage(page, options = {}) {
if (options.changeHash === undefined) {
options.changeHash = true;
}
if (options.next === undefined) {
options.next = true;
}
if (options.scroll === undefined) {
options.scroll = true;
}
if (options.changeHash) {
document.location.hash = "#" + page.hash;
}
if ($("#" + page.hash).size() == 0) {
if (options.next) {
$("#content").append('<div id="' + page.hash + '"></div>');
} else {
$("#content").prepend('<div id="' + page.hash + '"></div>');
}
$("#loader").show();
jQuery.ajax({
url: "/book/" + page.file,
success: function(result) {
//Преобразуем markdown в html
var converter = new showdown.Converter();
var html = converter.makeHtml(result);
$("#" + page.hash).html(html);
if(page.animation!==undefined){
//Рисуем иллюстрацию, если она есть
$("#" + page.hash).prepend('<div id="animation-'+page.hash+'" style="'+page.animation.style+'"></div>');
var vivus=new Vivus('animation-'+page.hash, {duration: page.animation.duration, file: '/svg/'+page.animation.svg, type:'oneByOne'}, finishedDrawing);
}
$("#loader").fadeOut();
if (options.scroll) {
//Ползем к загруженной странице, если нужно
$('html,body').animate({
scrollTop: $("#" + page.hash).offset().top
}, 300, 'swing');
}
}
});
}
}
Собственно, вот и вся магия. Автор спокойно пишет себе книгу и в пару кликов публикует (с редактурой json файла он тоже справился сам).
Готовую книгу (она на английском) можно почитать тут, а рассмотреть внутренности подробнее в коде на github.
Спасибо за уделенное время, надеюсь, что вы не сочли его потраченным впустую!
Комментарии (33)
J_K
01.10.2016 07:03А где книга-то? По ссылке одна-единственная страница.
ValeriaVG
01.10.2016 09:41Автор хочет преподнести ее в формате дневника — в данный момент там действительно одна страница с описанием предпосылок к этому. Идея автора, насколько мне объяснили, в том, чтобы читатель мог находиться в одной плоскости с книгой, почуствовать себя частью того мира, который она (книга) описывает и получать новую информацию в режиме реального времени с вымышленным миром.
Вкратце: это книга — блог или, скорее, сериал.
zartarn
01.10.2016 09:41+2Свой GitBook с блэкджеком и куртизанками?
ValeriaVG
01.10.2016 09:50+1Нет, это не gitbook. Описанное мною решение для совершенно другой задачи.
Gitbook замечательно подходит для технической литературы за счет создания четкой структуры страниц.
Я же описывала подход для создания «живой» художественной книги. В моем проекте страницы открываются и рисуется иллюстрация, но никто не мешает добавить любые другие эффекты — будь то музыка, видео, игра на canvas или еще что-то по аналогии.
nikitastaf1996
01.10.2016 16:01>чтобы ее можно было дописывать и переписывать
В таком случае нужна версионность.
ecl
01.10.2016 17:11Ничего не понял. Читатель должен после прочтения статьи стать чуточку (или не чуточку) умнее, осведомленее или веселее. Разве нет?
Здесь я почувствовал себя обманутым и растерянным. Что мне хотели донести?ValeriaVG
01.10.2016 23:11+1А что вы рассчитывали прочитать в статье «Онлайн книга своими руками на JavaScript»? Все инструменты расписала, основные куски кода показала, ссылку на код целиком дала. Может что-то подробнее рассказать? Что было непонятно?
DenimTornado
01.10.2016 18:48+2Подождите, автоматизации уведомлений об изменениях в книге нет, правильно? Тогда зачем эта морока с .md файлами? Тут по идее просто нужно добавление и изменение контента. То есть одна таблица в бд + ckeditor. И править проще, и не надо это вот аяксовые loadPage.
ValeriaVG
01.10.2016 23:09А почему вы считаете, что база данных тут подошла бы больше?
Поиска / сортировки / массового обновления в этом проекте нет и точно не будет. Автор один, все тексты в файлах, а к базе пришлось бы еще и отдельное кеширование прикручивать. А загрузка по ajax — это не вынужденная мера, это изначальное требование. Во-первых: нужно отследить загрузку страницы, чтобы проиграть анимацию, а во-вторых это сейчас там две с половиной страницы, а когда их 400 будет? Одним махом все грузить из базы/кеша для каждого пользователя?DenimTornado
02.10.2016 00:42+2Возможно я не прав и выбранная вами архитектура верная, но ответьте, пожалуйста на несколько вопросов.
1. Каким образом автор добавляет новую страницу? Правильно ли я понимаю, что от него требуется, экспортировать из Ворда .md файл, затем по фтп закинуть в определённую папку на сервер, затем поправить конфиг (тут отдельный вопрос по блоку animation).
2. Если я открою сразу 200-ую страницу, то загрузятся сразу три? Текущая + предыдущая + следующая?
3. Почему вы не держите статику на своём сервере? Сейчас среднее время загрузки первой страницы 2-4 секунды как из-за статики снаружи.
4. Что, если человек забудет сохранить в закладке на нужной странице? Ему ведь придётся пролистывать все 200-300 страниц?
5. Вы в курсе, что сайт не работает в ИЕ11 и в любом другом браузере, который не поддерживает ES6?ValeriaVG
02.10.2016 01:12-21. Плагин, описанный в статье позволяет сразу сохранять в этом формате из Word, а Github клиент в два клика изменения публикует. Касательно редактуры конфига — в данном случае автор сам справился, но никто не мешает написать UI, если такая необходимость возникнет
2. Загрузятся две. Предыдущая и запрошенная.
3. На своем сервере потребовалось бы поднять свой git, к нему делать отрисовку логов, давать возможность watch. По поводу скорости: сайт с большой картинкой на первом экране и svg на странице (суммарно 500кб) + шрифт, пару скриптов в т.ч. с шарилками. Сервер тут вообще не причем — он быстро все отдает.
4. А если вы в книгу человек закладку вложить забудет? Пока сложно сказать, есть ли нужда в карте. Технически — реализуемо и быстро (все есть в конфиге — нужно только вывести по порядку).
5. Да. В данном случае нет задачи поддержать пользователей IE, Edge и старых версий — целевая аудитория продукта не та, да и я лично против поддержки кривого по.
А на мой вопрос вы так и не ответили…bromium
02.10.2016 11:10+3Еще не старый айпад, новая айось. Ваша «книга» не работает — тоже нецелевая аудитория? Тоже кривое ПО? А какое тогда прямое!?
«Фтопку» такую «книгу», которую можно читать только на десктопе или ноутеValeriaVG
02.10.2016 12:38+1Спасибо, согласна с вами, не знала про такую проблему ios, внесла соответсвующие правки.
Source
02.10.2016 00:02+2А мне понравилась статья. На мой взгляд она не столько про конкретный код, сколько про то, что не нужно увлекаться оверинженерингом. Задача то на самом деле была нетиповая и получившееся решение выглядит весьма элегантно. Код, конечно, можно ещё подшлифовать, но куда ж без этого.
Плюс я узнал про существование Vivus и Writage. Кстати, интересно, насколько хорошо Writage справляется со своей задачей в плане истории изменений? Т.е. насколько сильно результат отличается от ручного редактирования Markdown, если отличается?ValeriaVG
02.10.2016 00:14Writage справляется неплохо, если у того, кто им пользуется есть представление о markdown. В моем случае пришлось объяснять, что специальные символы (например "-" с последующим пробелом в начале строки) лучше не использовать. В остальном — результат не отличается.
Согласна с вами насчет кода, куда ж без правок.
rumkin
02.10.2016 05:42По-моему, вполне оправданное применение KISS. Но в плане типографики есть недочет, строки растягиваются на всю длину окна, отступы по краям небольшие, для комфортного чтения придется подстраивать ширину окна. Текст из-за этого становится "тягучим", обратите внимание на типографику сайта medium.com, особенно на размер шрифта, высоту строки и отступы.
Gibrrr
02.10.2016 11:24+3Статья понравилась, как обзор возможных решений для конкретной небольшой задачи.
Одно «но» — в самой готовой книге наблюдается глюк со скроллом (Chrome): если пролистать книгу до самого конца, а потом пытаться отскроллить вверх, то в определенный момент происходит сброс обратно в конец книги.
antirek
03.10.2016 06:36Зачем Showdown?
wintersmith — готовый генератор сайта на основе markdown текстов и jade-шаблонов.ValeriaVG
03.10.2016 10:34+1Статья про Keep It Simple (Silly,Sweetheart,etc), как верно подметили rumkin и Source в комментариях, и вы не первый уже предлагаете использовать что-то, от чего понадобится минимум функций просто потому что оно уже есть / знакомо вам.
Единственное, что мне нужно было — это преобразовать markdown разметку в html. Showdown это и делает, это и ничего больше.
kahi4
И как же извещаете об этом читателей?
А так — это даже на тестовое задание junior'у тянет с трудом, если честно и по секрету.
ValeriaVG
Что и где изменилось в книге есть в логе github, непосредственно средство время и формат оповещения читателей целиком на попечении автора.
Вам не стоит рассматривать предоставленный код как вызов самому себе и обществу, код там прост и ничего принципиально нового не использует. В статье я хотела показать, что решая простую задачу лучше использовать простые решения, а не то, что привыкли/положено/модно.
И нет, мой маленький кодик куда лучше показывает уровень знаний нежели чем крестики-нолики по пять в ряд
geakstr
> мой маленький кодик куда лучше показывает уровень знаний нежели чем крестики-нолики по пять в ряд
Если уж сравнивать с заданием про крестики-нолики, то нет, не показывает
ValeriaVG
А что вы считаете уровнем? Просто/сложно? Я склонна ко мнению, что это оценка субъективна. Задача про крестики-нолики скорее на логику нежели чем на знание JavaScript, к примеру. Джуниору, возможно, только она и нужна, опыта он же в конце концов и идет набираться. А вышеописанную задачу, которая по сути сводится к проектированию архитектуры приложения, нежели к самому коду, он будет решать на
(см. ниже в комментариях)Ну и так, чтобы закрыть тему: я не ищу работу ни джуниором, ни кем ли бы то ни было и не претендую ни на ваши места, ни на ваши заслуги. Можете откинуться на спинку стула и отметить еще одну победу. Считайте, что ваш оппонент позорно бежал. Продолжать сей спор показывает — не показывает без указаний на конкретные огрехи кода считаю бессмысленным.
geakstr
Проникся уважением к вашему ответу. Но я только имел ввиду, что задание про крестики-нолики со всеми «плюсами» из списка — годное, комплексное и хорошо покажет кандидата как со стороны «на подумать», так и в плане, собственно, написания кода.