LPgenerator — профессиональная Landing Page платформа для увеличения продаж вашего бизнеса

  • Более 500 шаблонов в галерее
  • Инструменты оптимизации конверсии
  • Статистика и сквозная аналитика
  • CRM для работы с заявками и телефония
  • Визуальный редактор с расширенным функционалом
  • Быстрая техническая поддержка
  • Множество интеграций
  • Окупаемость инструмента — от 7 дней

Создаем анимированный календарь погоды при помощи CSS Clip и эффекта “наложения” (overlay)

Анимированный календарь

Сегодня мы будем изучать практическую сторону свойства CSS Clip и rect() функции. Мы создадим аккуратный и простой эффект для показа дополнительного скрытого контента и возможности полноэкранного наложения.

В этой статье пойдет речь о том, как использовать свойство CSS Clip, чтобы сделать плавный переход при нажатии на элемент окна. Идея состоит в том, чтобы показать какие-то наложения, как будто они на самом деле находятся под соответствующим элементом. Нажатие по элементу, создаст эффект “выреза”, открывая еще один слой, который будет расширяться. Звучит мудрено – но на самом деле стоит посмотреть Демо и все сразу станет понятно.

 Для начала нужно создать список предметов, которые будут выглядеть как боксы в стиле популярного METRO:

Бокс в стиле Метро

Каждый из квадратов будет содержать элемент (наложение), который будет иметь фиксированное (fixed) положение. Этот элемент будет располагаться на всю страницу, но мы этого не увидим, потому что прозрачность будет установлена на 0. При клике по коробке, мы будет использовать clip: rect(), чтобы обрезать соответствующей части внутреннего фиксированного элемента. Затем мы анимируем клип, чтобы показать всю ширину и высоту наложения, которая является нашей полной областью просмотра:

При нажатии кнопки “закрыть”, произайдет обратный эффект и наложение (overlay) минимизируется до размера элемента списка и затем исчезнет.
Итак, давайте начнем с HTML.

Разметка HTML

Для квадратов мы будем использовать неупорядоченный список. У каждого элемента списка будет класс “значок” (icon) и дополнительный “span” класс, который будет контролировать ширину коробки. Внутри мы добавим немного текста и раздел наложения. Наложение будет содержать структуру, которая будет иметь колонки. Так как мы выбрали фиктивное приложение погоды, как нашу тему, то тогда будем показывать прогноз погоды на ближайшие семь дней. У каждого из “дней” будет span который, мы будем использовать для будних дней, значка погоды и температуры:

<ul class="rb-grid clearfix">
<ul class="rb-grid clearfix">
<li class="icon-clima-1 rb-span-2">
<h3>Москва</h3>
<span class="rb-temp">21°C</span>
<div class="rb-overlay"><span class="rb-close">закрыть</span>
<div class="rb-week">
<div><span class="rb-city">Москва</span>21°C</div>
<div>Понедельник 19°C</div>
<div>Вторник 19°C</div>
<div>Среда 18°C</div>
<div>Четверг 17°C</div>
<div>Пятница 19°C</div>
<div>Суббота 22°C</div>
<div>Воскресение 18°C</div>
</div>
</div></li>
<li class="icon-clima-2">
<h3>Нью-Йорк</h3>
<span class="rb-temp">11°C</span>
<div class="rb-overlay"><!-- ... --></div></li>
<li><!-- ... --></li>
</ul>
</ul>
&nbsp;
<ul class="rb-grid clearfix">
<ul class="rb-grid clearfix"><!-- ... --></ul>
</ul>

CSS

* Обратите внимание, что здесь CSS не содержит никаких префиксов, но вы сможете найти их в файлах демо.

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

.rb-grid {
list-style: none;
text-align: center;
margin: 0 auto;
}

У элементов списка будет динамическая ширина и мы дадим им высоту 15em. Они будут сдвигаться влево:

.rb-grid li {
width: 24%;
height: 15em;
margin: 0.5%;
background: #8CC7DF;
color: #fff;
display: block;
float: left;
padding: 1.6em;
cursor: pointer;
position: relative;
}

У нас будет три различных ширины для элементов сетки, “по умолчанию” с 24%, а затем следующие два:

.rb-grid li.rb-span-2 {
width: 49%;
}

.rb-grid li.rb-span-4 {
width: 99%;
}

Давайте также стилизуем “Название города”:

.rb-grid li h3 {
font-size: 2.6em;
font-weight: 100;
}

Мы прилагаем CSS-файл для шрифта значков, который мы используем в нашем примере. Это Climacons Шрифт от Адама Виткрофта. Вы можете проверить climacons.css, чтобы увидеть, какие значки мы использовали. В основном мы используем класс значка (icon), чтобы добавить иконку с псевдо элементом. В нашей сетке мы хотим, чтобы иконки были расположены абсолютно в правом нижнем углу, и появлялись немного обрезаными:

.rb-grid li[class^="icon-"]:before,
.rb-grid li[class*=" icon-"]:before {
font-size: 10em;
position: absolute;
display: block;
width: 100%;
height: 100%;
top: 0;
left: 0;
line-height: 3;
opacity: 0.4;
text-align: right;
pointer-events: none;
}

Температура будет полупрозрачной и мы добавим переход (transition) для её прозрачности:

 .rb-temp {
display: block;
font-size: 2em;
opacity: 0.5;
transition: all 0.3s ease-in-out;
}

При наведении курсора на элемент списка, мы просто будем увеличивать прозрачность:

.rb-grid li:hover .rb-temp {
opacity: 1;
}

Теперь давайте взглянем на важный раздел наложения. Нашим окончательным результатом должно быть полноэкранное наложение. Поэтому, мы установим его ширину и высоту на 100% и дадим ему фиксированную позицию. Мы сделали так, что оно появляется поверх всего и у нас получается перекрытие и огромные накладки повсюду, поэтому мы должны сначала установить Z-Index в -1. Это позволит поставить их за содержанием страницы. Установка непрозрачности до 0 сделает их невидимыми:

.rb-overlay {
opacity: 0;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
transition: all 0.4s ease;
z-index: -1;
pointer-events: none;
cursor: default;
}

Это начальное состояние накладок. Как только мы нажимаем на элемент списка, мы установим правильное rect() значения для clip свойства и расширим наложения, анимированием rect() значения.
Но давайте сначала посмотрим на остальную часть стиля.

У каждого наложения будет небольшая кнопка “закрыть”, которая будет расположена в правом верхнем углу:

.rb-close {
position: absolute;
top: 0.4em;
right: 0.4em;
width: 2em;
height: 2em;
text-indent: -9000px;
cursor: pointer;
z-index: 1000;
}

.rb-close:before {
content: 'x';
font-weight: 100;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
font-size: 3em;
line-height: 0.6;
text-align: center;
text-indent: 0px;
}

Обертка для колонок будет иметь класс “rb-week” (хотя мы также включаем “текущую погоду” в нее). Нам нужно установить её на 100% ширину и высоту, так чтобы мы смогли определить правильную высоту и ширину для её child элементов:

.rb-week {
width: 100%;
height: 100%;
}

“Колонки” будут иметь ширину 10% (за исключением первой, которая будет 30% в ширину), и они будут двигаться влево:

.rb-week > div {
width: 10%;
height: 100%;
float: left;
position: relative;
padding: 3% 0;
}

.rb-week > div:first-child {
width: 30%;
}

В общей сложности у нас есть восемь колонок, 7мь раз по 10%, в результате 70%, поэтому у нас остается 30% для первой колонки.

У каждого из спэнов будет высота в 30% и с небольшим пэддингом в 5%:

.rb-week span {
padding: 5% 0;
font-size: 2em;
font-weight: 100;
display: block;
margin: auto 0;
height: 30%;
width: 100%;
line-height: 0.8;
}

У спэна для названия городов будет особый стиль, с более тонким весом шрифтом:

.rb-week span.rb-city {
font-weight: 700;
padding: 1% 10%;
font-size: 1em;
line-height: 1.2;
}

У иконок будет повышенный размер шрифта и нам нужно будет сбросить вес шрифта, потому что мы изменили его в другом правиле:

.rb-week [class^="icon-"]:before {
font-size: 2.5em;
font-weight: normal;
}

Значок в “текущем столбце погоды” будет почти прозрачным:

.rb-week > div:first-child [class^="icon-"] {
opacity: 0.1;
}

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

/* Цвета */

/* Сетка */
.rb-grid li:nth-child(1) { background: #3399CC; }
.rb-grid li:nth-child(2) { background: #33CCCC; }
.rb-grid li:nth-child(3) { background: #996699; }
.rb-grid li:nth-child(4) { background: #C24747; }
.rb-grid li:nth-child(5) { background: #e2674a; }
.rb-grid li:nth-child(6) { background: #FFCC66; }
.rb-grid li:nth-child(7) { background: #99CC99; }
.rb-grid li:nth-child(8) { background: #669999; }
.rb-grid li:nth-child(9) { background: #CC6699; }
.rb-grid li:nth-child(10) { background: #339966; }
.rb-grid li:nth-child(11) { background: #666699; }

И для каждого наложения у нас есть восемь колонок:

/* Колонки наложения */
.rb-grid li:nth-child(1) .rb-week > div:nth-child(1) { background: #3399CC; }
.rb-grid li:nth-child(1) .rb-week > div:nth-child(2) { background: #2D87B4; }
.rb-grid li:nth-child(1) .rb-week > div:nth-child(3) { background: #297AA3; }
.rb-grid li:nth-child(1) .rb-week > div:nth-child(4) { background: #256E93; }
.rb-grid li:nth-child(1) .rb-week > div:nth-child(5) { background: #216283; }
.rb-grid li:nth-child(1) .rb-week > div:nth-child(6) { background: #1D5672; }
.rb-grid li:nth-child(1) .rb-week > div:nth-child(7) { background: #184962; }
.rb-grid li:nth-child(1) .rb-week > div:nth-child(8) { background: #143D52; }

.rb-grid li:nth-child(2) .rb-week > div:nth-child(1) { background: #33CCCC; }
.rb-grid li:nth-child(2) .rb-week > div:nth-child(2) { background: #2DB4B4; }
.rb-grid li:nth-child(2) .rb-week > div:nth-child(3) { background: #29A3A3; }
.rb-grid li:nth-child(2) .rb-week > div:nth-child(4) { background: #259393; }
.rb-grid li:nth-child(2) .rb-week > div:nth-child(5) { background: #218383; }
.rb-grid li:nth-child(2) .rb-week > div:nth-child(6) { background: #1D7272; }
.rb-grid li:nth-child(2) .rb-week > div:nth-child(7) { background: #186262; }
.rb-grid li:nth-child(2) .rb-week > div:nth-child(8) { background: #145252; }

/* ... */

… И так далее для каждой из 11 коробок.

И последнее, давайте позаботимся о маленьких экранах с медиа запросами. Когда пространство ограничено, мы больше не захотим показывать наши квадраты в сетке:

@media screen and (max-width: 63.125em) {

.rb-grid li,
.rb-grid li.rb-span-2,
.rb-grid li.rb-span-4 {
width: 100%;
height: 10em;
text-align: left;
}

.rb-grid li[class^="icon-"]:before,
.rb-grid li[class*=" icon-"]:before {
font-size: 6em;
left: auto;
right: 0;
line-height: 2.5;
}

.rb-grid li > div {
text-align: center;
}
}

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

Анимированный календарь

JavaScript

Начнем с кэширования элементов и инициализирования нескольких переменных:

var $items = $( '#rb-grid > li' ),
transEndEventNames = {
'WebkitTransition' : 'webkitTransitionEnd',
'MozTransition' : 'transitionend',
'OTransition' : 'oTransitionEnd',
'msTransition' : 'MSTransitionEnd',
'transition' : 'transitionend'
},

// транзиции и название event
transEndEventName = transEndEventNames[ Modernizr.prefixed( 'transition' ) ],

// window и body элементы
$window = $( window ),
$body = $( 'BODY' ),

// поддержка транзиций
supportTransitions = Modernizr.csstransitions,

// индекс текущего элемента
current = -1,

// высота и ширина окна
winsize = getWindowSize();

Во-первых, мы применим далее FitText плагин в JQuery колонку текстовых элементов в наложении, в целях сокращения текста на небольших экранах.
Нам также нужно получить текущие значения шириныи высоты окна, поэтому мы будем привязывать событие изменения размера к окну элемента.

function init( options ) {
// apply fittext plugin
$items.find( 'div.rb-week > div span' ).fitText( 0.3 ).end().find( 'span.rb-city' ).fitText( 0.5 );
initEvents();
}

При нажатие на один пункт, будут применяться два перехода к соответствующему элементу наложения. Первый из них будет применять clip, который будет обрезать его в том же самом месте что текущий элемент списка. Мы также покажем наложения за счет увеличения его прозрачности. Второй переход, позаботится об анимации клипа, так что наложение будет “расширяться”, чтобы соответствовать ширине и высоте окна. Для первого перехода, необходимые значения соответствуют позиции и размеру элемента. Мы получаем это вызвав “getItemLayoutProp” функцию. Для второго, нам просто нужна ширина и высота окна, чтобы определить правильные значения clip.

Ещё две вещи также должны быть рассмотрены для того, чтобы все правильно работало. Во-первых, мы “отображаем и скрываем” прокрутки страницы между этапами, потому что мы не хотим продолжать скроллинг, как только конечный этап (расширенное наложения) достигнут. Во-вторых, мы устанавливаем наложение Z-индекса на высокое значение, так чтобы он всегда оставался “вверху”, и указатель событий на auto для того, чтобы контент наложения был интерактивным.

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

function initEvents() {

$items.each( function() {

var $item = $( this ),
$close = $item.find( 'span.rb-close' ),
$overlay = $item.children( 'div.rb-overlay' );

$item.on( 'click', function() {

if( $item.data( 'isExpanded' ) ) {
return false;
}
$item.data( 'isExpanded', true );
// save current index of the item
current = $item.index();

var layoutProp = getItemLayoutProp( $item ),
clipPropFirst = 'rect(' + layoutProp.top + 'px ' + ( layoutProp.left + layoutProp.width ) + 'px ' + ( layoutProp.top + layoutProp.height ) + 'px ' + layoutProp.left + 'px)',
clipPropLast = 'rect(0px ' + winsize.width + 'px ' + winsize.height + 'px 0px)';

$overlay.css( {
clip : supportTransitions ? clipPropFirst : clipPropLast,
opacity : 1,
zIndex: 9999,
pointerEvents : 'auto'
} );

if( supportTransitions ) {
$overlay.on( transEndEventName, function() {

$overlay.off( transEndEventName );

setTimeout( function() {
$overlay.css( 'clip', clipPropLast ).on( transEndEventName, function() {
$overlay.off( transEndEventName );
$body.css( 'overflow-y', 'hidden' );
} );
}, 25 );

} );
}
else {
$body.css( 'overflow-y', 'hidden' );
}

} );

...

} );

...

}

function getItemLayoutProp( $item ) {

var scrollT = $window.scrollTop(),
scrollL = $window.scrollLeft(),
itemOffset = $item.offset();

return {
left : itemOffset.left - scrollL,
top : itemOffset.top - scrollT,
width : $item.outerWidth(),
height : $item.outerHeight()
};

}

Что касается клика для закрытых элементов, то мы просто возвращаем (revert) того, что было сделано раньше:

function initEvents() {

$items.each( function() {

...

$close.on( 'click', function() {

$body.css( 'overflow-y', 'auto' );

var layoutProp = getItemLayoutProp( $item ),
clipPropFirst = 'rect(' + layoutProp.top + 'px ' + ( layoutProp.left + layoutProp.width ) + 'px ' + ( layoutProp.top + layoutProp.height ) + 'px ' + layoutProp.left + 'px)',
clipPropLast = 'auto';

// reset current
current = -1;

$overlay.css( {
clip : supportTransitions ? clipPropFirst : clipPropLast,
opacity : supportTransitions ? 1 : 0,
pointerEvents : 'none'
} );

if( supportTransitions ) {
$overlay.on( transEndEventName, function() {

$overlay.off( transEndEventName );
setTimeout( function() {
$overlay.css( 'opacity', 0 ).on( transEndEventName, function() {
$overlay.off( transEndEventName ).css( { clip : clipPropLast, zIndex: -1 } );
$item.data( 'isExpanded', false );
} );
}, 25 );

} );
}
else {
$overlay.css( 'z-index', -1 );
$item.data( 'isExpanded', false );
}

return false;

} );

} );

...

}

Вот и всё! Надеемся, вам понравился урок и вы найдете ему применение в своих проектах! Также, напишите в комментариях, если вам понравилась наша новая система рейтинга уровня сложности для уроков и качество подачи материала.

Высоких вам конверсий! 

9 марта 2016

LPgenerator — профессиональная Landing Page платформа для увеличения продаж вашего бизнеса

  • Более 500 шаблонов в галерее
  • Инструменты оптимизации конверсии
  • Статистика и сквозная аналитика
  • CRM для работы с заявками и телефония
  • Визуальный редактор с расширенным функционалом
  • Быстрая техническая поддержка
  • Множество интеграций
  • Окупаемость инструмента — от 7 дней
blog comments powered by Disqus
copyright © 2011–2017 by LPgenerator LLC. Все права защищены
Запрещено любое копирование материалов ресурса без письменного согласия владельца — ООО "ЛПгенератор".