Делаем стильный прелоадер при помощи SVG

в 15:28, , рубрики: javascript, svg, веб-дизайн, Веб-разработка, прелоадер

Многие пользователи пренебрегают сайтами с медленной загрузкой. Ian Culshaw объясняет, как использовать SVG Raphaël библиотеку что бы создать прелоадер, который будет удерживать внимание пользователей во время загрузки страниц.

image

ДЕМО
Исходные файлы


В этом уроке я покажу вам, как удержать пользователя достаточно долго, пока загружаются изображения и сама страница.

Прелоадер основан на Gaya Design's QueryLoader и использует Raphaël библиотку для создания красивых, векторных фигур. В нашем уроке мы будем использовать большие изображения с Flickr, чтобы показать предварительную загрузку в действии.

Вопрос скорости

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

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

Приступим к работе

В этой статье мы научим вас, как привлечь внимание пользователей достаточно долго, чтобы дать вашему содержанию сайта, весь охват аудитории которого он заслуживает. Мы будем создавать прелоадер с использованием SVG библиотеки — Raphaël. Мы собираемся использовать существующие QueryLoader библиотеки из GayaDesign. Оригинальный сценарий создает полосу загрузки, которая отображается на экране, так что первым делом нам нужно удалить этот стиль, который поставляется в комплекте с QueryLoader для того что бы реализовать наши собственные стили.

1. Папки

В главной директории проекта находятся папки под названиями CSS и JS, и файл demo.html. Папка CSS — пустая, но в будущем будет содержать style.css. Папка js содержит JQuery и Raphaël; резервные библиотеки используется только для того если не отвечает CDN.

image

2. Новый проект

Откройте файл demo.html. В теле документа у нас есть два элемента section: один с нашими изображениями для прелоадера, в другом находится сам прелоадер. Изображения будут невидимы для пользователя, пока полностью не закончится их загрузка.

Прелоадер состоит из двух элементов, один из которых является наш загрузчик, другой элемент это круг, который мы разместим над загрузчиком. Нам не нужно редактировать demo.html так как весь наш код будет работать при помощи JavaScript.

<!doctype html>
<html class="no-js" lang="en">
<head>
<meta charset="utf-8">
<title></title>
<script src="//cdnjs.cloudflare.com/ajax/libs/modernizr/2.0.6/modernizr.min.js"></script>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<section id="loading">
<figure id="innerCircle" class="circle"></figure>
<div id="loader"></div>
</section>
<section id="gallery">
<img src="http://farm1.staticflickr.com/94/243962216_49afc8c9ba_o.jpg">
<img src="http://farm6.staticflickr.com/5283/5361320118_d193bf5639_b.jpg">
<img src="http://farm7.staticflickr.com/6090/6125129993_9e675f8ca0_o.jpg">
</section>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/raphael/2.0.1/raphael-min.js"></script>
<script>window.jQuery || document.write('<script src="js/jquery.js"></script>')</script>
<script>window.Raphael || document.write('<script src="js/raphael.js"></script>')</script><script type="text/javascript" src="js/queryloader.js"></script
<script defer type="text/javascript">
QueryLoader.init();
</script>
</body>
</html>

3. Добавление CSS

Здесь мы добавим некоторые основные стили для нашего прелоадера.

#loading {
        position: absolute;
        width: 100%;
        height: 100%;
        background: #fff;
}
 
#loading .circle {
        width: 206px;
        height: 206px;
        position: absolute;
        left: 50%;
        top: 50%;
        margin: -103px 0 0 -103px;
        background: pink;
}
 
#loading #loader {
        width: 220px;
        height: 220px;
        position: absolute;
        left: 50%;
        top: 50%;
        margin: -110px 0 0 -110px;
        background: transparent;
}

4. Прелоадер

Теперь, что бы оживить нашу страницу, используем JavaScript. Откройте queryloader.js в папке js. Здесь мы добавим некоторые свойства, которые будут находиться в одном объекте в целях сокращения объема памяти.

var QueryLoader = {
      overlay: "",
      loadBar: "",
      preloader: "",
      items: new Array(),
      doneStatus: 0,
      doneNow: 0,
      selectorPreload: "body",
      logoImg: false,
      logoCircle: false,
      fakeCircle: false,
      ieLoadFixTime: 2000,
      ieTimeout: "",
      initialise: true,
      sec: 0,
      raph: false,
   
      init: function() {

5. Параметры высоты

Для инициализации, в JQuery мы добавим функцию resize для регулировки высоты страницы, чтобы наш прелоадер отображался одинаково, если пользователь изменяет размеры окна. Без этого, при изменении размера окна, прелоадер может потеряться из виду.

...
  initialise: true,
  sec:0,
  raph: false,
   
  init:   function() {
          $(window).resize(function() {
                  $('#loading').height($(window).height());
          });
          
          $('#loading').height($(window).height());
          
          if (navigator.userAgent.match(/MSIE (d+(?:.d+)+(?:bd*)?)/) == "MSIE 6.0,6.0") {
                  // break if IE6
                  return false;
          }
          
          if(QueryLoad.selectorPreload == 'body') {
                  QueryLoader.spawnLoader();
                  QueryLoader.getImages(QueryLoader.selectorPreload);
                  QueryLoader.createPreloading();
          } else {
                  $(document.ready(function) {
                          QueryLoader.spawnLoader();
  ...

6. Оптимизация

Нам нужно отредактировать оригинальный сценарий QueryLoader. Нужно убрать код который вставляется в некоторые HTML-страницы, которые в дальнейшем мы будем использовать. Вы должны удалить этот код, он находится на строке 115 в файле queryloader.js.

spawnLoader: function() {
          if (QueryLoader.selectorPreload == "body") {
                  var height = $(window).height();
                  var width = $(window).width();
                  var position = "fixed";
          } else {
                  var height = $(QueryLoader.selectorPreload).outerHeight();
                  var width = $(QueryLoader.selectorPreload).outerWidth();
                  var position = "absolute";
          }
          var left = $(QueryLoader.selectorPreload).offset()['left'];
          var top = $(QueryLoader.selectorPreload).offset()['top'];
          
          // <<< Начало удаления
          QueryLoader.overlay = $("<div></div>").appendTo($(QueryLoader.selectorPreload));
          $(QueryLoader.overlay).addClass("QOverlay");
          $(QueryLoader.overlay).css({
                  position: position,
                  top: top,
                  left: left,
                  width: width + "px",
                  height: height + "px"
          });
          
          QueryLoader.loadBar = $("<div></div>").appendTo($(QueryLoader.overlay));
          $(QueryLoader.loadBar).addClass("QLoader");
          
          $(QueryLoader.loadBar).css({
                  position: "relative",
                  top: "50%",
                  width: "0%"
          });
          // <<< Конец
  },

7. Декоративное окончание

Здесь мы адаптируем функцию doneLoad, для показа #gallery, для этого необходимо задать значение block, для атрибута display . Мы также используем JQuery для анимации прозрачности #loading, поэтому мы сделаем элемент #loading — невидимым, прежде чем удалить его с DOM.

doneLoad: function() {
                  //prevent IE from  calling the fix
                   clearTimeout(QueryLoader.ieTimeout);
                                   
                  //determine the height  of the preloader for the effect
                  if  (QueryLoader.selectorPreload == "body") {
                           var height = $(window).height();
                  } else {
                           var height = $(QueryLoader.selectorPreload).outerHeight();
                  }
                  
                  // Step 7 add this code
                  $('#gallery').show();
                   $('#loading').animate({'opacity': 0}, 1200, function() {
                           $(this).remove();
                  })

8. Ещё больше оптимизации

Далее мы удаляем код из оригинальной полосы загрузки, этот код мы показывали в шаге 7. В оригинале анимация будет исчезать сама, в шаге 7 мы рассказали что в нашей галерее появляется и исчезает загрузчик. Мы будем адаптировать эту функцию в конце урока.

doneLoad: function() {
                  ...
                  $('#gallery').show();
                  $('#loading').animate({'opacity': 0}, 1200, function() {
                          $(this).remove();
                  })
   
                  
                  
                  // <<< Step 8 Remove this code
                  $(QueryLoader.loadBar).animate({
                          height: height + "px",
                          top: 0
                  }, 500, "linear", function() {
                          $(QueryLoader.overlay).fadeOut(800);
                          $(QueryLoader.preloader).remove();
                  });
                  // <<< End removal
          }

9. Постепенное увеличение процесса

Здесь мы используем вызов функции updateVal, которая будет обновлять наш SVG круг. QueryLoader.doneNow это количество изображений завершённых в течение загрузки страницы. 105 это радиус окружности, а this.sec путь Raphaël SVG.

imgCallback: function() {
          QueryLoader.doneNow++;
          QueryLoader.updateVal(QueryLoader.doneNow, this.items.length, 105, this.sec);
          QueryLoader.animateLoader();
  },

10. Больше функциональности

Здесь мы добавляем updateVal только для функции imgCallback. Если параметр initialise со значением true, то можно загружать круг, для продолжения анимации. Кроме того, если круг завершен, мы должны расширить его, потому что мы не можем использовать больше чем 360 градусов дуги.

updateVal: function(value, total, R, hand, id) {
      if (QueryLoader.initialise) {
                  if(value == total) {
                          this.raph.clear();
                          // 2.1.1 - CIRCLE COMPLETION.
                          this.fakeCircle = this.raph.circle(110,110,105).attr({colour: '', "stroke-width": 10});
                  } else {
                  hand.animate({arc: [value, total, R]}, 0, ">");
              }
      }
  },

11. Удаление HTML

Мы удаляем код HTML, который содержит оригинальный сценарий QueryLoader.

createPreloading: function() {
          QueryLoader.preloader = $("<div></div>").appendTo(QueryLoader.selectorPreload);
          $(QueryLoader.preloader).css({
                  height:         "0px",
                  width:          "0px",
                  overflow:       "hidden"
          });

12. Вставляем Raphaël

Теперь мы создадим два SVG элемента, которые содержат в себе несколько элементов, присутствующих в нашем HTML. Мы установили некоторые переменные и атрибуты для SVG. Также #loader сделаем больше чем #innerCircle, это создаст иллюзию, что загрузчик дошел до границы.

createPreloading: function() {
          
          var logoC = Raphael("innerCircle", 206,206);
          $('#innerCircle').css('z-index', '31'); 
          this.logoCircle = logoC.circle(103,103,103).attr({'stroke': 'rgb(125,208,163)', 'fill': 'url(wave.jpg)', "stroke-width": 0});
   
          this.raph = Raphael("loader", 220, 220),
          xy = 110,
      R = 210,
      this.initialise = true,
      param = {stroke: "#000", "stroke-width": 10},
   
          // Custom Attribute
          this.raph.customAttributes.arc = function (value, total, R) {
              var alpha = 360 / total * value,
                  a = (90 - alpha) * Math.PI / 180,
                  x = xy + R * Math.cos(a),
                  y = xy - R * Math.sin(a),
                  color = 'rgb(29,79,107)',
                  path;
                  
                  path = [["M", xy, xy - R], ["A", R, R, 0, +(alpha > 180), 1, x, y]]; // MATRIX PATH
              return {path: path, stroke: color};
          };
  ... 

13. Пользовательские атрибуты

Это сложный математический процесс, что в конечном итоге будет матрица, которую можно сделать в виде дуги. К счастью, только ключевые значения value, total и R (радиус); как определили ранее, это анимация updateVal. Цвет не имеет значение, его можно изменить на ваше усмотрение.

// Custom Attribute
  this.raph.customAttributes.arc = function (value, total, R) {
     var alpha = 360 / total * value,
         a = (90 - alpha) * Math.PI / 180,
         x = xy + R * Math.cos(a),
         y = xy - R * Math.sin(a),
         color = 'rgb(29,79,107)',
         path;
     if (total == value) {
  path = [["M", xy, xy - R], ["A", R, R, 0, +(alpha > 180), 1, x, y]];
     } else {
         path = [["M", xy, xy - R], ["A", R, R, 0, +(alpha > 180), 1, x, y]];
     }
     return {path: path, stroke: color};
  };

14. Атрибуты цвета

Использование функции updateVal является ключом к созданию эффекта полного круга после того, как изображения будут загружены. Цвет вы можете изменить или даже заменить изображение, но при настройке убедитесь, что вы измените все значения – одинаково, иначе в конечном итоге вы получите круги с разными цветами!

this.raph.customAttributes.colour = function() {
          return {stroke: 'rgb(29,79,107)'};
  };

15. Секторы

После предыдущего кода мы определим сектор, но перед этим создадим путь и настроим его дуги (который будет вызывать нашу функцию дуги в шаге 13). Когда мы вызываем наш updateVal, мы устанавливаем количество загруженных изображений на 0.

var length = QueryLoader.items.length; 
  this.sec = this.raph.path().attr(param).attr({arc: [0, 60, R]});
  QueryLoader.updateVal(0, length, 105, this.sec, 2);

16. Анимация

После кождого загруженного изображения, вызывается animateLoader. Мы заменим большую логическую часть, поэтому я рекомендую просто переписать функцию. Функция измеряет сколько осталось не загруженных изображений и если опережаем на 99 процентов то происходит вызов doneLoad.

animateLoader: function() {
  var perc = (100 / QueryLoader.doneStatus) * QueryLoader.doneNow;
  var angle = (3.6 * perc);
  QueryLoader.updateVal(QueryLoader.doneNow, this.items.length, 105, this.sec, 2);
  if (perc > 99) {
  QueryLoader.doneLoad();
  }
  },
   
  doneLoad: function() { ... 

17. Настройка

Здесь мы постепенно изменим прозрачность каждого круга до полного исчезновения и удаления с предварительной загрузки элементов в целом. На данный момент мы имеем готовый прелоадер, который будет работать в браузерах IE7, однако ещё можно добавить индивидуальный эффект.

...
  doneLoad: function {
  ...
  var qLoad = this;
  qLoad.sec.hide();
   
  qLoad.logoCircle.stop().animate({opacity: 0}, 700);
  qLoad.fakeCircle.stop().animate({opacity: 0}, 700, 'easeInOut');
          
  $('#loading').css('min-height', 'auto').animate({top: ($(window).height()*-1) + 'px'}, '800', function() {      
                  $(this).remove();                               
          });
  });
  ...

18. Сокращение элементов

Используя CSS трансформации, мы можем масштабировать или «сжимать» элементы.

...
  doneLoad: function() {
  ...
  var qLoad = this;
  qLoad.sec.hide();
   
  qLoad.logoCircle.stop().animate({transform: "s0.6 0.6 103 103"}, '1000', "easeInOut");
  qLoad.fakeCircle.stop().animate({transform: "s0.6 0.6 110 110"}, '1000', "easeInOut", function() {
  qLoad.logoCircle.stop().animate({opacity: 0}, 700);
  qLoad.fakeCircle.stop().animate({opacity: 0}, 700, 'easeInOut');
  $('#loading').css('min-height', 'auto').animate({top: ($(window).height()*-1) + 'px'}, '800', function() {
  $(this).remove();
  });
  });
   
  }
  ...

19. Взлет

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

/* Keyframes */
   
  @-webkit-keyframes fly-away {
          0%   { -webkit-transform: translate3d(0,0, 0); }
          100% { -webkit-transform: translate3d(0,-900px, 0); }
  }
  @-moz-keyframes fly-away {
          0%   { -moz-transform: translate(0,0); }
          100% { -moz-transform: translate(0,-900px); }
  }
  @-ms-keyframes fly-away {
          0%   { -ms-transform: translate(0,0); }
          100% { -ms-transform: translate(0,-900px); }
  }
  ...

ДЕМО
Исходные файлы

Автор: Lecaw

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js