Масштабируемая векторная графика (SVG) – это грамматика языка XML для описания графических изображений. Слово «векторная» в названии указывает на фундаментальное отличие от таких форматов растровой графики, как GIF, JPEG и PNG, где изображение задается матрицей пикселов. Формат SVG пред_ ставляет собой точное, не зависящее от разрешения (отсюда слово «масштабируе_ мая») описание шагов, которые необходимо выполнить, чтобы нарисовать тре_ буемый рисунок. Вот пример простого SVG_изображения в текстовом формате:
<!__ Начало рисунка и объявление пространства имен __> <svg xmlns="http://www.w3.org/2000/svg"
На рис. 22.3 показано графическое представление этого SVG_файла.
SVG – это довольно обширная грамматика умеренной сложности. Помимо про_ стых примитивов рисования она позволяет воспроизводить произвольные кри_ вые, текст и анимацию. Рисунки в формате SVG могут даже содержать Java_ Script_сценарии и таблицы CSS_стилей, что позволяет наделить их информацией о поведении и представлении. В этом разделе показано, как с помощью клиент_ ского JavaScript_кода (встроенного в HTML_, а не в SVG_документ) можно дина_ мически создавать графические изображения средствами SVG. Приводимые здесь примеры SVG_изображений позволяют лишь отчасти оценить возможно_ сти формата SVG. Полное описание этого формата доступно в виде общирной, но вполне понятной спецификации, которая поддерживается консорциумом W3C и находится по адресу http://www.w3.org/TR/SVG/. Обратите внимание: эта спе_ цификация включает в себя полное описание объектной модели документа (DOM) для SVG_документов. В то же время в данном разделе рассматриваются приемы манипулирования SVG_графикой с помощью стандартной модели XML DOM, а модель SVG DOM вообще не упоминается.
К моменту написания этих строк из ведущих веб_броузеров лишь Firefox 1.5 имел встроенную поддержку формата SVG. Чтобы в этом броузере просматри_ вать SVG_графику, достаточно просто ввести URL_адрес требуемого изображе_ ния. SVG_графику очень удобно встраивать прямо в XHTML_файлы, как это по_ казано в следующем примере:
<?xml version="1.0"?> <!__
Объявить пространство имен HTML по умолчанию, а SVG _ с префиксом "svg:"
__>
564 Глава 22. Работа с графикой на стороне клиента
Рис. 22.4. SVG'графика в XHTML'документе
Рис. 22.5. Круговая диаграмма в формате SVG, построенная JavaScript'сценарием
На рис. 22.4 показано, как Firefox 1.5 отображает этот XHTML_документ.
Изображения в формате SVG могут также встраиваться в HTML_документы внутри тега <object>, что дает возможность отображать их с помощью модулей расширения. Компания Adobe свободно распространяет (без открытых исход_ ных текстов) модуль просмотра SVG_графики для работы в составе наиболее рас_ пространенных броузеров и операционных систем. Найти его можно, следуя по ссылкам, начиная с адреса http://www.adobe.com/svg.
Так как формат SVG – это грамматика языка XML, рисование SVG_изображений заключается лишь в использовании модели DOM для создания соответствующих XML_элементов. В примере 22.8 приводится код функции pieChart(), которая создает SVG_элементы для воспроизведения круговой диаграммы, подобной по_ казанной на рис. 22.5. (Другие технологии создания векторной графики, описы_
22.3. SVG – масштабируемая векторная графика
ваемые в этой главе, также могут использоваться для создания аналогичных круговых диаграмм.)
Пример 22.8. Рисование круговой диаграммы средствами JavaScript и SVG
/**
* Рисует круговую диаграмму внутри элемента <svg>.
* Аргументы:
* canvas: SVG_элемент (или id этого элемента) для рисования
* data: массив значений для диаграммы, по одному для каждого сектора
* cx, cy, r: координаты центра и радиус круга
* colors: массив HTML_строк цветов, по одному на каждый сектор
* labels: массив меток легенды, по одной на каждый сектор
* lx, ly: координаты верхнего левого угла легенды диаграммы
*/
function pieChart(canvas, data, cx, cy, r, colors, labels, lx, ly) { // Отыскать "холст", если он задан значением id
if (typeof canvas == "string") canvas = document.getElementById(canvas);
// Сложить вместе все значения, чтобы получить общую сумму всей диаграммы var total = 0;
for(var i = 0; i < data.length; i++) total += data[i];
// Определить величину каждого сектора. Углы измеряются в радианах.
// В соответствии с выбранными формулами углу 0 радиан соответствует
// точка в самой верхней части окружности, а положительные значения
// откладываются от нее по часовой стрелке.
var x1 = cx + r * Math.sin(startangle); var y1 = cy _ r * Math.cos(startangle); var x2 = cx + r * Math.sin(endangle); var y2 = cy _ r * Math.cos(endangle);
// Это флаг для углов, больших половины окружности var big = 0;
if (endangle _ startangle > Math.PI) big = 1;
// Мы описываем сектор с помощью элемента <svg:path>
// Примечательно, что он создается вызовом createElementNS() var path = document.createElementNS(SVG.ns, "path");
// Эта строка хранит информацию о пути пера, рисующего сектор var d = "M " + cx + "," + cy + // Начало в центре окружности
" L " + x1 + "," + y1 + // Нарисовать линию к точке (x1,y1)
" A " + r + "," + r + // Нарисовать дугу с радиусом r
" 0 " + big +
"
1 " +
//
Информация о дуге...
x2 + "," + y2
+
//
Дуга заканчивается в
точке (x2,y2)
Глава 22. Работа с графикой на стороне клиента
" Z";
// Закончить рисование в точке (cx,cy)
// Это XML_элемент, поэтому значения всех атрибутов должны
// устанавливаться с помощью setAttribute(). Здесь нельзя
// использовать свойства JavaScript
path.setAttribute("d", d);
// Установить этот
путь
path.setAttribute("fill", colors[i]);
// Установить цвет
сектора
path.setAttribute("stroke", "black");
// Рамка сектора _
черная
path.setAttribute("stroke_width", "2"); //
2 единицы толщиной
canvas.appendChild(path);
//
Вставить сектор
в "холст"
// Следующий сектор начинается в точке, где закончился предыдущий startangle = endangle;
// Нарисовать маленький квадрат для идентификации сектора в легенде var icon = document.createElementNS(SVG.ns, "rect");
icon.setAttribute("x",
lx);
//
Координаты квадрата
icon.setAttribute("y",
ly +
30*i);
icon.setAttribute("width", 20);
//
Размер квадрата
icon.setAttribute("height",
20);
icon.setAttribute("fill",
colors[i]);
// Тем же цветом,
что и сектор
icon.setAttribute("stroke", "black");
// Такая же
рамка
icon.setAttribute("stroke_width", "2");
canvas.appendChild(icon);
// Добавить
в "холст"
// Добавить
метку правее квадрата
var label =
document.createElementNS(SVG.ns, "text");
label.setAttribute("x",
lx
+
30);
//
Координаты текста
label.setAttribute("y",
ly
+
30*i
+ 18);
// Стиль текста можно было определить посредством таблицы CSS_стилей label.setAttribute("font_family", "sans_serif"); label.setAttribute("font_size", "16");
// Добавить текстовый DOM_узел в элемент <svg:text> label.appendChild(document.createTextNode(labels[i]));
canvas.appendChild(label); // Добавить текст в "холст"
}
};
Программный код из примера 22.8 достаточно прост. Здесь выполняются некото_ рые математические расчеты для преобразования исходных данных в углы секто_ ров круговой диаграммы. Однако основную часть примера составляет программ_ ный код, создающий SVG_элементы и выполняющий настройку атрибутов этих элементов. Обратите внимание: поскольку формат SVG задействует пространства имен, вместо метода createElement() используется метод createElementNS(). Кон_ станта SVG.ns с именем пространства имен определена в примере 22.9.
Самая малопонятная часть этого примера – код, выполняющий рисование секто_ ра диаграммы. Для отображения каждого сектора используется тег <svg:path>. Этот SVG_элемент описывает рисование произвольных фигур, состоящих из ли_ ний и кривых. Описание фигуры вставляется в тег <svg:path> в виде значения ат_ рибута d. Основу описания составляет компактная грамматика символьных ко_ дов и чисел, определяющих координаты, углы и прочие значения. Например, символ M означает «move to» (переместиться в точку), и вслед за ним должны следовать координаты X и Y точки. Символ L означает «line to» (рисовать линию до точки); он рисует линию от текущей точки до точки с координатами, которые
22.3. SVG – масштабируемая векторная графика
следуют далее. Кроме того, в этом примере используется символьный код A, ко_ торый рисует дугу (arc). Вслед за этим символом следуют семь чисел, описываю_ щих дугу. Точное описание нас здесь не интересует, но вы можете найти его в спецификации, по адресу http://www.w3.org/TR/SVG/.
В примере 22.8 использована константа SVG.ns, которая описывает пространство имен SVG. Эта константа и ряд вспомогательных функций определены в виде от_ дельного файла SVG.js, содержимое которого приводится в примере 22.9.
Пример 22.9. Вспомогательный программный SVG'код
// Создать пространство имен для вспомогательных функций var SVG = {};
// Эти URL_адреса определяют пространства имен, связанные с SVG SVG.ns = "http://www.w3.org/2000/svg";
SVG.xlinkns = "http://www.w3.org/1999/xlink";
// Создает и возвращает пустой элемент <svg>.
// Обратите внимание: элемент не добавляется в документ.
// Кроме того, можно определить размеры изображения в пикселах,
// Размер "холста" в пикселах svg.setAttribute("width", pixelWidth); svg.setAttribute("height", pixelHeight);
// Установить координаты, которые будут использоваться при рисовании svg.setAttribute("viewBox", "0 0 " + userWidth + " " + userHeight);
// Определить пространство имен XLink, которое используется SVG svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink",
SVG.xlinkns);
return svg;
};
// Сериализовать элемент "холста" в строку и использовать эту строку
// в спецификаторе data: URL_адреса для отображения тега <object>.
// Это позволит SVG работать в броузерах, которые поддерживают URL_адреса
// со спецификатором data: и в которых установлен модуль SVG. SVG.makeDataURL = function(canvas) {
// Мы не будем беспокоиться о вопросах сериализации в IE, поскольку
// этот броузер не поддерживает URL_адреса со спецификатором data: var text = (new XMLSerializer()).serializeToString(canvas);
var encodedText = encodeURIComponent(text);
return "data:image/svg+xml," + encodedText;
};
// Создает тег <object> для вывода SVG_рисунка с помощью
// URL_адреса со спецификатором data:
SVG.makeObjectTag
= function(canvas, width, height) {
var object = document.createElement("object"); //
Создать тег <object>
object.width = width;
//
Установить размеры
object.height
= height;
object.data =
SVG.makeDataURL(canvas);
//
SVG_изображение как URL_адрес
//
со спецификатором data:
Глава 22. Работа с графикой на стороне клиента
object.type = "image/svg+xml"
// MIME_тип для SVG
return object;
}
Наиболее важной в этом примере является функция SVG.makeCanvas(). Она с помо_ щью DOM_методов создает элемент <svg>, который затем используется в качестве «холста» для рисования SVG_графики. Функция makeCanvas() позволяет опреде_ лить размеры выводимого SVG_изображения (в пикселах), а также размеры внутренней системы координат (или «пользовательское пространство»), которая потребуется в процессе рисования. (Например, когда пользовательское простран_ ство с размерами 1000× 1000 отображается в квадрате 250× 250, каждому элемен_ ту пользовательского пространства соответствует одна четвертая пиксела.) Функ_ ция createCanvas() создает и возвращает тег <svg>, но не вставляет его в документ. Сделать это должен вызывающий программный код.
Другие две вспомогательные функции из примера 22.9 используются для выво_ да SVG_графики с помощью модулей расширения в броузерах. Функция SVG.makeDataURL() сериализует XML_текст тега <svg> и преобразует его в URL_ад_ рес со спецификатором data:. Функция SVG.makeObjectTag() идет еще дальше – она создает тег <object> для встраивания SVG_графики, а затем вызывает функ_ цию SVG.makeDataURL(), возвращаемое значение которой записывается в атрибут data этого тега. Подобно функции SVG.makeCanvas(), метод SVG.makeObjectTag() воз_ вращает тег <object>, и точно так же не вставляет его в документ.
Чтобы метод SVG.makeObjectTag() мог работать, броузер должен поддерживать URL_адреса со спецификатором data: (как Firefox 1.0) и DOM_методы, распо_ знающие пространства имен (как document.createElementNS()), а также иметь ус_ тановленный модуль расширения для просмотра SVG_графики. Обратите внима_ ние: эти методы не будут работать в IE, потому что IE не поддерживает ни URL_ адреса со спецификатором data:, ни метод createElementNS(). Для создания SVG_ изображения в IE вместо обращений к DOM_методам можно использовать мето_ ды манипулирования строками, чтобы собрать SVG_документ. После этого гра_ фика может быть преобразована в URL_адрес со спецификатором javascript: вместо data:.
Я закончу этот раздел содержимым HTML_файла, который объединяет функцию pieChart() из примера 22.8 и вспомогательные SVG_методы из примера 22.9. Следующий фрагмент создает «холст» SVG, рисует на нем диаграмму и затем дважды вставляет «холст» в документ – один раз непосредственно и второй раз в виде тега <object>:
<script src="SVG.js"></script> <!__ Вспомогательные методы __>
<script src="svgpiechart.js"></script> <!__ Методы рисования диаграммы __> <script>
function init() {
// Создать тег <svg> для рисования с разрешением 600x400 и выводов в 300x200 пикселов var canvas = SVG.makeCanvas("canvas", 300, 200, 600, 400);