Простой шаблон, который позволяет отображать события на временной шкале, а также организовывать их группами (дни недели, конференц-залы и т. Д.).
Мы часто сталкивались с этим веб-компонентом: когда мы проверяем график конференции или расписание занятий в нашем спортзале. С точки зрения веб-дизайнера, удобно использовать простой, отзывчивый шаблон, если вам нужно создать таблицу расписания. Итак, мы построили один!
Создание структуры
Структура HTML состоит из трех различных элементов: div.timeline
для временной шкалы событий (09:00, 09:30, ..), div.events
, которая включает список событий и div.event-modal
для модального окна Используется для предоставления более подробной информации о выбранном событии.
<div class="cd-schedule">
<div class="timeline">
<ul>
<li><span>09:00</span></li>
<li><span>09:30</span></li>
<!-- additional elements here -->
</ul>
</div>
<div class="events">
<ul>
<li class="events-group">
<div class="top-info"><span>Monday</span></div>
<ul>
<li class="single-event" data-start="09:30" data-end="10:30" data-content="event-abs-circuit" data-event="event-1">
<a href="#0">
<em class="event-name">Abs Circuit</em>
</a>
</li>
<!-- other events here -->
</ul>
</li>
<li class="events-group">
<div class="top-info"><span>Tuesday</span></div>
<ul>
<!-- events here -->
</ul>
</li>
<!-- additional li.events-group here -->
</ul>
</div>
<div class="event-modal">
<header class="header">
<div class="content">
<span class="event-date"></span>
<h3 class="event-name"></h3>
</div>
<div class="header-bg"></div>
</header>
<div class="body">
<div class="event-info"></div>
<div class="body-bg"></div>
</div>
<a href="#0" class="close">Close</a>
</div>
</div>
Добавление стиля
На небольших устройствах (ширина окна меньше 800 пикселей) все события внутри .events-group
выстраиваются горизонтально: мы устанавливаем display: flex
в элемент .events-group > ul
и overflow-x: scroll
, чтобы события прокручивались.
.cd-schedule .events .events-group > ul {
position: relative;
padding: 0 5%;
/* force its children to stay on one line */
display: flex;
overflow-x: scroll;
-webkit-overflow-scrolling: touch;
}
.cd-schedule .events .single-event {
/* force them to stay on one line */
flex-shrink: 0;
float: left;
height: 150px;
width: 70%;
max-width: 300px;
}
Что касается мода .event-modal
, он имеет фиксированное положение и перемещается вправо вне окна просмотра. Когда пользователь выбирает событие, класс .modal-is-open
используется для перевода .event-modal
обратно в область просмотра.
.cd-schedule .event-modal {
position: fixed;
z-index: 3;
top: 0;
right: 0;
height: 100%;
width: 100%;
visibility: hidden;
transform: translateX(100%);
transition: transform .4s, visibility .4s;
}
.cd-schedule.modal-is-open .event-modal {
/* .modal-is-open class is added as soon as an event is selected */
transform: translateX(0);
visibility: visible;
}
На больших устройствах все события находятся в абсолютном положении и помещаются в расписание: верхнее положение и высота каждого события оцениваются с использованием атрибутов data-start
и data-end
самого события и задаются с использованием JavaScript (больше в Раздел обработки событий).
@media only screen and (min-width: 800px) {
.cd-schedule .events {
float: left;
width: 100%;
}
.cd-schedule .events .events-group {
width: 20%;
float: left;
}
.cd-schedule .events .single-event {
position: absolute;
z-index: 3;
/* top position and height will be set using js */
width: calc(100% + 2px);
left: -1px;
}
}
Что касается .event-modal
, анимация открытия / закрытия создается с использованием jQuery в сочетании с переходами и преобразованиями CSS (подробнее в разделе Обработка событий).
Обработка событий
Чтобы реализовать это расписание событий, мы создали объект SchedulePlan
и использовали функции scheduleReset()
и initEvents()
, чтобы запустить расписание и присоединить обработчики событий к соответствующим элементам.
function SchedulePlan( element ) {
this.element = element;
this.timeline = this.element.find('.timeline');
//...
this.eventsWrapper = this.element.find('.events');
this.eventsGroup = this.eventsWrapper.find('.events-group');
this.singleEvents = this.eventsGroup.find('.single-event');
//..
this.scheduleReset();
this.initEvents();
}
На больших устройствах метод scheduleReset()
позволяет размещать события внутри расписания и устанавливать их высоту. Например, для оценки высоты мы вычисляем продолжительность события (конец-минус данных), делим его на «eventUnit» (в нашем случае это 30 минут), а затем умножаем его на высоту «временной шкалы» Единица (в нашем случае 50 пикселей).
var self = this;
this.singleEvents.each(function(){
//place each event in the grid -> need to set top position and height
var start = getScheduleTimestamp($(this).attr('data-start')), //getScheduleTimestamp converts hh:mm to timestamp
duration = getScheduleTimestamp($(this).attr('data-end')) - start;
var eventTop = self.eventUnitHeight*(start - self.timelineStart)/self.timelineUnitDuration,
eventHeight = self.eventUnitHeight*duration/self.timelineUnitDuration;
$(this).css({
top: (eventTop -1) +'px',
height: (eventHeight+1)+'px'
});
});
Когда пользователь выбирает событие, функция load()
jQuery используется для загрузки содержимого только что выбранного события (data-content
используется для определения загружаемого содержимого файла).
В дополнение к этому, на больших устройствах .event-modal
анимируется, чтобы показать содержимое события. Во-первых, .event-modal
помещается поверх выбранного события, а его высота и ширина изменяются так, чтобы они были равны единицам выбранного события; То элементы .header-bg
и .body-bg
масштабируются для создания анимации морфинга; В конце этой анимации обнаруживается модальный контент.
SchedulePlan.prototype.openModal = function(event) {
var self = this;
var mq = self.mq();
this.animating = true;
//update event name and time
this.modalHeader.find('.event-name').text(event.find('.event-name').text());
this.modalHeader.find('.event-date').text(event.find('.event-date').text());
this.modal.attr('data-event', event.parent().attr('data-event'));
//update event content
this.modalBody.find('.event-info').load(event.parent().attr('data-content')+'.html .event-info > *', function(data){
//once the event content has been loaded
self.element.addClass('content-loaded');
});
this.element.addClass('modal-is-open');
if( mq == 'mobile' ) {
self.modal.one(transitionEnd, function(){
self.modal.off(transitionEnd);
self.animating = false;
});
} else {
//change modal height/width and translate it
self.modal.css({
top: eventTop+'px', //this is the selected event top position
left: eventLeft+'px', //this is the selected event left position
height: modalHeight+'px', //this is the modal final height
width: modalWidth+'px', //this is the modal final width
});
transformElement(self.modal, 'translateY('+modalTranslateY+'px) translateX('+modalTranslateX+'px)');
//set modalHeader width
self.modalHeader.css({
width: eventWidth+'px', //this is the selected event width
});
//set modalBody left margin
self.modalBody.css({
marginLeft: eventWidth+'px',
});
//change modalBodyBg height/width and scale it
self.modalBodyBg.css({
height: eventHeight+'px',
width: '1px',
});
transformElement(self.modalBodyBg, 'scaleY('+HeaderBgScaleY+') scaleX('+BodyBgScaleX+')');
//change modal modalHeaderBg height/width and scale it
self.modalHeaderBg.css({
height: eventHeight+'px',
width: eventWidth+'px',
});
transformElement(self.modalHeaderBg, 'scaleY('+HeaderBgScaleY+')');
self.modalHeaderBg.one(transitionEnd, function(){
//wait for the end of the modalHeaderBg transformation and show the modal content
self.modalHeaderBg.off(transitionEnd);
self.animating = false;
self.element.addClass('animation-completed');
});
}
};
Примечание:
Мы выполнили простую функцию load()
, чтобы загрузить новый html-контент, но вы можете заменить его вызовом $.ajax
, чтобы обрабатывать ошибки, запрос sendSend и т. Д. В соответствии с вашим проектом.