Книга: 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>
- Графика
- Удалил драйвер видеокарты, а потом еще и отключил устройство по умолчанию SVGA. После перезагрузки увидел черный экран… ...
- Что такое инфографика?
- Графика в Excel
- Векторная обработка исключений
- Macromedia Flash Professional 8. Графика и анимация
- Две формы представления сетевого графика
- Глава 17. Графика и анимация
- Компьютерная графика: прошлое и настоящее
- Компьютер от «А» до «Я»: Windows, Интернет, графика, музыка, видео и многое другое
- 2. Жизнь диктует свои законы, или Клеточные автоматы и машинная графика
- Контроль за соблюдением графика работ