Raphael.js - Quickstart and Practice - #FFF001

Эта статья - это текстовая версия нашего совместного доклада, авторами которого стали Александр Павловский и Олег Рудой. Собственно этот доклад и стал темой первой встречи нашего комьюнити.

Intro

 И так начинаем...

Сегодня возможности CSS3 помогают реализовать почти любую UI прихоть дизайнеров и заказчиков. В виду чего наш интерфейс можно нарисовать одними лишь HTML элементами придав им динамику тем же CSS3. Но всё же почти любую прихоть, мы можем нарисовать и блок и превратить его в круг, но к сожалению CSS3 еще не научился рисовать сложные кривые и пути (path). В этом нам просто отличнейшим поможет маленькая и весьма развитая библиотека Raphael.js.

Paphael.js – это JavaScript библиотека которая простейшим способом реализовывает векторную графику (SVG - Scaleble Vector Graphic) в Web. Сегодня я вам расскажу как быстро начать работать с Raphael.js.
Собственно почему же я выбрал Raphael.js. Так уж сложилось что до сих пор время от времени приходится поддерживать десктоп с IE8 и одним из основных преимуществ Raphael – это то что он у нас не Flash и он кроссбраузерный. А именно заявлено что это: Firefox 3.0+, Safari 3.0+, Chrome 5.0+, Opera 9.5+ and Internet Explorer 6.0+. Естественно это обусловлено хорошей поддержкой SVG  caniuse.com демонстрирует вот такие результаты
Результаты на лицо, проблемы лишь с IE8 ну и старыми версиями Android. Но в случае с IE8 Raphael отобразит наши графические элементы в экзотическом формате VML (Аналог SVG разрабатываемый Microsoft).

Чем же еще прелестен вектор? Я визуально попытался изобразить разницу:
Да, современный пользователь не хочет на совей retina а то и здоровенной плазме видеть эти угловатые кривые, SVG-вектор же нарисует же для нас идеальные скругленные формы. Ведь в понимании круг он в браузере нарисует круг увеличивая до бесконечности который он будет оставаться круглым.

Так же не стоит забывать почему же всё такие не трендовый Canvas? Сравнивать эти две технологии можно очень долго и в итоге все равно будет сложно выбрать победителя, ведь выбираем мы по потребности. Поэтому прежде всего явное отличие в том что SVG это DOM-элементы и все элементы SVG так же будут отображены как DOM-элементы на которые мы в дальнейшем можем влиять не обязательно с помощью Raphael.js.
Вот пример SVG кода уже глазами браузера на котором отображены несколько графических фигур с некой стилистикой.
Так же плюс в том что воспроизвести сложную фигуру которую нам радостно и с легкостью нарисует нам дизайнер к примеру в Adobe Illustrator на труда не составит, а более каждой составляющей части нашего векторного рисунка мы сможем придать функционал и анимацию.
К тому же одним из немаловажных плюсов будет достаточно хорошая документация.
Ну и прежде чем пойти дальше хочется повторить слова разработчика и идеолога Raphael.js  Дмитрия Барановского: «Raphael for SVG is like JQuery for DOM» Действительно Raphael как и Великий Рафаэль Санти нам помогает оживить рисунок, придать ему движения и практическую функциональность.


Practice


Пример 1


И так начнем с простого, нарисуем круг. Для удобства решили выложить примеры на jsfiddle.
Как мы видим для подключения Raphael достаточно только подключить его библиотеку которую можно взять с официального сайта. Наш документ содержит лишь один элемент это div#draw собственно в него мы и будем инициализировать наш SVG с помощью Raphael.

Первым делом нужно инициализровать холст делает это таким образом:
var paper = Raphael('draw', 400, 250);
Соответственно передавая в объект Rapael paper Dom-элемент с id draw и размеры холста.

Далее мы создаем объект нашего круга и помещаем на холст paper, задав ему параметры (позиция x, позиция y, радиус)
var circle = paper.circle(50, 50, 40);

Далее мы задаем стилистику нашего круга это делается передачей параметров свойству attr
 circle.attr({
  fill: 'yellow',
  stroke: 'gray',
  'stroke-width': '3',
  'stroke-dasharray': '.',
  cursor: 'pointer',
 });
Тут мы соответственно задали заливку пунктирную обводку ну и курсор. Подробнее об допустимых атрибутах можно легко узнать из документации

А теперь попробуем задать событие на клик, предварительно определив нашей ноде id
circle.node.id = "circle";

$('#circle').on('click', function(){
 alert('I\'m a circle');
});
Я предпочел JQuery-way и уже навешал событие просто обратившись к объекту по id

Пример 2


В этом примере приведены базовые фигуры отрисованные с помощью Raphael:
 rect = paper.rect(40, 40, 100, 50), // прямоугольник
 line = paper.path('M0 0L150 100'), // линия
 text = paper.text(100, 150, 'I\'m just a text'), // текст
 path = paper.path('M 0 200 L 200 200 L 100 300 z'), // путь
 image = paper.image('img/css.gif', 250, 0, 250, 200); // изображение

Но особое внимание хотелось бы уделить объекту path в котором собственно и таится вся прелесть векторной графики.
path = paper.path('M 0 200 L 200 200 L 100 300 z')
Этот path изображает треугольник, не сложно догадаться каким что рисуется он заданием координат конечных точек линий ну и в свою очередь z завершает рисунок соединяя последнюю точку с начальной. Подробнее о построении pathов можно узнать тут

Есть так же у Raphael некий аналог определения очередности объектов
rect.toBack(); // переместит rect на задний план
rect.toBack(); // переместит rect на передний план

Ну наверно мое самое любимое, это метод animate, в данном случае мы анимируем атрибут transform, задавая новую трансформацию, соответственно t - translate(перемещение в определенную координату) s - scale(увеличение на пропорцию) r - radius(поворот на какой-то угол). Анимация длится 800ms с эфектом 'bounce', да в наше время имея возможность применять анимации без easing'ов просто грешно.
image.click(function(){
 this.animate({'transform': 't40 40s1.2r5'}, 800, 'bounce');
});

Пример 3


Это не сложный пример создания множества фигур с рандомной трансформацией
var paper = Raphael('draw', $('#content').width(), 600),
 filler = {
  fill: 'red',
  stroke: 'gray',
  'sctroke-width': 2
 },
 circles = [],
 size, angle;

for (var i = 0; i < 50; i++) {
 size = Math.random() * 100 + 30;
 filler.fill = "rgb(" + Math.random() * 255 + "," + Math.random() * 255 + "," + Math.random() * 255 +")";
 circles.push(
  paper.rect(
   Math.random() * paper.width,
   Math.random() * paper.height,
   size,
   size,
   Math.random() * 50
  )
   .attr(filler)
   .transform("r" + Math.random() * 90)
 );

};

Пример 4

Пример анимации, если кликнуть в примере на спираль она начнет крутится убеждая Вас в том что лучше Raphael.js в мире быть ничего не может)

Вы можете создавать анимацию в Raphael двумя способами либо передавая параметры непосредственно обработчику Element.animate либо создав объект Raphael.animation и уже в дальнейшем передавать его серии либо одному элементу
animInf = Raphael.animation({transform: 't200 200s10r-720'}, 4000, "easeInOut").repeat(Infinity);
В данном случае из новшеств только свойство repeat которое определяет число циклоп анимации в данном случая это удовольствие будет длится вечность)

Пример 5

И так Drug'n'Drop, реализация Raphael стандартна и слажено работающая.

Но для начала хочется рассказать про Paper.set() с его помощью мы получаем возможность объединять элементы Raphael в группы и с комфортом оперировать ими.
var paper = Raphael('draw', $('#content').width(), 600),
    catSet = paper.set();
  
 catSet
  .push(paper.path("m69.598007,392.080017c1.085999,-14.994995 4.411011,-9.707031 6.544983,-17.026001c15.416016,-52.88501 -25.237,-100.822021 -4.346985,-151.776001c-9.787003,0.555969 -24.16301,-23.432983 -23.068008,-43.833008c1.096985,-20.446991 13.373993,-40.070984 24.039993,-65.162994c-45.321999,-109.63501 -22.305008,-67.993988 41.938019,-42.265991c67.174988,-45.722015 80.600952,-6.480011 92.202972,-14.40802c57.314987,-38.154999 43.955994,-57.608002 52.587997,31.328003c95.209015,77.821991 168.78302,157.10202 111.286011,273.677032c62.024017,5.997986 74.03595,-82.088013 56.391968,-155.812012c-12.394958,-35.309021 22.537018,-50.17099 25.552032,61.151001c-11.772003,90.749023 -35.586975,129.896973 -95.696014,136.859985c4.471008,3.617004 -84.898987,25.504028 -141.927994,12.819031c-4.311981,-0.959045 -12.492981,-10.453003 6.860016,-20.520996c7.192993,-3.742004 -15.424011,-11.28302 -25.899994,-9.485046c-12.377014,2.125 -13.226013,30.888 -14.891022,31.532043c-10.858978,4.203003 -18.873993,4.947998 -37.723999,0.502991c-3.635986,-0.856995 -3.754974,-11.048035 3.817017,-18.825012c6.520996,-6.697998 -11.511017,-19.202026 -14.688995,-22.528992c-6.355988,-6.653992 -17.797974,-10.775024 -21.122986,-8.841003c-8.925995,5.190002 7.856995,35.039978 -41.28598,23.325012").attr({stroke: '#000000','stroke-width': '0','stroke-opacity': '1','fill': '#000000'}))
  .push(paper.path("m171.171982,120.373047c14.716003,-1.785004 16.588989,-10.28299 -1.192993,-6.34201c-15.330017,-2.709991 -15.615021,7.016998 1.192993,6.34201z").attr({fill: '#FFFFFF','stroke-width': '0','stroke-opacity': '1'}))
  .push(paper.path("m159.049973,119.146027c3.35199,7.553986 16.674011,8.468994 21.717987,2.526001").attr({stroke: '#333333',fill: 'none','stroke-width': '0','stroke-opacity': '1'}))
  .push(paper.path("m158.039963,112.075043c1.528992,-12.05899 24.884003,-11.454987 26.264008,0").attr({stroke: '#333333',fill: 'none','stroke-width': '0','stroke-opacity': '1'}))
  .push(paper.path("m169.191971,116.620026c0,2.789001 -0.904999,5.050995 -2.019989,5.050995c-1.115997,0 -2.02002,-2.260986 -2.02002,-5.050995l0,0c0,-2.789001 0.904999,-5.050995 2.02002,-5.050995c1.11499,0.001007 2.019989,2.261993 2.019989,5.050995z").attr({id: '',parent: '','stroke-width': '0','stroke-opacity': '1','fill': '#000000'}))
  .push(paper.path("m91.099991,117.68103c-11.677002,-1.415985 -13.162994,-8.158997 0.946014,-5.031982c12.164001,-2.151001 12.389984,5.567993 -0.946014,5.031982z").attr({fill: '#FFFFFF','stroke-width': '0','stroke-opacity': '1'}))
  .push(paper.path("m100.718002,116.707031c-2.660004,5.993988 -13.230988,6.720001 -17.233002,2.003998").attr({"stroke-width": '0.79',stroke: '#333333',fill: 'none','stroke-opacity': '1'}))
  .push(paper.path("m101.520004,111.097046c-1.213989,-9.567993 -19.744995,-9.088989 -20.839996,0").attr({"stroke-width": '0.79',stroke: '#333333',fill: 'none','stroke-opacity': '1'}))
  .push(paper.path("m92.671005,114.704041c0,2.213013 0.717987,4.007996 1.602997,4.007996s1.602997,-1.794006 1.602997,-4.007996l0,0c0,-2.212982 -0.717987,-4.007996 -1.602997,-4.007996c-0.885986,0 -1.602997,1.793976 -1.602997,4.007996z").attr({id: '',parent: '','stroke-width': '0','stroke-opacity': '1','fill': '#000000'}))
  .push(paper.path("m98.966019,171.694031c1.238007,37.747009 67.011963,47.419983 81.185974,0.625").attr({"stroke-width": '2',stroke: '#808080',fill: 'none','stroke-opacity': '1'}))
  .push(paper.path("m98.272995,171.536011c-11.743011,24.702026 -31.454987,34.617004 -39.062027,0.634033").attr({"stroke-width": '2',stroke: '#808080',fill: 'none','stroke-opacity': '1'}))
  .push(paper.path("m89.899002,140.739044c9.838989,-2.843994 21.002991,-3.125 30.276001,1.703003c3.287994,1.885986 6.541992,4.540985 7.855957,8.191986c1.073029,4.391998 -2.536957,9.096008 -7.085968,8.988007c-3.379974,0.096008 -6.379974,-2.190002 -7.789001,-5.154999c-2.050995,-2.04599 -6.502991,-0.130005 -4.454987,2.903992c0.949005,3.451019 5.191986,4.804016 6.024994,7.959015c0.466003,4.373993 -3.856995,7.378021 -7.77298,7.839996c-5.090027,0.906006 -11.040009,-0.093994 -14.651001,-4.04303c-3.112,-4.016968 -1.653015,-9.317963 -1.896027,-13.984985c0.443024,-2.53598 -1.375,-6.013977 -4.133972,-3.740997c-1.275024,1.396027 -3.743011,2.932007 -3.677002,-0.300995c2.052979,-5.338989 3.078979,-6.623993 7.303986,-10.360992l0,0z").attr({stroke: '#000000',fill: '#999999','stroke-width': '0','stroke-opacity': '1'}))
  .push(paper.path("m73.692001,156.521027c-21.166016,-7.858002 -47.851997,-8.958008 -65.660015,7.070984").attr({stroke: '#808080',fill: 'none','stroke-width': '1','stroke-opacity': '1'}))
  .push(paper.path("m139.351974,164.603027c27.06601,-11.019012 55.700989,9.437988 68.690002,32.325012").attr({stroke: '#808080',fill: 'none','stroke-width': '1','stroke-opacity': '1'}))
  .push(paper.path("m73.692001,174.704041c-23.068008,-3.552979 -44.232002,13.190002 -51.517998,34.345032").attr({stroke: '#808080',fill: 'none','stroke-width': '1','stroke-opacity': '1'}))
  .push(paper.path("m130.259964,178.745056c19.740997,9.684998 31.493988,32.900024 28.283997,54.547974").attr({stroke: '#808080',fill: 'none','stroke-width': '1','stroke-opacity': '1'}))
  .push(paper.path("m59.460999,165.68103c-20.835999,-0.317993 -43.075996,2.538025 -59.460993,12.994995").attr({"stroke-width": '1.14',stroke: '#808080',fill: 'none','stroke-opacity': '1'}))
  .push(paper.path("m153.884964,179.896057c18.493988,9.327026 27.196014,31.562988 39.623993,48.205017").attr({"stroke-width": '1.15',stroke: '#808080',fill: 'none','stroke-opacity': '1'}))
  .draggable();
Таки да это наш WonderCat и path'ы из которых он состоит объедены в одно целое, и в итоге наречен рукописным методом draggable(). Рассмотрим его по подробнее.

Raphael.st.draggable = function() {
 var me = this,
  lx = 0,
  ly = 0,
  ox = 0,
  oy = 0,
  moveFnc = function(dx, dy) {
    lx = dx + ox;
    ly = dy + oy;
    me.transform('t' + lx + ',' + ly);
  },
  startFnc = function() {},
  endFnc = function() {
    ox = lx;
    oy = ly;
  };
  
 me
  .attr('cursor', 'move')
  .drag(moveFnc, startFnc, endFnc);
};
Свойство Raphael.st дает возможность нам создавать собственный методы и присваивать их set'ам. В данном случае было создано три функции которые в дальнейшем были использованы как callback'и метода Raphael Element.drag().  Element.drag() - в итоге получает три события соответственно на move, start и end процесса перетаскивания.

Пример 6

Ну и финальный пример это морфинг - трансформация одного path'а в другой.
var path = {
      fly: 'M75.749,25.338c-0.915,6.4.....677,152.489,119.883,154.234,115.147,156.411z',
      elephant: 'M178.205,3.24c1.68,01....857C173.437,4.804,175.877,4.13,178.205,3.24z',
      palm: 'M91.398,181.119c-0.587-2....90.769,181.372,91.083,181.245,91.398,181.119z',
      palmWinded: 'M92.292,180.733c-02...2C91.649,180.993,91.97,180.863,92.292,180.733z'
    };

Raphael.el.morfing = false; // Morhing detector with Raphael.el

$(function(){

  var paper = Raphael('draw'),
      animal = paper.path(path.fly),
      palm = paper.path(path.palm);

  // Animals morphing
      animal
        .attr({
          'stroke': 'gray',
          'stroke-width': 3,
          cursor: 'pointer',
        })
        .click(function(){
          this.morfing = !this.morfing;
          this.animate({path: (this.morfing ? path.elephant : path.fly)}, 1000, 'easeInOut'); 
        });

  // Palm morphing  
  var animPalm = Raphael.animation(
        {
          '50%': {path: path.palmWinded},
          '100%': {path: path.palm}
        },
        4000, "easeInOut")
      .repeat(5);

      palm
        .attr({
          fill: '#21251B',
          stroke: '#997402',
          transform: 't400 50s1.5',
          cursor: 'pointer'
        })
        .click(function() {
          this.animate(animPalm);
        });

});

В нашем примере было создано две пары path'ов и по click событию происходит магическая трансформация из мухи в слона в первом случае и эмуляция ветра во втором случае.

Пример 7 (SVG карта областей Украины)

Олег Рудой радостно предоставил пример реализации карты Украины, которая была скачена в формате SVG с сайта Wikipedia (http://uk.wikipedia.org/wiki/%D0%A4%D0%B0%D0%B9%D0%BB:Map_of_Ukraine_Oblasts_DEMO.svg)


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

Комментарии

Отправить комментарий

Популярные сообщения