Книга: JavaScript. Подробное руководство, 6-е издание

21.3. SVG - масштабируемая векторная графика

21.3. SVG - масштабируемая векторная графика

Масштабируемая векторная графика (SVG) - это грамматика языка XML для описания графических изображений. Слово «векторная» в названии указывает на фундаментальное отличие от таких форматов растровой графики, как GIF, JPEG и PNG, где изображение задается матрицей пикселов. Формат SVG представляет собой точное, не зависящее от разрешения (отсюда слово «масштабируемая») описание шагов, которые необходимо выполнить, чтобы нарисовать требуемый рисунок. Вот пример простого SVG-изображения в текстовом формате:

<!-- Начало рисунка и объявление пространства имен -->
<svg xmlns="http://www.w3.org/2000/svg"
  viewBox="0 0 1000 1000"> <!-- Система координат рисунка -->
  <defs> <!-- Настройка некоторых определений -->
    <linearGradient> <!-- Цветовой градиент с именем ''fade” -->
      <stop offset="0%" stop-color="#008"/> <!-- Начинаем с темно-синего -->
      <stop offset="100%" stop-color="#ccf"/><!--Заканчиваем светло-синим-->
    </linearGradient>
  </defs>
  <! --
    Нарисовать прямоугольник с тонкой черной рамкой и заполнить его градиентом
  -->
  <rect х="100” у="200" width="800" height="600" stroke="black"
     stroke-width="25" fill="url(#fade)"/>
</svg>

На рис. 21.1 показано графическое представление этого SVG-файла.


SVG - это довольно обширная грамматика умеренной сложности. Помимо простых примитивов рисования она позволяет воспроизводить произвольные кривые, текст и анимацию. Рисунки в формате SVG могут даже содержать JavaScript-сценарии и таблицы CSS-стилей, что позволяет наделить их информацией о поведении и представлении. В этом разделе показано, как с помощью клиентского JavaScript-кода (встроенного в HTML-, а не в SVG-документ) можно динамически создавать графические изображения средствами SVG. Приводимые здесь примеры SVG-изображений позволяют лишь отчасти оценить возможности формата SVG. Полное описание этого формата доступно в виде обширной, но вполне понятной спецификации, которая поддерживается консорциумом W3C и находится по адресу http://www.w3.org/TR/SVG/. Обратите внимание: эта спецификация включает в себя полное описание объектной модели документа (DOM) для SVG-документов. В данном разделе рассматриваются приемы манипулирования SVG-графикой с помощью стандартной модели XML DOM, а модель SVG DOM не затрагивается.

К моменту написания этих строк все текущие веб-броузеры, кроме IE, имели встроенную поддержку формата SVG (она также будет включена в IE9). В последних версиях броузеров отображать SVG-изображения можно с помощью обычного элемента <img>. Некоторые немного устаревшие броузеры (такие как Firefox 3.6) не поддерживают такую возможность и требуют использовать для этих целей элемент <object>:

<object data="sample.svg" type="image/svg+xml" width=''100" height="100"/>

При использовании в элементе <img> или <object> SVG можно рассматривать как еще один формат представления графических изображений, который, с точки зрения программиста на языке JavaScript, ничем особенным не выделяется. Гораздо больший интерес представляет сама возможность встраивания SVG-изображений непосредственно в документы и выполнения операций над ними. Поскольку формат SVG является грамматикой языка XML, изображения в этом формате можно встраивать непосредственно в XHTML-документы, как показано ниже:

<?xml version="1.0"?>
<!-- Объявить HTML как пространство имен по умолчанию, a SVG - с префиксом ''svg:" -->
<html xmlns="http://www.w3.org/1999/xhtmr'
     xmlns:svg="http://www.w3.org/2000/svg">
  <body>
    Это красный квадрат: <svg:svg width="10" height="10">
      <svg:rect x="0" y="0” width="10" height="10" fill="red"/>
    </svg:svg>
    Это голубой круг: <svg:svg width="10" height="10">
      <svg:circle cx="5" cy="5" r="5" fill="blue"/>
    </svg:svg>
  </body>
</html>

Этот прием можно использовать во всех текущих броузерах, кроме IE. Нарис. 21.2 показано, как Firefox отображает этот XHTML-документ.


Стандарт HTML5 сокращает количество различий между XML и HTML и позволяет вставлять разметку на языке SVG (и MathML) непосредственно в HTML-файлы, без объявления пространств имен или префиксов тегов:

<!DOCTYPE html>
<html>
  <body>
    Это красный квадрат: <svg width="10" height="10">
      <rect x="0" y="0" width="10" height="10" fill="red"/>
    </svg>
    Это голубой круг: <svg width="10" height="10">
      <circle cx="5" cy="5" r="5" fill="blue"/>
    </svg>
  </body>
</html>

На момент написания этих строк непосредственное встраивание SVG-изображе-ний в разметку HTML поддерживали только самые последние версии броузеров.

Так как формат SVG - это грамматика языка XML, рисование SVG-изображений заключается просто в использовании модели DOM для создания соответствующих XML-элементов. В примере 21.2 приводится реализация функции pieChart(), которая создает SVG-элементы для воспроизведения круговой диаграммы, подобной той, что показана на рис. 21.3.


Рис. 21.3. Круговая диаграмма в формате SVG, построенная JavaScript-сценарием

Пример 21.2. Рисование круговой диаграммы средствами JavaScript и SVG

/* *
* Создает элемент <svg> и рисует в нем круговую диаграмму.
* Аргументы:
* data: массив чисел для диаграммы, по одному для каждого сектора.
* width,height: размеры SVG-изображения в пикселах
* сх, су, г: координаты центра и радиус круга
* colors: массив цветов в формате HTML, по одному для каждого сектора
* labels: массив меток для легенды, по одной для каждого сектора
* 1х, 1у: координаты левого верхнего угла легенды диаграммы
* Возвращает:
* Элемент <svg>, хранящий круговую диаграмму.
* Вызывающая программа должна вставить возвращаемый элемент в документ.
*/
function pieChart(data, width, height, cx, су, r, colors, labels, lx, ly) {
  // Пространство имен XML для элементов svg
  var svgns = "http://www.w3.org/2000/svg";
  // Создать элемент <svg>, указать размеры в пикселах и координаты
  var chart = document.createElementNS(svgns, "svg:svg");
  chart.setAttribute("width", width);
  chart.setAttribute("height", height);
  chart.setAttribute("viewBox", "0 0 ” + width + " " + height);
  // Сложить вместе все значения, чтобы получить общую сумму всей диаграммы
  var total = 0;
  for(var і = 0; і < data.length; і++) total += data[і];
  // Определить величину каждого сектора. Углы измеряются в радианах,
  var angles = []
  for(var і = 0; і < data.length; і++) angles[i] = data[i]/total*Math.PI*2;
  // Цикл по всем секторам диаграммы,
  startangle = 0;
  for(var і = 0; і < data.length; i++) {
    // Точка, где заканчивается сектор
    var endangle = startangle + angles[i];
    // Вычислить координаты точек пересечения радиусов, образующих сектор,
    // с окружностью. В соответствии с выбранными формулами углу 0 радиан
    // соответствует точка в самой верхней части окружности,
    // а положительные значения откладываются от нее по часовой стрелке.
    var х1 = сх + r * Math.sin(startangle);
    var y1 = су - r * Math.cos(startangle);
    var x2 = cx + r * Math.sin(endangle);
    var y2 = су - r * Math.cos(endangle);
    // Это флаг для углов, больших половины окружности.
    // Он необходим SVG-механизму рисования дуг
    var big = 0;
    if (endangle - startangle > Math.PI) big = 1;
    // Мы описываем сектор с помощью элемента <svg:path>.
    // Обратите внимание, что он создается вызовом createElementNS()
    var path = document.createElementNS(svgns, "path");
    // Эта строка хранит информацию о контуре, образующем сектор
    var d = "М " + сх + + су + // Начало в центре окружности
      " L " + х1 + "," + у1 +  // Нарисовать линию к точке (х1,у1)
      " А " + r + "," + r +    // Нарисовать дугу с радиусом r
      " 0 " + big + " 1 " +    // Информация о дуге...
      х2 + "," + у2 +          // Дуга заканчивается в точке (х2,у2)
      Z";                      // Закончить рисование в точке (сх,су)
    // Теперь установить атрибуты элемента <svg:path>
    path.setAttribute("d", d);               // Установить описание контура
    path.setAttribute("fill", colors[i]);    // Установить цвет сектора
    path.setAttribute("stroke", "black");    // Рамка сектора - черная
    path.setAttribute("stroke-width”, "2");  // 2 единицы толщиной
    chart.appendChild(path);                 // Вставить сектор в диаграмму
    // Следующий сектор начинается в точке, где закончился предыдущий
    startangle = endangle;
    // Нарисовать маленький квадрат для идентификации сектора в легенде
    var icon = document.createElementNS(svgns, "rect");
    icon.setAttribute("x”, lx);           // Координаты квадрата
    icon.setAttribute("y", ly + ЗО*і);
    icon.setAttribute("width", 20);       // Размер квадрата
    icon.setAttribute("height", 20);
    icon.setAttribute("fill", colors[i]); // Тем же цветом, что и сектор
    icon.setAttribute("stroke", "black"); // Такая же рамка,
    icon.setAttribute("stroke-width", "2");
    chart.appendChild(icon);              // Добавить в диаграмму
    // Добавить метку правее квадрата
    var label = document.createElementNS(svgns, "text");
    label.setAttribute("x", lx + 30); // Координаты текста
    label.setAttribute("y", ly + ЗО*і + 18);
    // Стиль текста можно также определить посредством таблицы CSS-стилей
    label.setAttribute("font-family", "sans-serif");
    label.setAttribute("font-size", "16");
    // Добавить текстовый DOM-узел в элемент <svg:text>
    label.appendChild(document.createTextNode(labels[і]));
    chart.appendChild(label); // Добавить текст в диаграмму
  }
  return chart;
}

Программный код в примере 21.2 относительно прост. Здесь выполняются некоторые математические расчеты для преобразования исходных данных в углы секторов круговой диаграммы. Однако основную часть примера составляет программный код, создающий SVG-элементы и выполняющий настройку их атрибутов. Чтобы этот пример мог работать в броузерах, не полностью поддерживающих стандарт HTML5, здесь формат SVG интерпретируется как грамматика XML и вместо метода createElement() используются пространство имен SVG и метод createElementNS().

Самая малопонятная часть этого примера - программный код, выполняющий рисование сектора диаграммы. Для отображения каждого сектора используется тег <svg:path>. Этот SVG-элемент описывает рисование произвольных фигур, состоящих из линий и кривых. Описание фигуры определяется атрибутом d элемента <svg:path>. Основу описания составляет компактная грамматика символьных кодов и чисел, определяющих координаты, углы и прочие значения. Например, символ М означает «move to» (переместиться в точку), и вслед за ним должны следовать координаты X и У точки. Символ L означает «line to» (рисовать линию до точки); он рисует линию от текущей точки до точки с координатами, которые следуют далее. Кроме того, в этом примере используется символьный код А, который рисует дугу (arc). Вслед за этим символом следуют семь чисел, описывающих дугу. Точное описание нас здесь не интересует, но вы можете найти его в спецификации по адресу http://www.w3.org/TR/SVG/.

Обратите внимание, что функция pieChart() возвращает элемент <svg>, содержащий описание круговой диаграммы, но она не вставляет этот элемент в документ. Предполагается, что это будет делать вызывающая программа. Диаграмма, изображенная на рис. 21.3, была создана с помощью следующего файла:

<html>
<head>
  <script src="PieChart.js"x/scr.ipt>
</head>
<body>
</body>
</html>

В примере 21.3 демонстрируется создание еще одного SVG-изображения: в нем формат SVG используется для отображения аналоговых часов (рис. 21.4). Однако вместо создания дерева SVG-элементов с самого начала, в нем используется статическое SVG-изображение циферблата, встроенное в HTML-страницу. Это статическое изображение включает два SVG-элемента <line>, представляющих часовую и минутную стрелки. Обе линии направлены вверх, в результате чего статическое изображение часов показывает время 12:00. Чтобы превратить это изображение в действующие часы, в примере используется сценарий на языке JavaScript, устанавливающий атрибут transform каждого элемента <liпе> и поворачивающий их на углы, соответствующие текущему времени.


Обратите внимание, что в примере 21.3 разметка SVG встроена непосредственно в файл HTML5 и в ней не используются пространства имен XML, как в XHTML-файле выше. Это означает, что данный пример будет работать только в броузерах, поддерживающих возможность непосредственного встраивания разметки SVG. Однако если преобразовать HTML-файл в XHTML, тот же самый прием будет работать и в старых броузерах с поддержкой SVG.

Пример 21.3. Отображение времени посредством манипулирования SVG-изображением

<! D0CTYPE НТМ1_>
<html>
<head>
  <title>Analog Clock</title>
  <script>
    function updateTime() { // Обновляет SVG-изображение часов
                            // в соответствии с текущим временем
      var now = new Date();       // Текущее время
      var min = now.getMinutes(); // Минуты
      var hour = (now.getHours() % 12) + min/60; // Часы с дробной частью
      var minangle = min*6;      // 6 градусов на минуту
      var hourangle = hour*30;   // 30 градусов на час
      // Получить SVG-элементы стрелок часов
      var minhand = document.getElementById("minutehand");
      var hourhand = document.getElementByldC'hourhand”);
      // Установить в них SVG-атрибут для перемещения по циферблату
      minhand.setAttribute("transform", "rotate(" + minangle + ",50,50)");
      hourhand.setAttribute("transform", "rotate(" + hourangle + ”,50,50)");
      // Обновлять показания часов 1 раз в минуту setTimeout(updateTime, 60000);
    }
  </script>
  <style>
    /* Все следующие CSS-стили применяются к SVG-элементам, объявленным ниже */
    #clock { /* общие стили для всех элементов часов */
      stroke: black; /* черные линии */
      stroke-linecap: round; /* с закругленными концами */
      fill: #ееf; /* на светлом, голубовато-сером фоне */
    }
    #numbers { /* стиль отображения цифр на циферблате */
      font-family: sans-serif;
      font-size: 7pt;
      font-weight: bold;
      text-anchor: middle;
      stroke: none; fill: black;
    }
  </style>
</head>
<body>
  <!-- viewBox - система координат, width и height - экранные размеры -->
  <svg viewBox="0 0 100 100” width="500" height="500">
    <defs> <!-- Определить фильтр для рисования теней -->
      <filter x="-50%" y="-50%" width="200%" height="200%">
        <feGaussianBlur in="SourceAlpha" stdDeviation="1" result="blur" />
        <fe0ffset in="blur" dx="1" dy="1" result="shadow" />
        <feMerge>
          <feMergeNode in="SourceGraphic"/xfeMergeNode in="shadow"/>
        </feMerge>
      </filter>
    </defs>
    <circle cx="50" cy="50" r="45"/> <!-- циферблат -->
    <g> <!-- 12 часовых меток -->
      <line x1='50' y1='5.000' x2='50.00‘ y2='10.00'/>
      <line x1='72.50' y1='11.03' x2=’70.00' y2='15.36'/>
      <1ine x1='88.97' y1='27.50' x2='84.64' y2='30.00'/>
      <line x1='95.00' y1='50.00' x2='90.00' y2='50.00'/>
      <line х1 = ‘88. 97' у1 = '72. 50' х2='84.64' у2='70.00'/>
      <line х1 = '72. 50' у 1 ='88. 97' х2='70.00' у2='84.64'/>
      <line х1 = '50.00’ у 1 ='95.00' х2='50.00' у2='90.007>
      <line х1 = '27.50' у1 = '88.97' х2='30.00' у2='84.64'/>
      <line х1 = ' 11.03' у1 = '72. 50' х2=' 15.36' у2='70.00'/>
      <line х1='5.000' у1='50.00' х2='10.00' у2='50.00'/>
      <line х1='11.03' у1='27.50' х2='15.36' у2='30.00'/>
      <line х1='27.50' у1='11.03' х2='30.00' у2='15.36/>
    </g>
    <g> <!-- Числа в основных направлениях -->
      <text х="50" y=”18">12</text>
      <text х="85" y="53">3</text>
      <text х="50" y="88">6</text>
      <text х="15" y="53">9</text>
    </g>
    <!-- Нарисовать стрелки, указывающие вверх. Они вращаются сценарием. -->
    <ghands" filter="url(#shadow)"> <!-- Добавить тени к стрелкам -->
      <line x1="50" у 1="50" х2="50" у2=''24"/>
      <line х1="50" у1="50" х2="50" у2="20”/>
    </g>
  </svg>
</body>
</html>

Оглавление книги


Генерация: 1.156. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз