Онлайн-книга своими руками на JavaScript

    image

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

    Изначально планировалось написать полноценную админку с 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.

    Спасибо за уделенное время, надеюсь, что вы не сочли его потраченным впустую!
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 33

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

      И как же извещаете об этом читателей?


      А так — это даже на тестовое задание junior'у тянет с трудом, если честно и по секрету.

        +4
        Что и где изменилось в книге есть в логе github, непосредственно средство время и формат оповещения читателей целиком на попечении автора.

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

        И нет, мой маленький кодик куда лучше показывает уровень знаний нежели чем крестики-нолики по пять в ряд
          0
          > мой маленький кодик куда лучше показывает уровень знаний нежели чем крестики-нолики по пять в ряд

          Если уж сравнивать с заданием про крестики-нолики, то нет, не показывает
            +4
            А что вы считаете уровнем? Просто/сложно? Я склонна ко мнению, что это оценка субъективна. Задача про крестики-нолики скорее на логику нежели чем на знание JavaScript, к примеру. Джуниору, возможно, только она и нужна, опыта он же в конце концов и идет набираться. А вышеописанную задачу, которая по сути сводится к проектированию архитектуры приложения, нежели к самому коду, он будет решать на
            таблица в бд + ckeditor. И править проще, и не надо это вот аяксовые loadPage.
            (см. ниже в комментариях)

            Ну и так, чтобы закрыть тему: я не ищу работу ни джуниором, ни кем ли бы то ни было и не претендую ни на ваши места, ни на ваши заслуги. Можете откинуться на спинку стула и отметить еще одну победу. Считайте, что ваш оппонент позорно бежал. Продолжать сей спор показывает — не показывает без указаний на конкретные огрехи кода считаю бессмысленным.
              0
              Проникся уважением к вашему ответу. Но я только имел ввиду, что задание про крестики-нолики со всеми «плюсами» из списка — годное, комплексное и хорошо покажет кандидата как со стороны «на подумать», так и в плане, собственно, написания кода.
        +1
        Чем gitbook не подошёл?
          +1
          Да в общем-то всем: оглавление было не нужно, нужна была автозагрузка страниц по скроллу и проигрывание анимации при этом. Вроде как читаешь ты себе книгу, а на полях рисуется тематическая иллюстрация.
          0
          А где книга-то? По ссылке одна-единственная страница.
            0
            Автор хочет преподнести ее в формате дневника — в данный момент там действительно одна страница с описанием предпосылок к этому. Идея автора, насколько мне объяснили, в том, чтобы читатель мог находиться в одной плоскости с книгой, почуствовать себя частью того мира, который она (книга) описывает и получать новую информацию в режиме реального времени с вымышленным миром.

            Вкратце: это книга — блог или, скорее, сериал.
            • UFO just landed and posted this here
            +2
            Свой GitBook с блэкджеком и куртизанками?
              +1
              Нет, это не gitbook. Описанное мною решение для совершенно другой задачи.

              Gitbook замечательно подходит для технической литературы за счет создания четкой структуры страниц.

              Я же описывала подход для создания «живой» художественной книги. В моем проекте страницы открываются и рисуется иллюстрация, но никто не мешает добавить любые другие эффекты — будь то музыка, видео, игра на canvas или еще что-то по аналогии.
            • UFO just landed and posted this here
                0
                Поэтому github
                0
                Ничего не понял. Читатель должен после прочтения статьи стать чуточку (или не чуточку) умнее, осведомленее или веселее. Разве нет?
                Здесь я почувствовал себя обманутым и растерянным. Что мне хотели донести?
                  +1
                  А что вы рассчитывали прочитать в статье «Онлайн книга своими руками на JavaScript»? Все инструменты расписала, основные куски кода показала, ссылку на код целиком дала. Может что-то подробнее рассказать? Что было непонятно?
                  +2
                  Подождите, автоматизации уведомлений об изменениях в книге нет, правильно? Тогда зачем эта морока с .md файлами? Тут по идее просто нужно добавление и изменение контента. То есть одна таблица в бд + ckeditor. И править проще, и не надо это вот аяксовые loadPage.
                    0
                    А почему вы считаете, что база данных тут подошла бы больше?

                    Поиска / сортировки / массового обновления в этом проекте нет и точно не будет. Автор один, все тексты в файлах, а к базе пришлось бы еще и отдельное кеширование прикручивать. А загрузка по ajax — это не вынужденная мера, это изначальное требование. Во-первых: нужно отследить загрузку страницы, чтобы проиграть анимацию, а во-вторых это сейчас там две с половиной страницы, а когда их 400 будет? Одним махом все грузить из базы/кеша для каждого пользователя?
                      +2
                      Возможно я не прав и выбранная вами архитектура верная, но ответьте, пожалуйста на несколько вопросов.

                      1. Каким образом автор добавляет новую страницу? Правильно ли я понимаю, что от него требуется, экспортировать из Ворда .md файл, затем по фтп закинуть в определённую папку на сервер, затем поправить конфиг (тут отдельный вопрос по блоку animation).
                      2. Если я открою сразу 200-ую страницу, то загрузятся сразу три? Текущая + предыдущая + следующая?
                      3. Почему вы не держите статику на своём сервере? Сейчас среднее время загрузки первой страницы 2-4 секунды как из-за статики снаружи.
                      4. Что, если человек забудет сохранить в закладке на нужной странице? Ему ведь придётся пролистывать все 200-300 страниц?
                      5. Вы в курсе, что сайт не работает в ИЕ11 и в любом другом браузере, который не поддерживает ES6?
                        –2
                        1. Плагин, описанный в статье позволяет сразу сохранять в этом формате из Word, а Github клиент в два клика изменения публикует. Касательно редактуры конфига — в данном случае автор сам справился, но никто не мешает написать UI, если такая необходимость возникнет

                        2. Загрузятся две. Предыдущая и запрошенная.

                        3. На своем сервере потребовалось бы поднять свой git, к нему делать отрисовку логов, давать возможность watch. По поводу скорости: сайт с большой картинкой на первом экране и svg на странице (суммарно 500кб) + шрифт, пару скриптов в т.ч. с шарилками. Сервер тут вообще не причем — он быстро все отдает.

                        4. А если вы в книгу человек закладку вложить забудет? Пока сложно сказать, есть ли нужда в карте. Технически — реализуемо и быстро (все есть в конфиге — нужно только вывести по порядку).

                        5. Да. В данном случае нет задачи поддержать пользователей IE, Edge и старых версий — целевая аудитория продукта не та, да и я лично против поддержки кривого по.

                        А на мой вопрос вы так и не ответили…
                          +3
                          Еще не старый айпад, новая айось. Ваша «книга» не работает — тоже нецелевая аудитория? Тоже кривое ПО? А какое тогда прямое!?

                          «Фтопку» такую «книгу», которую можно читать только на десктопе или ноуте
                            +1
                            Спасибо, согласна с вами, не знала про такую проблему ios, внесла соответсвующие правки.
                    +2

                    А мне понравилась статья. На мой взгляд она не столько про конкретный код, сколько про то, что не нужно увлекаться оверинженерингом. Задача то на самом деле была нетиповая и получившееся решение выглядит весьма элегантно. Код, конечно, можно ещё подшлифовать, но куда ж без этого.
                    Плюс я узнал про существование Vivus и Writage. Кстати, интересно, насколько хорошо Writage справляется со своей задачей в плане истории изменений? Т.е. насколько сильно результат отличается от ручного редактирования Markdown, если отличается?

                      0
                      Writage справляется неплохо, если у того, кто им пользуется есть представление о markdown. В моем случае пришлось объяснять, что специальные символы (например "-" с последующим пробелом в начале строки) лучше не использовать. В остальном — результат не отличается.

                      Согласна с вами насчет кода, куда ж без правок.
                      0

                      По-моему, вполне оправданное применение KISS. Но в плане типографики есть недочет, строки растягиваются на всю длину окна, отступы по краям небольшие, для комфортного чтения придется подстраивать ширину окна. Текст из-за этого становится "тягучим", обратите внимание на типографику сайта medium.com, особенно на размер шрифта, высоту строки и отступы.

                        0
                        Большое спасибо, обязательно посмотрю.
                          0
                          text-align: justify??? Лебедев негодует и требует смертной кары! :D
                            0
                            Ни на medium.com, ни на the-downpour.com этого нет. Откуда вы это вытащили?
                          +3
                          Статья понравилась, как обзор возможных решений для конкретной небольшой задачи.
                          Одно «но» — в самой готовой книге наблюдается глюк со скроллом (Chrome): если пролистать книгу до самого конца, а потом пытаться отскроллить вверх, то в определенный момент происходит сброс обратно в конец книги.
                            0
                            Спасибо, а можно подробнее? Мне не удалось повторить проблему
                              0
                              А, нет, удалось, спасибо. Поправлю.
                              0
                              Зачем Showdown?
                              wintersmith — готовый генератор сайта на основе markdown текстов и jade-шаблонов.
                                +1
                                Статья про Keep It Simple (Silly,Sweetheart,etc), как верно подметили rumkin и Source в комментариях, и вы не первый уже предлагаете использовать что-то, от чего понадобится минимум функций просто потому что оно уже есть / знакомо вам.

                                Единственное, что мне нужно было — это преобразовать markdown разметку в html. Showdown это и делает, это и ничего больше.

                              Only users with full accounts can post comments. Log in, please.