29дек
1961
1
0

Эффект для предзагрузки страницы.

Сегодня мы хотим показать вам, как создать очень простую страницу с эффектом предварительной загрузки используя CSS-анимации, SVG и JavaScript. Для веб-сайтов, где важно, чтобы загрузка всего или части контента отображались на экране только после полной её загрузки - страница предзагрузки может быть отличным решением в подобной ситуации. К тому же это может служить поводом для создания чего-то оригинального и интересного, чтобы хоть как-то сделать ожидание загрузки контента менее скучным для посетителя. 

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

 

В этом уроке мы повторим эффект увиденный на Fontface Ninja с некоторыми коррективами, а также создадим второй вариант демо, со слегка другими эффектами. Для логотипа и круглого элемента прогресса загрузки мы будем использовать SVG графику, которую вставим инлайном в htmlразметку, так что мы их сможем стилизовать через CSS. Создадим также небольшой скрипт для хода анимации в SVG элементе загрузки.

 

Использовать мы будем CSS-анимации и CSS 3D-преобразования, так что работать всё это будет в актуальной/современной версии браузера.

Итак, давайте начнём!

 

Разметка

Давайте обернём header и основное содержимое страницы в контейнер. Делаем это мы для того, чтобы контролировать всё, что происходит в первоначальном виде страницы. Таким образом, мы будем использовать основной контейнер, как элемент управления. Присвоим ему класса и id ip-container

Первоначальный вид состоит из header’а, содержащего логотип и элемент процесса загрузки. Они оба в SVG, но наш логотип будет немного сложнее, чем кружок грузки, поэтому его код в теге path, что в примере ниже мы уберём, так как он очень длинный, но в исходниках он будет. Как вы можете видеть, мы задали некоторые атрибуты для SVG, такие как: ширина (width), высота (height), Viewbox и preserveAspectRatio. Значением для preserveAspectRatio является xMidYMin meet, которое определяет равномерное масштабирование в пределах своего контейнера, а его центр по оси X выравнивается по верхнему краю. Для того чтобы логотип был доступен, мы добавляем название к атрибуту aria-labelledby.

Основное содержимое страницы имеет класс ip-main, и в дальнейшем мы будем применять анимацию к его дочерним элементам:

<div id="ip-container" class="ip-container">

<!-- initial header -->
<header class="ip-header">

<h1 class="ip-logo">
<svg class="ip-inner" width="100%" height="100%" viewBox="0 0 300 160" preserveAspectRatio="xMidYMin meet" aria-labelledby="logo_title">
<title id="logo_title">Delightful Demonstrations by Codrops</title>
<path d="...our super-long path..." />
</svg>
</h1>

<div class="ip-loader">
<svg class="ip-inner" width="60px" height="60px" viewBox="0 0 80 80">
<path class="ip-loader-circlebg" d="M40,10C57.351,10,71,23.649,71,40.5S57.351,71,40.5,71 S10,57.351,10,40.5S23.649,10,40.5,10z"/>
<path id="ip-loader-circle" class="ip-loader-circle" d="M40,10C57.351,10,71,23.649,71,40.5S57.351,71,40.5,71 S10,57.351,10,40.5S23.649,10,40.5,10z"/>
</svg>
</div>

</header>

<!-- main content -->
<div class="ip-main">

<h2>Make yourself at home.</h2>

<div class="browser clearfix">
<div class="box">
<span class="icon-bell"></span>
<p>...</p>
</div>
<div class="box">
<span class="icon-heart"></span>
<p>...</p>
</div>
<div class="box">
<span class="icon-cog"></span>
<p>...</p>
</div>
</div>

</div>
</div><!-- /container -->

Идём дальше и переходим к стилям.

 

CSS

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

Для начала мы подключим некоторые шрифты, которые понадобятся нам для текста и иконок. Иконки используем из демо-набора Feather icon set и шрифтовых иконок Icomoon App. Макет, имитирующий текст Blokk – это действительно отличная вещь при создании каркасов и макетов.

@font-face {
    font-weight: normal;
    font-style: normal;
    font-family: 'Blokk';
    src: url('../fonts/blokk/BLOKKRegular.eot');
    src: url('../fonts/blokk/BLOKKRegular.eot?#iefix') format('embedded-opentype'),
         url('../fonts/blokk/BLOKKRegular.woff') format('woff'),
         url('../fonts/blokk/BLOKKRegular.svg#BLOKKRegular') format('svg');
}
 
@font-face {
    font-weight: normal;
    font-style: normal;
    font-family: 'feather';
    src:url('../fonts/feather/feather.eot?-9jv4cc');
    src:url('../fonts/feather/feather.eot?#iefix-9jv4cc') format('embedded-opentype'),
        url('../fonts/feather/feather.woff?-9jv4cc') format('woff'),
        url('../fonts/feather/feather.ttf?-9jv4cc') format('truetype'),
        url('../fonts/feather/feather.svg?-9jv4cc#feather') format('svg');
}

Мы хотим, чтобы header заполнил всё окно просмотра на начальном этапе, так что давайте зададим ему 100% по ширине и высоте, и установим фиксированное позиционирование:

.ip-header {
    position: fixed;
    top: 0;
    z-index: 100;
    min-height: 480px;
    width: 100%;
    height: 100%;
    background: #f1f1f1;
}

Обнулим отступы для тега h1:

.ip-header h1 {
    margin: 0;
}

И логотип, и прогресс загрузки будут иметь абсолютное позиционирование:

.ip-logo,
.ip-loader {
    position: absolute;
    left: 0;
    width: 100%;
    opacity: 0;
    cursor: default;
    pointer-events: none;
}

Вместо того чтобы просто взять логотип и расположить его в центре header’а, мы должны будем иметь в виду следующее: нам нужно чтобы сам SVGлоготип был адаптивным, и нам также нужно переместить его в начало основного содержимого страницы используя 3D-преобразования, когда загрузка завершится. Всё потому что мы не знаем размеров нашего логотипа, мы не знаем, сколько у нас на самом деле, но мы может работать с одной конкретной величиной: высота окна. Так что давайте просто установить логотип на 100% по высоте, и установим translate3d на 25%, так чтобы логотип расположился ровно посередине страницы:

.ip-logo {
    top: 0;
    height: 100%;
    transform: translate3d(0,25%,0);
}

Позиционируем элемент загрузки в нижнюю часть экрана:

.ip-loader {
    bottom: 20%;
}

SVG, которым мы присвоили класс ip-inner будут отображаться как блочный элемент. Отцентрируем их по горизонтали свойством margin, а именно значение autoих отцентрирует:

.ip-header .ip-inner {
    display: block;
    margin: 0 auto;
}

Также нужно учитывать то, что наш SVGлоготип должен быть не слишком большим и не слишком маленьким, поэтому зададим максимальную и минимальную ширину:

.ip-header .ip-logo svg {
    min-width: 320px;
    max-width: 480px;
    width: 25%;
}

Так как мы вставили SVG логотип инлайном, то мы можем непосредственно менять его цвет в css: 

.ip-header .ip-logo svg path {
    fill: #ef6e7e;
}

И то же самое нам доступно и для прогресса загрузки:

.ip-header .ip-loader svg path {
    fill: none;
    stroke-width: 6;
}

Первый <path> прогресса загрузки имеет серую заливку:

.ip-header .ip-loader svg path.ip-loader-circlebg {
    stroke: #ddd;
}

Второй <path> будет иметь прогресс перехода, который мы будем контролировать в JS. Но здесь, мы будем определять переход stroke-dashoffset:

.ip-header .ip-loader svg path.ip-loader-circle {
    transition: stroke-dashoffset 0.2s;
    stroke: #ef6e7e;
}

Cтили основного содержимого страницы завёрнуты в блок ip-main:

.ip-main {
    overflow: hidden;
    margin: 0 auto;
    padding: 160px 0 10em 0;
    max-width: 1100px;
    width: 90%;
}

Размер шрифта заголовка будет иметь единицы VW, который зависят и определяют размеры элемента-(ов) в нашем случае размер шрифта от размера окна браузера, то есть VWявляется адаптивной единицей:

.ip-main h2 {
    margin: 0;
    padding: 0.5em 0 1em;
    color: #be4856;
    text-align: center;
    font-size: 4.25em;
    font-size: 4vw;
    line-height: 1;
}

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

.browser {
    margin: 0 auto;
    padding-top: 8%;
    min-height: 400px;
    max-width: 1000px;
    width: 100%;
    border-radius: 8px;
    background: #fff url(../img/browser.png) no-repeat 50% 0;
    background-size: 100%;
    color: #d3d3d3;
}

И три бокса внутри нашего браузера:

.box {
    float: left;
    padding: 3.5em;
    width: 33.3%;
    font-size: 0.7em;
    line-height: 1.5;
}
 
.box p {
    font-family: 'Blokk', Arial, sans-serif;
}

Каждый из боксов будет иметь свой значок:

[class^="icon-"]::before, 
[class*=" icon-"]::before {
    display: block;
    margin-bottom: 0.5em;
    padding: 0.5em;
    border-radius: 5px;
    background: #dfdfdf;
    color: #fff;
    text-align: center;
    text-transform: none;
    font-weight: normal;
    font-style: normal;
    font-variant: normal;
    font-size: 5em;
    font-family: 'feather';
    line-height: 1;
    speak: none;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}
 
.icon-bell:before {
    content: "\e006";
}
 
.icon-cog:before {
    content: "\e023";
}
 
.icon-heart:before {
    content: "\e024";
}

Теперь нам необходимо определить анимации для них.

Первоначальной анимацией элементов header’a будут движения снизу вверх:

.loading .ip-logo,
.loading .ip-loader {
    opacity: 1;
    animation: animInitialHeader 1s cubic-bezier(0.7,0,0.3,1) both;
}
 
.loading .ip-loader {
    animation-delay: 0.2s;
}
 
@keyframes animInitialHeader {
    from { 
        opacity: 0; 
        transform: translate3d(0,800px,0); 
    }
}

Поэтому определяем анимацию только для from keyframes.

Функция cubic-bezier добавит хороший эффект плавности движения. Элемент загрузки должен иметь небольшую задержку (animation-delay), перед тем как он начнёт своё движение вверх.

 

Так же мы должны будем переключать "состояние", как только анимация прогресса закончится. Добавим класс loaded к контейнеру и применим следующую анимацию:

.loaded .ip-logo,
.loaded .ip-loader {
    opacity: 1;
}
 
.loaded .ip-logo {
    transform-origin: 50% 0;
    animation: animLoadedLogo 1s cubic-bezier(0.7,0,0.3,1) forwards;
}
 
@keyframes animLoadedLogo {
    to { 
        transform: translate3d(0,100%,0) translate3d(0,50px,0) scale3d(0.65,0.65,1); 
    }
}
 
.loaded .ip-logo svg path {
    transition: all 0.5s ease 0.3s;
    fill: #fff;
}
 
.loaded .ip-loader {
    animation: animLoadedLoader 0.5s cubic-bezier(0.7,0,0.3,1) forwards;
}
 
@keyframes animLoadedLoader {
    to { 
        opacity: 0; 
        transform: translate3d(0,-100%,0) scale3d(0.3,0.3,1); 
    }
}

Логотип движется на 100% вниз  (помните, что высота нашего логотипа составляет 100% от окна просмотра браузера - это заставит его двигаться на всю высоту). Цвет SVG изменится с переходом.

Элемент подгрузки перемещается вверх, масштабируется и исчезает.

 

Фиксированный header должен также двигаться вверх:

.loaded .ip-header {
    animation: animLoadedHeader 1s cubic-bezier(0.7,0,0.3,1) forwards;
}
 
@keyframes animLoadedHeader {
    to { transform: translate3d(0,-100%,0); }
}

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

/* Content animations */
.loaded .ip-main h2,
.loaded .ip-main .browser,
.loaded .ip-main .browser .box,
.loaded .codrops-demos {
    animation: animLoadedContent 1s cubic-bezier(0.7,0,0.3,1) both;
}
 
.loaded .ip-main .browser,
.loaded .ip-main .browser .box:first-child {
    animation-delay: 0.1s;
}
 
.loaded .ip-main .browser .box:nth-child(2) {
    animation-delay: 0.15s;
}
 
.loaded .ip-main .browser .box:nth-child(3) {
    animation-delay: 0.2s;
}
 
@keyframes animLoadedContent {
    from { 
        opacity: 0; 
        transform: translate3d(0,200px,0); 
    }
}

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

 

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

.layout-switch .ip-header {
    position: absolute;
}

Если JavaScript в браузере отключён, то нам желательно показать состояние после всех анимаций. Это можно сделать, установив header на относительное позиционирование и задать размер для логотипа:

.no-js .ip-header {
    position: relative;
    min-height: 0px;
}
 
.no-js .ip-header .ip-logo {
    margin-top: 20px;
    height: 180px;
    opacity: 1;
    transform: none;
}
 
.no-js .ip-header .ip-logo svg path {
    fill: #fff;
}

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

@media screen and (max-width: 45em) {
 
    .ip-main h2 {
        font-size: 2.25em;
        font-size: 10vw;
    }
 
    .box {
        width: 100%%;
    }
 
}

И это все со стилями.

 

JavaScript

JavaScriptсостоит из двух частей. Мы будем разделять функциональные нагрузки общих элементов прогресса от остальных. Давайте назовём этот сценарий pathLoader.js.  Нам необходимо, иметь возможность устанавливать stroke-dashoffset для того, чтобы анимировать заполнения прогресса. Первоначально stroke-dashoffset и stroke-dasharray установлены на длине пути (getTotalLength ()). Мы рисуем линию, задав смещение штриха на более низкое значение вплоть до нуля, где путь полностью нарисован. Это делается путём вызова функции setProgress с параметром value. Необязательный параметр callback может быть полезен, если мы хотим выполнить несколько сценариев, как только значение установлено и переход закончен.

function PathLoader( el ) {
    this.el = el;
    // clear stroke
    this.el.style.strokeDasharray = this.el.style.strokeDashoffset = this.el.getTotalLength();
}
 
PathLoader.prototype._draw = function( val ) {
    this.el.style.strokeDashoffset = this.el.getTotalLength() * ( 1 - val );
}
 
PathLoader.prototype.setProgress = function( val, callback ) {
    this._draw(val);
    if( callback && typeof callback === 'function' ) {
        // give it a time (ideally the same like the transition time) so that the last progress increment animation is still visible.
        setTimeout( callback, 200 );
    }
}
 
PathLoader.prototype.setProgressFn = function( fn ) {
    if( typeof fn === 'function' ) { fn( this ); }
}

Метод setProgressFn здесь используется для того чтобы определить возможный способ взаимодействия с загрузчиком. Например, для нашего демо мы никакой контент не подгружаем, но вместо этого мы имитируем анимацию загрузки, установив случайное значение между 0 и 1 на протяжении множества временных интервалов:

var simulationFn = function(instance) {
    var progress = 0,
        interval = setInterval( function() {
            progress = Math.min( progress + Math.random() * 0.1, 1 );
            instance.setProgress( progress );
            // reached the end
            if( progress === 1 ) {
                clearInterval( interval );
            }
        }, 100 );
};
 
var loader = new PathLoader([pathselector]);
loader.setProgressFn(simulationFn);

Далее, давайте создадим наш скрипт покоя в main.js. Сначала мы инициализируем и кэшируем некоторые переменные:

var support = { animations : Modernizr.cssanimations },
    container = document.getElementById( 'ip-container' ),
    header = container.querySelector( 'header.ip-header' ),
    loader = new PathLoader( document.getElementById( 'ip-loader-circle' ) ),
    animEndEventNames = { 'WebkitAnimation' : 'webkitAnimationEnd', 'OAnimation' : 'oAnimationEnd', 'msAnimation' : 'MSAnimationEnd', 'animation' : 'animationend' },
    // animation end event name
    animEndEventName = animEndEventNames[ Modernizr.prefixed( 'animation' ) ];

Начнём начальную анимацию (когда логотип, и элемент загрузки скользят вверх), добавив класс loading к главному контейнеру. После завершения анимации запускаем "фальшивую" загрузку - анимация заполнения процесса загрузки, как было объяснено выше. Обратите внимание, в то время как эти анимации воспроизводятся, мы не позволяем страницу скроллить.

function init() {
    var onEndInitialAnimation = function() {
        if( support.animations ) {
            this.removeEventListener( animEndEventName, onEndInitialAnimation );
        }
 
        startLoading();
    };
 
    // disable scrolling
    window.addEventListener( 'scroll', noscroll );
 
    // initial animation
    classie.add( container, 'loading' );
 
    if( support.animations ) {
        container.addEventListener( animEndEventName, onEndInitialAnimation );
    }
    else {
        onEndInitialAnimation();
    }
}
 
// no scroll
function noscroll() {
    window.scrollTo( 0, 0 );
}

Опять же, мы будем симулировать, что что-то загружается, передавая пользовательскую функцию в setProgressFn. После того, как анимация завершена, заменяем класс loading на класс loaded, который будет инициировать основные анимации для header’aи контента. После того как всё будет готово, мы добавим класс layout-switch к тегу body и позволим скролить страницу:

function startLoading() {
    // simulate loading something..
    var simulationFn = function(instance) {
        var progress = 0,
            interval = setInterval( function() {
                progress = Math.min( progress + Math.random() * 0.1, 1 );
 
                instance.setProgress( progress );
 
                // reached the end
                if( progress === 1 ) {
                    classie.remove( container, 'loading' );
                    classie.add( container, 'loaded' );
                    clearInterval( interval );
 
                    var onEndHeaderAnimation = function(ev) {
                        if( support.animations ) {
                            if( ev.target !== header ) return;
                            this.removeEventListener( animEndEventName, onEndHeaderAnimation );
                        }
 
                        classie.add( document.body, 'layout-switch' );
                        window.removeEventListener( 'scroll', noscroll );
                    };
 
                    if( support.animations ) {
                        header.addEventListener( animEndEventName, onEndHeaderAnimation );
                    }
                    else {
                        onEndHeaderAnimation();
                    }
                }
            }, 80 );
    };
 
    loader.setProgressFn( simulationFn );
}

И это все, все готово!

 

Перевод: Art

Оригинал: tympanus.net

Демо тут, исходники тут.

Рейтинг записи: 1
Добавлять комментарии могут только авторизированные/зарегистрированные пользователи. Пожалуйста, зарегистрируйтесь или авторизуйтесь.