Здравствуйте, меня зовут Дмитрий Карловский и я иногда выступаю на конференциях, митапах, а так же с недавних пор сам вхожу в команду организаторов одного из них — PiterJS. Недавно у нас был юбилей — 40 проведённых митапов. Но вместо того, чтобы расслабиться и получать поздравления, мы запарились и сами подготовили доклады от организаторов.
Но и этого нам мало, поэтому мы решили отметить юбилей по крупному, организовав конференцию на берегах Невы PiterJSConf, которая пройдёт уже в эту субботу 7 сентября 2019. Спешите записываться, пока ещё есть свободные места, ведь участие в ней для вас будет совершенно бесплатно.
Мы всё это делаем не за деньги, а за великую идею, что знания должны быть бесплатны. Поэтому всё, что мы делаем, доступно в Open Source. Мы с радостью делимся своими наработками, знаниями и опытом с другими. И призываем к сотрудничеству организаторов из других городов для создания открытой платформы организации технологических митапов на регулярной основе. Присоединяйтесь к нам в качестве организатора, партнёра, докладчика, волонтёра, патрона или просто слушателя.
Перед вами интерфейс, который видят слушатели во время выступления. В нём нет ничего лишнего.
Сверху выводится название и номер слайда. У слушателей могут возникнуть вопросы. И быстро записав этот номер, они смогут после выступления назвать его и тем самым избавить аудиторию от утомительного ожидания, когда же докладчик найдёт слайд, по которому задаётся вопрос.
Внизу можете обратить внимание на прогресс бар, показывающий опоздавшим слушателям сколько они пропустили. А всем остальным — сколько ещё осталось до конца. Рассчитывается он не по числу слайдов, а по объёму рассказанной речи.
Смотри как я могу
Основная идея в том, чтобы позволить докладчику сконцентрироваться на содержимом и не беспокоиться об оформлении. Докладу вовсе не нужны какие-то особые красоты и эффектные анимации. Иначе оформление будет перетягивать внимание на себя. А содержание рискует пролететь мимо ушей.
Но оформление тем не менее должно быть всё же опрятным, чтобы не портить впечатление о докладе. Поэтому дизайн приложения простой, не броский, и, что самое главное, соответствующий рекомендациям программных комитетов.
Слева отображается то, что видят слушатели. А справа — заметки докладчика. Они помогут вам вспомнить потерянную мысль, не отворачиваясь от слушателей и не напрягая их долгой… эм… это… паузой.
Главное — контент
Поэтому в нашем случае контент пишется как статья в формате MarkDown и выкладывается на какой-нибудь GitHub Pages. А всё остальное берёт на себя веб-приложение.
# Название первого слайда
Содержимое первого слайда
# Название второго слайда
Содержимое второго слайда
После выступления текстовую расшифровку доклада часто выкладывают в виде статьи на каком-нибудь Хабре. Делать её могут месяц, два, пол года. Задача это неблагодарная переводить речь в текст. Но, благодаря тому, что исходники слайдов уже в формате markdown и содержат комментарии докладчика, их в таком виде можно сразу публиковать.
Стили текста
Обычный текст отображается только докладчику. А всякие картинки, списки, таблицы, цитаты и тп штуки видны всем.
Обратите внимание, что фон слайдов слегка серый. Поэтому изображения лучше готовить не на белом фоне, а с прозрачностью, чтобы вокруг картинок не было неприятного белого прямоугольника. Сделано это, чтобы белые области смотрелись контрастно, а не сливались с фоном.
Видео
Можно размещать видео, веб страницы и любой другой внешний контент, используя тот же MarkDown синтаксис, что и для вставки картинок.
Для примера я вставил видео иллюстрирующее, что современные интерфейсы настолько простые и удобные, что с ними справится даже обезьяна.
Клавиатурная навигация
Вы можете переключать слайды стрелочками на клавиатуре. Но чтобы не быть привязанным к ноуту, а свободно ходить по сцене, вам пригодится радио удлинитель пальца. Он же — кликер.
Но что если кликер сломался?
Кодовые фразы
Вы, наверно, думаете, что в зале у меня тут где-то есть засланный казачок, который переключает слайды вместо меня? Однако, это не так.
Дальше, пожалуйста.
Назад, пожалуйста.
Слайд номер 5, будь добра.
На начало, пожалуйста.
В конец, пожалуйста.
Найди "котейку", будь добра.
Повтори, пожалуйста.
Помолчи, будь любезна.
Продолжай, пожалуйста.
Выключи свет, будь любезна.
Повтори, пожалуйста. Голос свыше повторяет последнюю фразу.
Да, слайдами можно полностью управлять голосом, оставляя свои руки свободными для жестикуляций. Тут используется стандартное веб-апи для распознавания и синтеза голоса.
Но повторять эти кодовые фразы по десять раз подряд — это скучно, поэтому $hyoo_slides умеет анализировать заметки докладчика и, когда вы произносите последнее слово, переключать слайд автоматически.
Жесты пальцем
Ладно, усложняем ситуацию. Кто-то наслушавшись вашего выступления, решил посмотреть ваши слайды с планшета, пока едет в метро домой.
Клавиатуры нет. Поезда шумят. Тут на выручку приходят обычные пальцевые жесты.
Оффлайн
Но тут он заезжает в тоннель и у него пропадает связь.
Не беда, у нас же Web2.0 HTML5 Progressive Web Application с полной работоспособностью даже, когда нет интернета.
Печать в PDF
Но тут к вам подходят организаторы и говорят: "Хотим PDF".
Какой PDF? У нас же тут мультимедийный интерактивный Web2.0 HTML5 Progressive Web Application. Однако, вам объясняют, что приложение сегодня есть, а завтра его уже нет. А если есть, то хочет денежку. А если не хочет, то слайды там уже могут быть изменены до неузнаваемости. А PDF лежит тихонько в архиве ровно с тем содержимым, которое соответствует записанному во время выступления видео.
Ну что ж, не беда, жмём Ctrl+P, выбираем "Печать в PDF" и получаем то, что нужно. Делается это просто — отслеживается событие onbeforeprint и, когда оно возникает, вместо одного лишь текущего слайда рендерятся вообще все слайды. А на onafterprint, все, кроме текущего, слайды удаляются.
На этом списков возможностей пока что закончен.
Как создать презентацию
Попробовать в деле $hyoo_slides очень просто. Вам потребуется readme.md с вашим контентом и картинки. Так же рядом нужно будет скопипастить index.html, который редиректнет на веб приложение и откроет вашу презентацию в нём. А так же offline.js для поддержки оффлайна.
Имейте ввиду, что этот index.html будет выдавать приложению любые файлы, доступные с того домена, куда вы всё это дело выложите. GitHub Pages — вполне удобный и безопасный вариант. Сам его использую.
Другие приложения
Если вам понравилось это приложение, то можете глянуть и другие интересные приложения, реализованные на фреймворке $mol. Они настолько легковесные, что даже несколько десятков их не страшно загрузить разом на одном слайде.
Подробнее о фреймворке можно узнать на отдельной презентации. Копнуть глубже можно в презентации посвящённой ОРП. А приподнять завесу грядущего можно в презентации о квантовании вычислений.
Перед вами верхнеуровневое описание одного экрана на языке view.tree и эквивалентный код на TypeScript. Тут мы объявляем компонент $hyoo_slides_page, который расширяет базовый компонент $mol_view. У этого компонента есть свойство sub. Всё, что возвращает это свойство, будет отрендерено внутри компонента. Поэтому мы переопределяем его, передавая в качестве значения массив из двух элементов: Listener — компонент вывода слайда слушателям и Speaker — компонент дополнительной панели докладчика.
Переключение раскладки страницы
В дополнение к описанию структуры мы можем приложить и программную логику, позволяющую любые свойства вычислять динамически.
Тут логика у нас простая: слайды для слушателей выводим всегда, а вот панель докладчика показываем только, если текущая роль — speaker. Если роль изменится, то и раскладка приложения тоже изменится благодаря магии объектного реактивного программирования.
Роутинг
Роль мы будем брать из параметра адреса, через специальный реактивный API $mol_state_arg.
По какой бы причине ни поменялся адрес — роль будет извлечена из него автоматически, и проведена через в этот метод.
Структура интерфейса слушателя
Давайте опишем интерфейс слушателя.
Listener $mol_page
title <= title
tools /
<= Slide_switcher
body /
<= Listener_content
<= Progress
Он использует стандартный компонент $mol_page который рисует типичную страницу с шапкой и телом. В шапке есть область, куда выводится название страницы. Через свойство title можно указывать, что туда выводить. Что мы и сделали, связав его свойство title с нашим одноимённым свойством. Теперь, меняя наше свойство, мы полностью контролируем, что будет выводиться на странице в качестве заголовка.
Справа в шапке, есть область вывода дополнительных инструментов — tools. В неё мы выводим Slides_switcher — компонент для отображения номера слайда и переключения между соседними слайдами.
И, наконец, в качестве тела страницы в body мы выводим содержимое слайда и прогресс бар.
Структура переключателя страниц
Как же реализовать Slide_switcher? Просто используем стандартный компонент $mol_paginator.
Всё, что у него есть — это изменяемое свойство value, которое мы двусторонне связываем с нашим свойством, содержащим номер текущего слайда. Никаких импортов, колбэков, событий и прочего хлама. Эти две строчки — это всё, что необходимо, чтобы у вас на странице появился работающий переключатель страниц.
Структура содержимого слайда
Для отображения содержимого слайда мы воспользуемся опять же стандартным компонентом $mol_text.
Listener_content $mol_text
uri_base <= uri_base
text <= listener_content
Он принимает текст в формате markdown и визуализирует его. Так как ссылки в этом тексте будет относительно исходного файла, а не нашего приложения, то в свойство uri_base мы передаём ссылку, относительно которой будут резолвиться все пути.
Структура индикатора прогресса
Как вы уже, наверно, догадались для отображения прогресса тоже есть стандартный компонент — $mol_portion.
Progress $mol_portion
portion <= progress
portion: [ 0 .. 1 ]
Скармливаем ему число от 0 до 1 и получаем заполненную на эту долю индикатор.
Структура интерфейса докладчика
В интерфейсе докладчика у нас есть кое-что по интересней. Отображаемые в шапке инструменты никак не привязаны к текущей странице — они общие для всего приложения. Поэтому вместо того, чтобы хардкодить их тут, мы разместим ту лишь слот speaker_tools, через который будем передавать список компонент извне.
Speaker $mol_page
head <= speaker_tools /$mol_view
body /
<= Speaker_content
Структура приложения
Теперь поднимемся уровнем выше и создадим компонент приложения $hyoo_slides, который использует компонент страницы.
У любого $mol_view компонента есть свойство plugins через которое можно подключать к нему дополнительную логику. Подключаемые плагины живут на том же DOM узле, что и сам компонент. Компонент их инициирует при своём рендеринге. А когда о перестаёт рендериться — плагины уничтожаются автоматически.
Также мы объявили свойство Page, которое для каждого индекса возвращает отдельный экземпляр разработанного нами ранее компонента $hyoo_slides_page.
Настройка страницы извне
Page!index $hyoo_slides_page
role <= role
slide?val <=> page_slide!index?val
speaker_tools /
<= Speech_toggle
<= Speech_text
<= Open_listener
Свойство role мы передаём в подкомпонент как есть. Свойство slide компонента страницы связываем двусторонней связью со свойством page_slide приложения. Обратите внимание, что page_slide принимает не только опциональное новое значение, но и индекс страницы. Это позволяет для каждой страницы возвращать свой номер. Наконец, в ранее объявленный нами слот speaker_tools мы помещаем три компонента, помогающие управлять слайдами.
Структура переключателя голосового управления
Speech_toggle мы реализуем через стандартный компонент $mol_check_icon, рисующий иконку. При клике на него, он переключает флаг checked. А текущее состояние отображается изменением цвета иконки.
Иконку мы взяли из пакета $mol_icon, где из 4000 иконок, выполненных в аскетичном material design стиле, легко найти нужную.
Структура кнопки открытия ведомого окна
Тут всё просто. Эта кнопка будет ссылкой $mol_link. Ей можно задать свойство uri с адресом, а можно поступить хитрее и просто пропатчить текущий адрес, заменив через arg некоторые параметры.
Listener_open $mol_link
target _blank
arg *
role listener
slide null
sub /
<= Listener_open_icon $mol_icon_external
Тут мы стёрли из адреса номер слайда, чтобы ведомое окно брало его из локального хранилища, а не из адреса. Это обеспечит синхронизацию окон друг с другом. А так же указали, что роль должна быть "слушатель". Внутрь ссылки вместо текста мы положили иконку.
Плагины
Плагины позволяют значительно расширить возможности компонента. Будем использовать их по максимуму.
Все использованные нами плагины можно разделить на 3 категории: клавиатурная навигация, управление жестами и управление голосом.
Клавиатурная навигация
Через $mol_nav легко реализовать клавиатурную навигацию как по вертикали так и по горизонтали. Всё, что для этого нужно — это предоставить плагину список ключей, по которым он будет переключать, и двустороннюю связь на текущее значение.
Для отслеживания пальцев есть плагин $mol_touch. С его помощью можно зумить, панорамировать и свайпать. Именно последняя возможность нас сейчас и интересует.
Есть два вида свайпов. Например, свайп влево или вправо из любой части экрана и свайп из-за правого или левого края экрана к центру. В представленном коде мы повесили свои обработчики на первый тип свайпов.
Голосовое управление
Для голосового управления служит плагин $mol_speech. Необходимо создавать по экземпляру плагина на каждый вариант действия.
Плагин принимает обработчик действия и набор патернов, при обнаружении которых, будет срабатывать событие. В патернах можно использовать захватывающие скобки, чтобы получить в обработчике соответствующие их содержимому слова.
Автоматическое переключение слайдов
По умолчанию, $mol_speech требует вежливого обращения. Например, нужно говорить не "спой", а "спой, пожалуйста". Можно переопределить свойство suffix, чтобы изменить или вообще убрать это кодовое слово.
Например, для реализации автоматического переключения слайдов, волшебные слова нам не нужны. А вот набор патернов мы будем генерировать динамически, основываясь на анализе содержимого текста спикера.
Запуск приложения
Имея компонент приложения, можно инстанцировать его вручную как любой обычный класс. Но мы воспользуемся автоматическим запуском через специальный атрибут mol_view_root.
В качестве значения атрибута указывается имя компонента. Отрендерить таким образом, разумеется, можно совершенно любой компонент.
Оффлайн
Чтобы добавить поддержку оффлайна, достаточно включить в бандл модуль mol/offline/install.
include /mol/offline/install
Он автоматически поднимает ServiceWorker, который кеширует все запросы. А в случае недоступности сети — выдаёт данные из кеша. Это не самая крутая реализация, но для простых случаев, когда не хочется заморачиваться, — подойдёт.
Режим печати
Ранее я говорил, что во время печати нужно рендерить все страницы, но вручную отслеживать onbeforeprint и onafterprint нет никакой необходимости, ведь у нас же реактивное программирование в полный рост, поэтому мы воспользуемся реактивным состоянием $mol_print, дающим нам реактивный флаг, к которому мы можем подвязать рендеринг.
Тут мы возвращаем лишь одну страницу, если флаг active не поднят. А иначе возвращаем ленивый массив, вычисляющий свою длину и элементы по заданным формулам.
Эпилог
В итоге у нас получилось современное веб приложение с кучей функциональности и весом всего в 35кб. Добавить manifest и будет полноценное Progressive Web Application. Разумеется, многие детали не были рассмотрены. Увидеть их можно в коде на ГитХабе. По всем вопросам пишите телеграмы в чате.
Буду рад, если вы попробуете сделать свою презентацию с помощью $hyoo_slides. И буду совсем счастлив, если поможете сделать его ещё лучше, чем есть сейчас. Спасибо за внимание!