756
|
1 /* http://keith-wood.name/svg.html
|
|
2 SVG attribute animations for jQuery v1.4.3.
|
|
3 Written by Keith Wood (kbwood{at}iinet.com.au) June 2008.
|
|
4 Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and
|
|
5 MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses.
|
|
6 Please attribute the author if you use it. */
|
|
7
|
|
8 (function($) { // Hide scope, no $ conflict
|
|
9
|
|
10 // Enable animation for all of these SVG numeric attributes -
|
|
11 // named as svg-* or svg* (with first character upper case)
|
|
12 $.each(['x', 'y', 'width', 'height', 'rx', 'ry', 'cx', 'cy', 'r', 'x1', 'y1', 'x2', 'y2',
|
|
13 'stroke-width', 'strokeWidth', 'opacity', 'fill-opacity', 'fillOpacity',
|
|
14 'stroke-opacity', 'strokeOpacity', 'font-size', 'fontSize'],
|
|
15 function(i, attrName) {
|
|
16 var ccName = attrName.charAt(0).toUpperCase() + attrName.substr(1);
|
|
17 $.fx.step['svg' + ccName] = $.fx.step['svg-' + attrName] = function(fx) {
|
|
18 var realAttrName = $.svg._attrNames[attrName] || attrName;
|
|
19 var attr = fx.elem.attributes.getNamedItem(realAttrName);
|
|
20 if (!fx.set) {
|
|
21 fx.start = (attr ? parseFloat(attr.nodeValue) : 0);
|
|
22 var offset = fx.options.curAnim['svg-' + attrName] ||
|
|
23 fx.options.curAnim['svg' + ccName];
|
|
24 if (/^[+-]=/.exec(offset)) {
|
|
25 fx.end = fx.start + parseFloat(offset.replace(/=/, ''));
|
|
26 }
|
|
27 $(fx.elem).css(realAttrName, '');
|
|
28 fx.set = true;
|
|
29 }
|
|
30 var value = (fx.pos * (fx.end - fx.start) + fx.start) + (fx.unit == '%' ? '%' : '');
|
|
31 (attr ? attr.nodeValue = value : fx.elem.setAttribute(realAttrName, value));
|
|
32 };
|
|
33 }
|
|
34 );
|
|
35
|
|
36 // Enable animation for the SVG viewBox attribute
|
|
37 $.fx.step['svgViewBox'] = $.fx.step['svg-viewBox'] = function(fx) {
|
|
38 var attr = fx.elem.attributes.getNamedItem('viewBox');
|
|
39 if (!fx.set) {
|
|
40 fx.start = parseViewBox(attr ? attr.nodeValue : '');
|
|
41 var offset = fx.options.curAnim['svg-viewBox'] || fx.options.curAnim['svgViewBox'];
|
|
42 fx.end = parseViewBox(offset);
|
|
43 if (/^[+-]=/.exec(offset)) {
|
|
44 offset = offset.split(' ');
|
|
45 while (offset.length < 4) {
|
|
46 offset.push('0');
|
|
47 }
|
|
48 for (var i = 0; i < 4; i++) {
|
|
49 if (/^[+-]=/.exec(offset[i])) {
|
|
50 fx.end[i] = fx.start[i] + parseFloat(offset[i].replace(/=/, ''));
|
|
51 }
|
|
52 }
|
|
53 }
|
|
54 fx.set = true;
|
|
55 }
|
|
56 var value = $.map(fx.start, function(n, i) {
|
|
57 return (fx.pos * (fx.end[i] - n) + n);
|
|
58 }).join(' ');
|
|
59 (attr ? attr.nodeValue = value : fx.elem.setAttribute('viewBox', value));
|
|
60 };
|
|
61
|
|
62 /* Parse a viewBox definition: x, y, width, height.
|
|
63 @param value (string) the definition
|
|
64 @return (number[4]) the extracted values */
|
|
65 function parseViewBox(value) {
|
|
66 var viewBox = value.split(' ');
|
|
67 for (var i = 0; i < viewBox.length; i++) {
|
|
68 viewBox[i] = parseFloat(viewBox[i]);
|
|
69 if (isNaN(viewBox[i])) {
|
|
70 viewBox[i] = 0;
|
|
71 }
|
|
72 }
|
|
73 while (viewBox.length < 4) {
|
|
74 viewBox.push(0);
|
|
75 }
|
|
76 return viewBox;
|
|
77 }
|
|
78
|
|
79 // Enable animation for the SVG transform attribute
|
|
80 $.fx.step['svgTransform'] = $.fx.step['svg-transform'] = function(fx) {
|
|
81 var attr = fx.elem.attributes.getNamedItem('transform');
|
|
82 if (!fx.set) {
|
|
83 fx.start = parseTransform(attr ? attr.nodeValue : '');
|
|
84 fx.end = parseTransform(fx.end, fx.start);
|
|
85 fx.set = true;
|
|
86 }
|
|
87 var transform = '';
|
|
88 for (var i = 0; i < fx.end.order.length; i++) {
|
|
89 switch (fx.end.order.charAt(i)) {
|
|
90 case 't':
|
|
91 transform += (fx.start.translateX != fx.end.translateX || fx.start.translateY != fx.end.translateY ?
|
|
92 ' translate(' + (fx.pos * (fx.end.translateX - fx.start.translateX) + fx.start.translateX) + ',' +
|
|
93 (fx.pos * (fx.end.translateY - fx.start.translateY) + fx.start.translateY) + ')' : '');
|
|
94 break;
|
|
95 case 's':
|
|
96 transform += (fx.start.scaleX != fx.end.scaleX || fx.start.scaleY != fx.end.scaleY ?
|
|
97 ' scale(' + (fx.pos * (fx.end.scaleX - fx.start.scaleX) + fx.start.scaleX) + ',' +
|
|
98 (fx.pos * (fx.end.scaleY - fx.start.scaleY) + fx.start.scaleY) + ')' : '');
|
|
99 break;
|
|
100 case 'r':
|
|
101 transform += (fx.start.rotateA != fx.end.rotateA ||
|
|
102 fx.start.rotateX != fx.end.rotateX || fx.start.rotateY != fx.end.rotateY ?
|
|
103 ' rotate(' + (fx.pos * (fx.end.rotateA - fx.start.rotateA) + fx.start.rotateA) + ',' +
|
|
104 (fx.pos * (fx.end.rotateX - fx.start.rotateX) + fx.start.rotateX) + ',' +
|
|
105 (fx.pos * (fx.end.rotateY - fx.start.rotateY) + fx.start.rotateY) + ')' : '');
|
|
106 break;
|
|
107 case 'x':
|
|
108 transform += (fx.start.skewX != fx.end.skewX ?
|
|
109 ' skewX(' + (fx.pos * (fx.end.skewX - fx.start.skewX) + fx.start.skewX) + ')' : '');
|
|
110 case 'y':
|
|
111 transform += (fx.start.skewY != fx.end.skewY ?
|
|
112 ' skewY(' + (fx.pos * (fx.end.skewY - fx.start.skewY) + fx.start.skewY) + ')' : '');
|
|
113 break;
|
|
114 case 'm':
|
|
115 var matrix = '';
|
|
116 for (var j = 0; j < 6; j++) {
|
|
117 matrix += ',' + (fx.pos * (fx.end.matrix[j] - fx.start.matrix[j]) + fx.start.matrix[j]);
|
|
118 }
|
|
119 transform += ' matrix(' + matrix.substr(1) + ')';
|
|
120 break;
|
|
121 }
|
|
122 }
|
|
123 (attr ? attr.nodeValue = transform : fx.elem.setAttribute('transform', transform));
|
|
124 };
|
|
125
|
|
126 /* Decode a transform string and extract component values.
|
|
127 @param value (string) the transform string to parse
|
|
128 @param original (object) the settings from the original node
|
|
129 @return (object) the combined transformation attributes */
|
|
130 function parseTransform(value, original) {
|
|
131 value = value || '';
|
|
132 if (typeof value == 'object') {
|
|
133 value = value.nodeValue;
|
|
134 }
|
|
135 var transform = $.extend({translateX: 0, translateY: 0, scaleX: 0, scaleY: 0,
|
|
136 rotateA: 0, rotateX: 0, rotateY: 0, skewX: 0, skewY: 0,
|
|
137 matrix: [0, 0, 0, 0, 0, 0]}, original || {});
|
|
138 transform.order = '';
|
|
139 var pattern = /([a-zA-Z]+)\(\s*([+-]?[\d\.]+)\s*(?:[\s,]\s*([+-]?[\d\.]+)\s*(?:[\s,]\s*([+-]?[\d\.]+)\s*(?:[\s,]\s*([+-]?[\d\.]+)\s*[\s,]\s*([+-]?[\d\.]+)\s*[\s,]\s*([+-]?[\d\.]+)\s*)?)?)?\)/g;
|
|
140 var result = pattern.exec(value);
|
|
141 while (result) {
|
|
142 switch (result[1]) {
|
|
143 case 'translate':
|
|
144 transform.order += 't';
|
|
145 transform.translateX = parseFloat(result[2]);
|
|
146 transform.translateY = (result[3] ? parseFloat(result[3]) : 0);
|
|
147 break;
|
|
148 case 'scale':
|
|
149 transform.order += 's';
|
|
150 transform.scaleX = parseFloat(result[2]);
|
|
151 transform.scaleY = (result[3] ? parseFloat(result[3]) : transform.scaleX);
|
|
152 break;
|
|
153 case 'rotate':
|
|
154 transform.order += 'r';
|
|
155 transform.rotateA = parseFloat(result[2]);
|
|
156 transform.rotateX = (result[3] ? parseFloat(result[3]) : 0);
|
|
157 transform.rotateY = (result[4] ? parseFloat(result[4]) : 0);
|
|
158 break;
|
|
159 case 'skewX':
|
|
160 transform.order += 'x';
|
|
161 transform.skewX = parseFloat(result[2]);
|
|
162 break;
|
|
163 case 'skewY':
|
|
164 transform.order += 'y';
|
|
165 transform.skewY = parseFloat(result[2]);
|
|
166 break;
|
|
167 case 'matrix':
|
|
168 transform.order += 'm';
|
|
169 transform.matrix = [parseFloat(result[2]), parseFloat(result[3]),
|
|
170 parseFloat(result[4]), parseFloat(result[5]),
|
|
171 parseFloat(result[6]), parseFloat(result[7])];
|
|
172 break;
|
|
173 }
|
|
174 result = pattern.exec(value);
|
|
175 }
|
|
176 return transform;
|
|
177 }
|
|
178
|
|
179 // Enable animation for all of these SVG colour properties - based on jquery.color.js
|
|
180 $.each(['fill', 'stroke'],
|
|
181 function(i, attrName) {
|
|
182 var ccName = attrName.charAt(0).toUpperCase() + attrName.substr(1);
|
|
183 $.fx.step['svg' + ccName] = $.fx.step['svg-' + attrName] = function(fx) {
|
|
184 if (!fx.set) {
|
|
185 fx.start = getColour(fx.elem, attrName);
|
|
186 var toNone = (fx.end == 'none');
|
|
187 fx.end = (toNone ? getColour(fx.elem.parentNode, attrName) : getRGB(fx.end));
|
|
188 fx.end[3] = toNone;
|
|
189 $(fx.elem).css(attrName, '');
|
|
190 fx.set = true;
|
|
191 }
|
|
192 var attr = fx.elem.attributes.getNamedItem(attrName);
|
|
193 var colour = 'rgb(' + [
|
|
194 Math.min(Math.max(parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0], 10), 0), 255),
|
|
195 Math.min(Math.max(parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1], 10), 0), 255),
|
|
196 Math.min(Math.max(parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2], 10), 0), 255)
|
|
197 ].join(',') + ')';
|
|
198 colour = (fx.end[3] && fx.state == 1 ? 'none' : colour);
|
|
199 (attr ? attr.nodeValue = colour : fx.elem.setAttribute(attrName, colour));
|
|
200 }
|
|
201 }
|
|
202 );
|
|
203
|
|
204 /* Find this attribute value somewhere up the node hierarchy.
|
|
205 @param elem (element) the starting element to find the attribute
|
|
206 @param attr (string) the attribute name
|
|
207 @return (number[3]) RGB components for the attribute colour */
|
|
208 function getColour(elem, attr) {
|
|
209 var colour;
|
|
210 do {
|
|
211 colour = (elem.attributes && elem.attributes.getNamedItem(attr) ?
|
|
212 elem.attributes.getNamedItem(attr).nodeValue : '');
|
|
213 // Keep going until we find an element that has colour, or exit SVG
|
|
214 if ((colour != '' && colour != 'none') || $(elem).hasClass('hasSVG')) {
|
|
215 break;
|
|
216 }
|
|
217 } while (elem = elem.parentNode);
|
|
218 return getRGB(colour);
|
|
219 }
|
|
220
|
|
221 /* Parse strings looking for common colour formats.
|
|
222 @param colour (string) colour description to parse
|
|
223 @return (number[3]) RGB components of this colour */
|
|
224 function getRGB(colour) {
|
|
225 var result;
|
|
226 // Check if we're already dealing with an array of colors
|
|
227 if (colour && colour.constructor == Array && (colour.length == 3 || colour.length == 4)) {
|
|
228 return colour;
|
|
229 }
|
|
230 // Look for rgb(num,num,num)
|
|
231 if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(colour)) {
|
|
232 return [parseInt(result[1], 10), parseInt(result[2], 10), parseInt(result[3], 10)];
|
|
233 }
|
|
234 // Look for rgb(num%,num%,num%)
|
|
235 if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(colour)) {
|
|
236 return [parseFloat(result[1]) * 2.55, parseFloat(result[2]) * 2.55,
|
|
237 parseFloat(result[3]) * 2.55];
|
|
238 }
|
|
239 // Look for #a0b1c2
|
|
240 if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(colour)) {
|
|
241 return [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)];
|
|
242 }
|
|
243 // Look for #abc
|
|
244 if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(colour)) {
|
|
245 return [parseInt(result[1] + result[1], 16), parseInt(result[2] + result[2], 16),
|
|
246 parseInt(result[3] + result[3], 16)];
|
|
247 }
|
|
248 // Otherwise, we're most likely dealing with a named color
|
|
249 return colours[$.trim(colour).toLowerCase()] || colours['none'];
|
|
250 }
|
|
251
|
|
252 // The SVG named colours
|
|
253 var colours = {
|
|
254 '': [255, 255, 255, 1],
|
|
255 none: [255, 255, 255, 1],
|
|
256 aliceblue: [240, 248, 255],
|
|
257 antiquewhite: [250, 235, 215],
|
|
258 aqua: [ 0, 255, 255],
|
|
259 aquamarine: [127, 255, 212],
|
|
260 azure: [240, 255, 255],
|
|
261 beige: [245, 245, 220],
|
|
262 bisque: [255, 228, 196],
|
|
263 black: [ 0, 0, 0],
|
|
264 blanchedalmond: [255, 235, 205],
|
|
265 blue: [ 0, 0, 255],
|
|
266 blueviolet: [138, 43, 226],
|
|
267 brown: [165, 42, 42],
|
|
268 burlywood: [222, 184, 135],
|
|
269 cadetblue: [ 95, 158, 160],
|
|
270 chartreuse: [127, 255, 0],
|
|
271 chocolate: [210, 105, 30],
|
|
272 coral: [255, 127, 80],
|
|
273 cornflowerblue: [100, 149, 237],
|
|
274 cornsilk: [255, 248, 220],
|
|
275 crimson: [220, 20, 60],
|
|
276 cyan: [ 0, 255, 255],
|
|
277 darkblue: [ 0, 0, 139],
|
|
278 darkcyan: [ 0, 139, 139],
|
|
279 darkgoldenrod: [184, 134, 11],
|
|
280 darkgray: [169, 169, 169],
|
|
281 darkgreen: [ 0, 100, 0],
|
|
282 darkgrey: [169, 169, 169],
|
|
283 darkkhaki: [189, 183, 107],
|
|
284 darkmagenta: [139, 0, 139],
|
|
285 darkolivegreen: [ 85, 107, 47],
|
|
286 darkorange: [255, 140, 0],
|
|
287 darkorchid: [153, 50, 204],
|
|
288 darkred: [139, 0, 0],
|
|
289 darksalmon: [233, 150, 122],
|
|
290 darkseagreen: [143, 188, 143],
|
|
291 darkslateblue: [ 72, 61, 139],
|
|
292 darkslategray: [ 47, 79, 79],
|
|
293 darkslategrey: [ 47, 79, 79],
|
|
294 darkturquoise: [ 0, 206, 209],
|
|
295 darkviolet: [148, 0, 211],
|
|
296 deeppink: [255, 20, 147],
|
|
297 deepskyblue: [ 0, 191, 255],
|
|
298 dimgray: [105, 105, 105],
|
|
299 dimgrey: [105, 105, 105],
|
|
300 dodgerblue: [ 30, 144, 255],
|
|
301 firebrick: [178, 34, 34],
|
|
302 floralwhite: [255, 250, 240],
|
|
303 forestgreen: [ 34, 139, 34],
|
|
304 fuchsia: [255, 0, 255],
|
|
305 gainsboro: [220, 220, 220],
|
|
306 ghostwhite: [248, 248, 255],
|
|
307 gold: [255, 215, 0],
|
|
308 goldenrod: [218, 165, 32],
|
|
309 gray: [128, 128, 128],
|
|
310 grey: [128, 128, 128],
|
|
311 green: [ 0, 128, 0],
|
|
312 greenyellow: [173, 255, 47],
|
|
313 honeydew: [240, 255, 240],
|
|
314 hotpink: [255, 105, 180],
|
|
315 indianred: [205, 92, 92],
|
|
316 indigo: [ 75, 0, 130],
|
|
317 ivory: [255, 255, 240],
|
|
318 khaki: [240, 230, 140],
|
|
319 lavender: [230, 230, 250],
|
|
320 lavenderblush: [255, 240, 245],
|
|
321 lawngreen: [124, 252, 0],
|
|
322 lemonchiffon: [255, 250, 205],
|
|
323 lightblue: [173, 216, 230],
|
|
324 lightcoral: [240, 128, 128],
|
|
325 lightcyan: [224, 255, 255],
|
|
326 lightgoldenrodyellow: [250, 250, 210],
|
|
327 lightgray: [211, 211, 211],
|
|
328 lightgreen: [144, 238, 144],
|
|
329 lightgrey: [211, 211, 211],
|
|
330 lightpink: [255, 182, 193],
|
|
331 lightsalmon: [255, 160, 122],
|
|
332 lightseagreen: [ 32, 178, 170],
|
|
333 lightskyblue: [135, 206, 250],
|
|
334 lightslategray: [119, 136, 153],
|
|
335 lightslategrey: [119, 136, 153],
|
|
336 lightsteelblue: [176, 196, 222],
|
|
337 lightyellow: [255, 255, 224],
|
|
338 lime: [ 0, 255, 0],
|
|
339 limegreen: [ 50, 205, 50],
|
|
340 linen: [250, 240, 230],
|
|
341 magenta: [255, 0, 255],
|
|
342 maroon: [128, 0, 0],
|
|
343 mediumaquamarine: [102, 205, 170],
|
|
344 mediumblue: [ 0, 0, 205],
|
|
345 mediumorchid: [186, 85, 211],
|
|
346 mediumpurple: [147, 112, 219],
|
|
347 mediumseagreen: [ 60, 179, 113],
|
|
348 mediumslateblue: [123, 104, 238],
|
|
349 mediumspringgreen: [ 0, 250, 154],
|
|
350 mediumturquoise: [ 72, 209, 204],
|
|
351 mediumvioletred: [199, 21, 133],
|
|
352 midnightblue: [ 25, 25, 112],
|
|
353 mintcream: [245, 255, 250],
|
|
354 mistyrose: [255, 228, 225],
|
|
355 moccasin: [255, 228, 181],
|
|
356 navajowhite: [255, 222, 173],
|
|
357 navy: [ 0, 0, 128],
|
|
358 oldlace: [253, 245, 230],
|
|
359 olive: [128, 128, 0],
|
|
360 olivedrab: [107, 142, 35],
|
|
361 orange: [255, 165, 0],
|
|
362 orangered: [255, 69, 0],
|
|
363 orchid: [218, 112, 214],
|
|
364 palegoldenrod: [238, 232, 170],
|
|
365 palegreen: [152, 251, 152],
|
|
366 paleturquoise: [175, 238, 238],
|
|
367 palevioletred: [219, 112, 147],
|
|
368 papayawhip: [255, 239, 213],
|
|
369 peachpuff: [255, 218, 185],
|
|
370 peru: [205, 133, 63],
|
|
371 pink: [255, 192, 203],
|
|
372 plum: [221, 160, 221],
|
|
373 powderblue: [176, 224, 230],
|
|
374 purple: [128, 0, 128],
|
|
375 red: [255, 0, 0],
|
|
376 rosybrown: [188, 143, 143],
|
|
377 royalblue: [ 65, 105, 225],
|
|
378 saddlebrown: [139, 69, 19],
|
|
379 salmon: [250, 128, 114],
|
|
380 sandybrown: [244, 164, 96],
|
|
381 seagreen: [ 46, 139, 87],
|
|
382 seashell: [255, 245, 238],
|
|
383 sienna: [160, 82, 45],
|
|
384 silver: [192, 192, 192],
|
|
385 skyblue: [135, 206, 235],
|
|
386 slateblue: [106, 90, 205],
|
|
387 slategray: [112, 128, 144],
|
|
388 slategrey: [112, 128, 144],
|
|
389 snow: [255, 250, 250],
|
|
390 springgreen: [ 0, 255, 127],
|
|
391 steelblue: [ 70, 130, 180],
|
|
392 tan: [210, 180, 140],
|
|
393 teal: [ 0, 128, 128],
|
|
394 thistle: [216, 191, 216],
|
|
395 tomato: [255, 99, 71],
|
|
396 turquoise: [ 64, 224, 208],
|
|
397 violet: [238, 130, 238],
|
|
398 wheat: [245, 222, 179],
|
|
399 white: [255, 255, 255],
|
|
400 whitesmoke: [245, 245, 245],
|
|
401 yellow: [255, 255, 0],
|
|
402 yellowgreen: [154, 205, 50]
|
|
403 };
|
|
404
|
|
405 })(jQuery);
|