Сайт Игоря Кононученко   Статьи

Движок презентации в браузере

7 апреля 2010

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

Предпосылки

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

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

Первой мыслью было не тратить время на реализацию реализованного и просто использовать движок Харисова. Я попробовал, но мне не понравилось на чужой кухне. Объясню. Не уверен, создавал ли Харисов у себя каждый слайд в отдельном штмл-файле («разделяй и властвуй»), но судя по всему — нет. Не, ну сверстано там все ок — просто, логично, семантично. А вот джаваскриптовая реализация оказалась сложной — и кодом, и системой рендера слайдов. Работает все так, начальная разметка парсится джаваскриптом, а потом рендерится в то, что мы листаем в браузере. Как по мне, подобный перерендер — излишество.

Я понял, что хочу сделать все проще. Во-первых хранить все слайды в отдельных файлах, а во-вторых все преобразования возложить на серверный шаблонизатор (jinja2).

Реализация на сервере

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

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

(оценили понятность джинджа-шаблона?)

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
{% extends "base.html" %}
{% set SLIDE_WIDTH = 1024+2 %}
{%block head%}
<script type="text/javascript" src="{{ MEDIA_URL }}js/jquery.mousewheel.js"></script>
<script type="text/javascript" src="{{ MEDIA_URL }}js/jTweener.js"></script>
<script type="text/javascript" src="{{ MEDIA_URL }}js/urlparser.js"></script>
<script type="text/javascript" src="{{ MEDIA_URL }}js/presentation.js"></script>
<link rel="stylesheet" media="all" href="{{ MEDIA_URL }}css/talks/reset.css" />
<link rel="stylesheet" media="all" href="{{ MEDIA_URL }}css/talks/layout.css" />
<link rel="stylesheet" media="all" href="{{ MEDIA_URL }}css/talks/typo.css" />
<link rel="stylesheet" media="all" href="{{ MEDIA_URL }}css/talks/last-slide.css" />
<link rel="stylesheet" media="all" href="{{ MEDIA_URL }}css/talks/first-slide.css" />
<script>
	$(document).ready(function() {
		window.SLIDE_WIDTH = {{SLIDE_WIDTH}};
		new Presentation();
});
	
</script>
{% endblock %}
{% block body%}

{% macro slide(name) -%}

{% include("/talks/"+item.name+"/slides/"+name+".html")%}
{%- endmacro %}
<div class="l-presentation">
<div id="rightBtn" class="nav-btn" style="right:-80px"></div>
<div id="leftBtn" class="nav-btn" style="left:-80px"></div>
	<div class="l-wrap">
	
		<ol class="slides" id="slides" style="width:{{SLIDE_WIDTH*slides|count+200}}px">
			{%typo ""%}
				{% for name in slides%}
					{{ slide(name) }}
				{%endfor%}
			{%endtypo%}
		</ol>
	</div>
</div>
{%endblock%}

Для полноты картины приведу код базового слайда:

1
2
3
4
5
6
<li class="{%block attr%}{%endblock%} ps">
	<div class="l-slide {%block inner_class%}{%endblock%}" {%block inner_attr%}{%endblock%}>
		{%block content%}{%endblock%}
	</div>
</li>
</p>

Структура страницы

Система такая. Снаружи находится контейнер, который задает размер и является окошком для слайдов:

1
2
3
4
5
6
7
8
.l-wrap {
	height:768px;
	left:0;
	overflow:hidden;
	position:absolute;
	top:0;
	width:1024px;
}
Внутри него находится контейнер со слайдами, у которого относительная позиция (чтоб не промахнуться можете полазить там Фаербагом). В зависимости от номера слайда, я меняю горизонтальную координату.

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

Джаваскрипт

Контейнер со слайдами я перемещал подобным образом:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
animateTo:function(num)
{
	this.currentSlide = num;
	var left = -num*SLIDE_WIDTH;
	document.location.href = this.url+"#"+(num+1);
	
	
	jTweener.addTween(this.el[0], 
					  {left: left,  
					   time: 1, 
					   transition: 'easeoutcubic',
					   onComplete: function(){ this.watchUrl = true; }.bind(this)
					   });
	this.updateBtns();
}

Для анимации использовал Чикуеновский jTweener. Не скажу, что я остался доволен результатом. Все-таки при анимации больших дом-структур происходят лаги. Хочу попробовать цсс-реализацию, но пока руки не дошли.

Помимо этого использовал jQuery, classy.js (позволяет удобно создавать классы), jquery.mousewheel (биндит колесо мышки) и jquery.shortcuts (шарткат-либ великое множество, либа Степана Резникова первой попалась на глаза).

Моя реализация

Тут джс-код, который управляет презентацией.

Что дальше?

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

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

Bind-функция для jQuery
Про создание этого сайта
Ctrl
SmartInterval — класс для эффективной обработки повторяющихся событий
Перетаскивание файлов в браузер (Drag and Drop, XMLHttpRequest)