RaphaëlJS, codage sans douleur, et Stinky navigateurs (IE, la plupart du temps)
Question
Vous avez besoin d'un conseil sur la meilleure approche à ce problème:
Je suis tombé follement amoureux de RaphaëlJS , comme elle l'a fait réalisable SVG pour moi et mon codage depuis il a réussi à mettre IE dans le pli.
Cependant, je n'aime pas avoir à inclure un fichier .js
pour chaque graphique SVG Je veux rendre sur la page.
Alors, je poussé autour sur Internet pour voir si je pouvais trouver quelque chose qui était plus « dynamique », et je trouve ceci: (le ci-dessous est que je trouve ici ma version modifiée du code: http://groups.google.com/group/raphaeljs/msg/ce59df3d01736a6f )
function parseXML(xml) {
if (window.ActiveXObject && window.GetObject) {
var dom = new ActiveXObject('Microsoft.XMLDOM');
dom.loadXML(xml);
return dom;
}
if (window.DOMParser) {
return new DOMParser().parseFromString(xml, 'text/xml');
throw new Error('No XML parser available');
}
}
(function($) {
$.fn.render_raphaels = function(options) {
var defaults, options, counter, img_path, doc, root, vb, dims, img, node, path, atts, container, new_svg, inline;
defaults = {};
options = $.extend(defaults, options);
counter = 0;
inline = false;
// find all the img's that point to SVGs
$('img[src*="\.svg"]').each(function() {
$(this).fadeOut(1000);
img_path = $(this).attr('src');
if (!$(this).attr('id')) new_svg = true;
if ($(this).hasClass('inline')) inline = true;
container = jQuery('<div/>', {
id: $(this).attr('id') ? $(this).attr('id') : 'svg-' + (counter + 1),
'class': $(this).attr('class') ? $(this).attr('class') : 'svg'
}).hide().insertBefore($(this));
$.get(img_path, null, function(doc) {
doc = parseXML(doc);
root = $(doc).find('svg')[0];
dims = [root.getAttribute('width'), root.getAttribute('height')];
if(new_svg) container.css({ width: dims[0], height: dims[1] });
if(inline) container.css('display', 'inline-block');
img = Raphael(container.attr('id'), parseInt(dims[0]), parseInt(dims[1]));
$(root).find('path').each(function() {
node = this;
path = img.path($(this).attr('d'));
$(['stroke-linejoin','stroke','stroke-miterlimit','stroke-width','fill','stroke-linecap']).each(function() {
if($(node).attr(this.toString())) {
path.attr(this, $(node).attr(this.toString()));
} else {
path.attr(this, 0);
}
});
if($(node).attr('style')) {
atts = $(node).attr('style').split(';');
for(var i=0; i < atts.length; i++) {
bits = atts[i].split(':');
path.attr(bits[0],bits[1]);
}
}
});
}, 'text');
$(this).remove(); // removes the original image after the new one has been redrawn
container.fadeIn(2000);
});
};
})(jQuery);
En substance, cela me permet d'écrire juste une balise image normale avec le graphique .svg
, et le plugin jQuery remplacera automatiquement avec la version rendue Raphaël.
Cela fonctionne très bien dans les navigateurs non compatibles avec SVG, comme IE, mais dans les navigateurs modernes qui prennent en charge effectivement déjà graphiques SVG, la balise d'image fonctionne comme-est (sans Raphaël), de sorte que lorsque les charges Raphaël, il décharge l'image existante, puis se fane dans la version Raphaël ... créant essentiellement un scintillement. J'ai essayé de minimiser l'importance de ce par la décoloration dans la nouvelle version, mais je suis toujours confronté au problème que l'ancien montre, est caché, puis réapparaît.
Je besoin d'un moyen de concilier le comportement souhaité dans les navigateurs IE et problématiques comme le comportement indésirable dans les navigateurs modernes, conformes aux standards tels Safari 4 et Firefox 3. Mais, je veux faire cela d'une manière que je don « t doivent changer de manière significative le code I façon (pourquoi j'utilise le plug-in en premier lieu).
Je sais que SVG est encore un peu d'avant-garde, mais que quelqu'un a des idées sur la façon dont je peux contourner cela?
AVERTISSEMENT:. Si possible, je voudrais rester loin de ciblage des navigateurs ... Je suis à la recherche d'une solution de workflow gérable et fonctionnel, pas un hack navigateur
DISCLAIMER 2ND: Je ne veux pas une solution basée sur Flash; Je veux être comme « native » que possible, et je considère javascript pour être beaucoup plus que Flash. (Voilà pourquoi je suis tellement excité à propos de Raphaël, parce que je peux rester loin de Flash).
La solution 2
Droit, je suis venu avec cette ... (en utilisant Ruby on Rails, btw)
- Exporter graphiques SVG à
public/images/svg/
- Exécuter
rake svg:parse:to_json
(ma source de tâche Rake ci-dessous) - Redessiner le graphique Raphaël en utilisant les données de chemin JSON.
- Appelez mon graphique SVG en utilisant seulement
<span class="svg">name_of_svg_file</span>
(cachant la durée par défaut à l'aide CSS et redessiner en utilisant le plugin jQuery modifié ci-dessous).
Cela signifie que mon flux de travail d'Illustrator vers HTML est très propre (je mon feu de tâche râteau pendant mon Capistrano déploiement). Cela répond à mes besoins décrits dans ma question que cela rend aussi rapidement et sans scintillement dans presque tous les navigateurs, je l'ai testé dans la mesure (que Raphaël soutient), ne nécessite pas Flash, et consiste à écrire une seule ligne de code par SVG graphique (la balise <span>
avec le nom du graphique).
Refactorings bienvenue.
ToDo: En ce moment, si l'utilisateur a le javascript tourné off , il n'y a pas de remplacement. Je pense que d'un simple bloc de <noscript>
pourrait me aider là-bas ...
parse_svg.rake
require 'hpricot' # >= 0.8.2
require 'json/pure'
namespace :svg do
namespace :parse do
desc "Parse all SVG graphics in '/public/images/svg' into JSON libraries in '/public/javascripts/raphael/svg-js/' usable by Raphaël"
task :to_json do
FileList['public/images/svg/*.svg'].each do |svg|
name = File.basename(svg).split('.')[0]
doc = open(svg) { |f| Hpricot.XML(f) } # parse the SVG
js = {} # container
js[:name] = name
js[:width] = doc.at('svg')['width'].to_i
js[:height] = doc.at('svg')['height'].to_i
js[:paths] = [] # all paths
doc.search("/svg/g//path").each do |p|
path = {} # our path hash
path[:path] = p['d'].gsub(/(?:[\r\n\t])+/, ',')
path[:stroke_width] = p['stroke-width'] || 0
path[:stroke] = p['stroke'] || 0
path[:fill] = p['fill'] || 0
js[:paths] << path
end
File.open("public/javascripts/raphael/svg-js/#{name}.js", 'w') { |f| f.write(js.to_json) }
end
puts "Done!"
end
end
end
render_raphaels.jquery.js
(function($) {
$.fn.render_raphaels = function(options) {
var defaults, options, name, container, raphael;
defaults = {
span_id: 'svg-ref'
};
options = $.extend(defaults, options);
// find all the spans that point to SVGs, based on default or passed-in identifier
$('span.'+options.span_id).each(function() {
name = $(this).text();
$.getJSON('/javascripts/raphael/svg-js/' + name + '.js', function(data) {
paper = Raphael(document.getElementById(data.name), data.width, data.height);
for (var p in data.paths) {
paper.path(data.paths[p].path).attr({
fill: data.paths[p].fill,
stroke: data.paths[p].stroke,
'stroke-width': data.paths[p].stroke_width
});
}
});
// remove the span
$(this).remove();
});
};
})(jQuery);
appel à toutes les pages avec SVGs
$.fn.render_raphaels({ span_id: 'svg' });
Autres conseils
Avez-vous essayé svgweb ?
S'il n'y a pas de support natif, un plug-in flash sera déclenché pour rendre le SVG.