Эффект реалистичного перелистывания страниц на JS

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



    Демо и документация
    Github
    Плагин для React

    Подобный эффект я реализовывал данным давно, еще в университете и на Delphi. Получилось вполне достойно, правда времени я потратил тогда очень много. Сейчас, во время самоизоляции, стало интересно реализовать что-то подобное на JS, для PC и мобильных устройств.

    Признаться честно, я не уверен в практической применимости данного эффекта, и думаю что в большинстве случаев, нет ничего лучше простой смены картинок без всякой анимации. Но это вполне можно использовать, допустим на сайтов ресторанов, для публикации меню (но главное — что бы рядом дублировалось ссылкой!).

    Написано все на Typescript. Не использовались ни какие сторонние библиотеки. Зависимостей нет.

    Ключевые особенности библиотеки


    • Работает как с простыми изображениями, с отрисовкой на canvas, так и с html блоками — используя css трансформации
    • Довольно гибкая система конфигурации и простое API
    • Поддерживает мобильные устройства
    • Автоматическая смена ориентации между портретным и ландшафтным режимом

    Код писал с оглядкой только на ES6+, и модульная система тоже ES6. Поддержка браузерами в среднем на уровне 90%, основываясь на caniuse.com.

    Установка


    Установка возможна из npm:

    npm install page-flip

    Либо, скачать собранные файлы из репозитория

    Базовый вариант инициализации библиотеки может быть примерно таким:

    <div id="book">
        <div class="my-page">
            Page one
        </div>
        <div class="my-page">
            Page two
        </div>
        <div class="my-page">
            Page three
        </div>
        <div class="my-page">
            Page four
        </div>
    </div>

    import {PageFlip} from 'page-flip';
    const pageFlip = new PageFlip(document.getElementById('book'),
        {
            width: 400, // required parameter - base page width
            height: 600  // required parameter - base page height
        }
    );
    
    pageFlip.loadFromHTML(document.querySelectorAll('.my-page'));

    Более подробное описание, и спецификацию API — можно найти по ссылке выше.
    Я же хочу рассказать о некоторых проблемах и нюансах с которыми столкнулся во время разработки.

    Расчеты


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

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



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

    Для определения угла необходимо рассчитать два значения: расстояние от точки А до верхней и правой границ книги. На изображении ниже они обозначены T и L соответственно.



    G — диагональ угла, можно рассчитать по теореме Пифагора.

    В итоге, для расчета поворота изображения можно воспользоваться следующей формулой: angle = — 2 * acos(L / G), и главное не забывать что точкой трансформации в данном случае является верхний левый угол страницы.

    После расчета угла, остается самая трудоемкая часть — это расчет области видимости страницы. То, что должно быть видимо необходимо оставить, а остальное, соответственно — обрезать.

    Для начала, нужно найти точки пересечения перелистываемой страницы с границами книги. На рисунками они обозначены точками B и C.



    Я делал это самым простым и незамысловатым способом — в лоб. Строил уравнения прямых по двум точкам, и далее искал их точку пересечения.

    Найдя все точки пересечения, определяем вершины области видимости — и по этим точкам уже выполняем обрезку перелистываемой страницы.



    В принципе, вся математика здесь и сводится к двум вещам:

    • расчет угла трансформации
    • расчет области видимости страниц

    Наложение теней производится уже на основе ранее сделанных расчетов.

    Теперь перейдем к некоторым моментам, с которыми пришлось столкнутся при реализации.

    Общий алгоритм довольно простой и сводится к поворотам и обрезке страниц.

    В случае с canvas и простыми изображениями — все довольно просто. После выполнения расчетов, используются методы 2d контекста холста, такие как translate, rotate и clip.
    С html блоками несколько сложнее. И если с поворотом, благодаря css трансформациям, проблем нет, то с обрезкой все оказалось несколько хуже.

    В итоге, самым простым способом, оказалось использование свойства clip-path и css фигуры polygon. Но прежде чем задавать вершины многоугольника для обрезки, необходимо выполнить трансформацию координат точек из «глобальных» холста — в локальные, относительно html элемента. Решается это обратным применением матрицы поворота, со сдвигом относительно позиции элемента.

    Другой проблемой было масштабирование и авто-позиционирование книги. Это я попытался решить объектом конфигурации, который передается при создании. Но в итоге, параметров стало довольно много, и получилось не совсем удобно и не очевидно.

    Для сборки сначала использовал Webpack, но в итоге все-таки решил попробовать rollup.js, и был очень приятно удивлен итоговым кодом. Webpack пока остается, поскольку справляется со сборкой на лету в несколько раз быстрее, а при разработке это удобнее.

    Буду рад услышать комментарии, и предложения по дальнейшей разработке библиотеки.
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 14

      +1
      Выглядит офигенно! Обязательно применю как-нибудь по случаю, очень отлично смотрится.
        +1
        Спасибо) Будут вопросы по подключению / настройке — пишите!
        0
        По доработке. Добавьте плотность страниц.
        Т.е. это мягкая книга. А есть книги, у которых плотные пластиковые страницы.
        Эту либу можно использовать в фотомагазинах, которые печатают фотокниги и у них могут быть мягкие страницы, а могут твердые.
          0
          Да, спасибо, это как раз было в планах
            0
            Тогда можно и плюшевые страницы сделать — в детских книжках пригодится — бывают книжки для купания. Тогда уж и с формой-прозрачностью заморочиться.

            book
          +1
          Выглядит действительно неплохо, но есть небольшой момент в конце анимации перелистывания, который выглядит чуток неестественно — такое ощущение что страница просто линейно доходит/доезжает до конца, в жизни это было бы скорее похоже на эффект ускорения и постепенного торможения (что-то наподобие ease-in-out из css). Но возможно это лишь кажется.
            0
            Анимация действительно линейная, можно действительно попробовать реализовать что нибудь подобное ease-in-out, попробую
            0
            Интересно было бы увидеть видео.
            Совершенствоваться разумеется можно — скажем рисовать криволинейную поверхность (страницу) и выводить на неё текст так, чтобы видны были искажения текста. Но в общем и так весьма хорошо.
              +1

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


              Как результат: заголовок хороший, а реализация — нет. Это не плохая реализация, но это не "реалистично".

                0
                Да это все таки двухмерное решение. В той же ibooks, сделано в 3d и выглядит очень круто
                  +2

                  Тогда не надо писать, что оно реалистичное. Да, хорошая реализация перелистывания, но не реалистичная.

                +1
                А в чем принципиальное отличие от этого проекта — github.com/blasten/turn.js?
                  +4
                  В общем-то, принципиального отличия конечно нет, оба по факту выполняют один и тот же функционал. Но отличия в деталях и реализации:
                  — turn.js требует jQuery и реализован как плагин к ней. PageFlip же — не требует никаких зависимостей и оформлен в виде ES6 модуля
                  — PageFlip может автоматически подстраиваться под размер экрана, в том числе со сменой ориентации книги
                  — Лицензия turn.js против коммерческого использования, PageFlip — можно использовать где и как угодно
                  0
                  Чутьё мне подсказывает, что статье хронически не хватает вычитки в части пунктуации. >_<
                  ИМХО местами много лишних тире.

                  Посмотрел пример. При перелистывании с изгибом страницы блоки текста у меня (Firefox 77.0.1) как-то странно выглядят. Страница изгибается, а текст остаётся плоским.

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое