Ceci est un auto-Q & A d'un morceau de code pratique que je suis venu avec.
Actuellement, il n'existe pas de moyen facile d'intégrer une image SVG et d'avoir ensuite accès aux éléments SVG via CSS. Il existe différentes méthodes d'utilisation des frameworks JS SVG, mais elles sont excessivement compliquées si vous ne faites que créer une simple icône avec un état de substitution.
Voici donc ce que j’ai proposé, qui est de loin le moyen le plus simple d’utiliser des fichiers SVG sur un site Web. Il reprend son concept des premières méthodes de remplacement de texte en image, mais pour autant que je sache, cela n'a jamais été fait pour les SVG.
Ceci est la question:
Tout d'abord, utilisez une balise IMG dans votre code HTML pour incorporer un graphique SVG. J'ai utilisé Adobe Illustrator pour créer le graphique.
<img id="facebook-logo" class="svg social-link" src="/images/logo-facebook.svg"/>
Cela ressemble à la manière dont vous intégrez une image normale. Notez que vous devez définir l'IMG pour qu'il ait une classe de svg. La classe 'lien social' n'est qu'un exemple. L'identifiant n'est pas obligatoire, mais est utile.
Ensuite, utilisez ce code jQuery (dans un fichier séparé ou en ligne dans HEAD).
/*
* Replace all SVG images with inline SVG
*/
jQuery('img.svg').each(function(){
var $img = jQuery(this);
var imgID = $img.attr('id');
var imgClass = $img.attr('class');
var imgURL = $img.attr('src');
jQuery.get(imgURL, function(data) {
// Get the SVG tag, ignore the rest
var $svg = jQuery(data).find('svg');
// Add replaced image's ID to the new SVG
if(typeof imgID !== 'undefined') {
$svg = $svg.attr('id', imgID);
}
// Add replaced image's classes to the new SVG
if(typeof imgClass !== 'undefined') {
$svg = $svg.attr('class', imgClass+' replaced-svg');
}
// Remove any invalid XML tags as per http://validator.w3.org
$svg = $svg.removeAttr('xmlns:a');
// Replace image with new SVG
$img.replaceWith($svg);
}, 'xml');
});
Le code ci-dessus recherche tous les programmes d'implémentation avec la classe 'svg' et les remplace par le fichier SVG intégré du fichier lié. L’énorme avantage est que cela vous permet d’utiliser CSS pour changer la couleur du SVG maintenant, comme ceci:
svg:hover path {
fill: red;
}
Le code jQuery que j'ai écrit intègre également des ports sur l'ID et les classes d'origine. Donc, ce CSS fonctionne aussi:
#facebook-logo:hover path {
fill: red;
}
Ou:
.social-link:hover path {
fill: red;
}
Vous pouvez voir un exemple de ce travail ici: http://labs.funkhausdesign.com/examples/img-svg/img-to-svg.html
Nous avons une version plus compliquée qui inclut la mise en cache ici: https://github.com/funkhaus/style-guide/blob/master/template/js/site.js#L32-L90
Style
svg path {
fill: #000;
}
Scénario
$(document).ready(function() {
$('img[src$=".svg"]').each(function() {
var $img = jQuery(this);
var imgURL = $img.attr('src');
var attributes = $img.prop("attributes");
$.get(imgURL, function(data) {
// Get the SVG tag, ignore the rest
var $svg = jQuery(data).find('svg');
// Remove any invalid XML tags
$svg = $svg.removeAttr('xmlns:a');
// Loop through IMG attributes and apply on SVG
$.each(attributes, function() {
$svg.attr(this.name, this.value);
});
// Replace IMG with SVG
$img.replaceWith($svg);
}, 'xml');
});
});
Vous pouvez maintenant utiliser la propriété CSS filter
in la plupart des navigateurs modernes (y compris Edge, mais pas IE11). Cela fonctionne aussi bien sur les images SVG que sur d'autres éléments. Vous pouvez utiliser hue-rotate
ou invert
pour modifier les couleurs, bien qu'ils ne vous permettent pas de modifier différentes couleurs indépendamment. J'utilise la classe CSS suivante pour afficher une version "désactivée" d'une icône (où l'original est une image SVG avec une couleur saturée):
.disabled {
opacity: 0.4;
filter: grayscale(100%);
-webkit-filter: grayscale(100%);
}
Cela le rend gris clair dans la plupart des navigateurs. Dans IE (et probablement Opera Mini, que je n'ai pas encore testé), il est sensiblement atténué par la propriété opacity, qui a quand même une belle apparence même si elle n'est pas grise.
Voici un exemple avec quatre classes CSS différentes pour l'icône Twemoji bell: original (jaune), la classe "désactivée" ci-dessus, hue-rotate
(vert) et invert
(bleu).
.twa-bell {
background-image: url("https://twemoji.maxcdn.com/svg/1f514.svg");
display: inline-block;
background-repeat: no-repeat;
background-position: center center;
height: 3em;
width: 3em;
margin: 0 0.15em 0 0.3em;
vertical-align: -0.3em;
background-size: 3em 3em;
}
.grey-out {
opacity: 0.4;
filter: grayscale(100%);
-webkit-filter: grayscale(100%);
}
.hue-rotate {
filter: hue-rotate(90deg);
-webkit-filter: hue-rotate(90deg);
}
.invert {
filter: invert(100%);
-webkit-filter: invert(100%);
}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<span class="twa-bell"></span>
<span class="twa-bell grey-out"></span>
<span class="twa-bell hue-rotate"></span>
<span class="twa-bell invert"></span>
</body>
</html>
Sinon, vous pouvez utiliser CSS mask
, accordé support du navigateur n'est pas bon mais vous pouvez utiliser un repli
.frame {
background: blue;
-webkit-mask: url(image.svg) center / contain no-repeat;
}
Si vous pouvez inclure des fichiers (PHP include ou à inclure via votre CMS de choix) dans votre page, vous pouvez ajouter le code SVG et l'inclure dans votre page. Cela fonctionne comme si vous colliez le source SVG dans la page, mais rend le nettoyeur de balisage de page plus clair.
L'avantage est que vous pouvez cibler des parties de votre SVG via CSS pour le survol - aucun javascript n'est requis.
http://codepen.io/chriscoyier/pen/evcBu
Vous devez juste utiliser une règle CSS comme celle-ci:
#pathidorclass:hover { fill: #303 !important; }
Notez que le bit !important
est nécessaire pour remplacer la couleur de remplissage.
@Drew Baker a donné une excellente solution pour résoudre le problème. Le code fonctionne correctement. Cependant, ceux qui utilisent AngularJs peuvent trouver beaucoup de dépendance à jQuery. Par conséquent, j’ai pensé que c’était une bonne idée de coller pour les utilisateurs d’AngularJS un code suivant la solution de @Drew Baker.
AngularJs façon du même code
1. Html: utilisez la balise bellow dans votre fichier html:
<svg-image src="/icons/my.svg" class="any-class-you-wish"></svg-image>
2. Directive: ce sera la directive dont vous aurez besoin pour reconnaître la balise:
'use strict';
angular.module('myApp')
.directive('svgImage', ['$http', function($http) {
return {
restrict: 'E',
link: function(scope, element) {
var imgURL = element.attr('src');
// if you want to use ng-include, then
// instead of the above line write the bellow:
// var imgURL = element.attr('ng-include');
var request = $http.get(
imgURL,
{'Content-Type': 'application/xml'}
);
scope.manipulateImgNode = function(data, elem){
var $svg = angular.element(data)[4];
var imgClass = elem.attr('class');
if(typeof(imgClass) !== 'undefined') {
var classes = imgClass.split(' ');
for(var i = 0; i < classes.length; ++i){
$svg.classList.add(classes[i]);
}
}
$svg.removeAttribute('xmlns:a');
return $svg;
};
request.success(function(data){
element.replaceWith(scope.manipulateImgNode(data, element));
});
}
};
}]);
3. CSS:
.any-class-you-wish{
border: 1px solid red;
height: 300px;
width: 120px
}
4. Test unitaire avec karma-jasmine:
'use strict';
describe('Directive: svgImage', function() {
var $rootScope, $compile, element, scope, $httpBackend, apiUrl, data;
beforeEach(function() {
module('myApp');
inject(function($injector) {
$rootScope = $injector.get('$rootScope');
$compile = $injector.get('$compile');
$httpBackend = $injector.get('$httpBackend');
apiUrl = $injector.get('apiUrl');
});
scope = $rootScope.$new();
element = angular.element('<svg-image src="/icons/icon-man.svg" class="svg"></svg-image>');
element = $compile(element)(scope);
spyOn(scope, 'manipulateImgNode').andCallThrough();
$httpBackend.whenGET(apiUrl + 'me').respond(200, {});
data = '<?xml version="1.0" encoding="utf-8"?>' +
'<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->' +
'<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">' +
'<!-- Obj -->' +
'<!-- Obj -->' +
'<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"' +
'width="64px" height="64px" viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">' +
'<g>' +
'<path fill="#F4A902" d=""/>' +
'<path fill="#F4A902" d=""/>' +
'</g>' +
'</svg>';
$httpBackend.expectGET('/icons/icon-man.svg').respond(200, data);
});
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('should call manipulateImgNode atleast once', function () {
$httpBackend.flush();
expect(scope.manipulateImgNode.callCount).toBe(1);
});
it('should return correct result', function () {
$httpBackend.flush();
var result = scope.manipulateImgNode(data, element);
expect(result).toBeDefined();
});
it('should define classes', function () {
$httpBackend.flush();
var result = scope.manipulateImgNode(data, element);
var classList = ["svg"];
expect(result.classList[0]).toBe(classList[0]);
});
});
Je réalise que vous voulez accomplir cela avec CSS, mais rappelez-vous que c'est une petite image simple. Vous pouvez toujours l'ouvrir dans Notepad ++ et changer le remplissage du chemin/de tout élément:
<path style="fill:#010002;" d="M394.854,205.444c9.218-15.461,19.102-30.181,14.258-49.527
...
C412.843,226.163,402.511,211.451,394.854,205.444z"/>
Cela pourrait économiser une tonne de scénario laid. Désolé si c'est hors de la base, mais parfois, les solutions simples peuvent être négligés.
... même en échangeant plusieurs images svg peut être de taille plus petite que certains extraits de code pour cette question.
J'ai écrit une directive pour résoudre ce problème avec AngularJS. Il est disponible ici - ngReusableSvg .
Il remplace l'élément SVG après son rendu et le place dans un élément div
, ce qui rend son code CSS facilement modifiable. Cela aide à utiliser le même fichier SVG à différents endroits en utilisant différentes tailles/couleurs.
L'utilisation est simple:
<object oa-reusable-svg
data="my_icon.svg"
type="image/svg+xml"
class="svg-class"
height="30" // given to prevent UI glitches at switch time
width="30">
</object>
Après cela, vous pouvez facilement avoir:
.svg-class svg {
fill: red; // whichever color you want
}
Voici un code non-framework, uniquement de js purs:
document.querySelectorAll('img.svg').forEach(function(element) {
var imgID = element.getAttribute('id')
var imgClass = element.getAttribute('class')
var imgURL = element.getAttribute('src')
xhr = new XMLHttpRequest()
xhr.onreadystatechange = function() {
if(xhr.readyState == 4 && xhr.status == 200) {
var svg = xhr.responseXML.getElementsByTagName('svg')[0];
if(imgID != null) {
svg.setAttribute('id', imgID);
}
if(imgClass != null) {
svg.setAttribute('class', imgClass + ' replaced-svg');
}
svg.removeAttribute('xmlns:a')
if(!svg.hasAttribute('viewBox') && svg.hasAttribute('height') && svg.hasAttribute('width')) {
svg.setAttribute('viewBox', '0 0 ' + svg.getAttribute('height') + ' ' + svg.getAttribute('width'))
}
element.parentElement.replaceChild(svg, element)
}
}
xhr.open('GET', imgURL, true)
xhr.send(null)
})
Voici une version pour knockout.js
basée sur la réponse acceptée:
Important: Il faut aussi que jQuery soit remplacé, mais j’ai pensé que cela pourrait être utile à certains.
ko.bindingHandlers.svgConvert =
{
'init': function ()
{
return { 'controlsDescendantBindings': true };
},
'update': function (element, valueAccessor, allBindings, viewModel, bindingContext)
{
var $img = $(element);
var imgID = $img.attr('id');
var imgClass = $img.attr('class');
var imgURL = $img.attr('src');
$.get(imgURL, function (data)
{
// Get the SVG tag, ignore the rest
var $svg = $(data).find('svg');
// Add replaced image's ID to the new SVG
if (typeof imgID !== 'undefined')
{
$svg = $svg.attr('id', imgID);
}
// Add replaced image's classes to the new SVG
if (typeof imgClass !== 'undefined')
{
$svg = $svg.attr('class', imgClass + ' replaced-svg');
}
// Remove any invalid XML tags as per http://validator.w3.org
$svg = $svg.removeAttr('xmlns:a');
// Replace image with new SVG
$img.replaceWith($svg);
}, 'xml');
}
};
Ensuite, appliquez simplement data-bind="svgConvert: true"
à votre tag img.
Cette solution remplace complètement la balise img
par un SVG et toute liaison supplémentaire ne serait pas respectée.
Puisque SVG est fondamentalement du code, vous n'avez besoin que de contenu. J'ai utilisé PHP pour obtenir du contenu, mais vous pouvez utiliser ce que vous voulez.
<?php
$content = file_get_contents($pathToSVG);
?>
Ensuite, j'ai imprimé le contenu "tel quel" dans un conteneur div
<div class="fill-class"><?php echo $content;?></div>
Définir finalement la règle sur les enfants SVG du conteneur sur CSS
.fill-class > svg {
fill: orange;
}
J'ai obtenu ces résultats avec une icône de matériau SVG:
Il existe une bibliothèque open source appelée SVGInject qui utilise l'attribut onload
pour déclencher l'injection. Vous pouvez trouver le projet GitHub à https://github.com/iconfu/svg-inject
Voici un exemple minimal d'utilisation de SVGInject:
<html>
<head>
<script src="svg-inject.min.js"></script>
</head>
<body>
<img src="image.svg" onload="SVGInject(this)" />
</body>
</html>
Une fois l'image chargée, onload="SVGInject(this)
déclenchera l'injection et l'élément <img>
sera remplacé par le contenu du fichier SVG fourni dans l'attribut src
.
Il résout plusieurs problèmes avec l'injection de SVG:
Les SVG peuvent être masqués jusqu'à la fin de l'injection. Ceci est important si un style est déjà appliqué pendant le temps de chargement, ce qui provoquerait sinon un bref "flash de contenu non style".
Les éléments <img>
s’injectent automatiquement. Si vous ajoutez des fichiers SVG de manière dynamique, vous n'avez pas à vous inquiéter d'appeler à nouveau la fonction d'injection.
Une chaîne aléatoire est ajoutée à chaque ID du SVG pour éviter d'avoir le même ID plusieurs fois dans le document si un SVG est injecté plusieurs fois.
SVGInject est un script Javascript simple qui fonctionne avec tous les navigateurs prenant en charge SVG.
Disclaimer: Je suis co-auteur de SVGInject
Si nous avons un plus grand nombre d'images de ce type, nous pouvons également utiliser les fichiers de polices.
Des sites comme https://glyphter.com/ peuvent nous obtenir un fichier de police à partir de nos svgs.
Par exemple.
@font-face {
font-family: 'iconFont';
src: url('iconFont.eot');
}
#target{
color: white;
font-size:96px;
font-family:iconFont;
}
Vous pouvez utiliser data-image pour cela. en utilisant data-image (data-URI), vous pouvez accéder à SVG comme inline.
Voici un effet de survol utilisant du CSS et du SVG purs.
Je le sais malpropre mais vous pouvez le faire de cette façon.
.action-btn {
background-size: 20px 20px;
background-position: center center;
background-repeat: no-repeat;
border-width: 1px;
border-style: solid;
border-radius: 30px;
height: 40px;
width: 60px;
display: inline-block;
}
.delete {
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg version='1.1' id='Capa_1' fill='#FB404B' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='482.428px' height='482.429px' viewBox='0 0 482.428 482.429' style='enable-background:new 0 0 482.428 482.429;' xml:space='preserve'%3e%3cg%3e%3cg%3e%3cpath d='M381.163,57.799h-75.094C302.323,25.316,274.686,0,241.214,0c-33.471,0-61.104,25.315-64.85,57.799h-75.098 c-30.39,0-55.111,24.728-55.111,55.117v2.828c0,23.223,14.46,43.1,34.83,51.199v260.369c0,30.39,24.724,55.117,55.112,55.117 h210.236c30.389,0,55.111-24.729,55.111-55.117V166.944c20.369-8.1,34.83-27.977,34.83-51.199v-2.828 C436.274,82.527,411.551,57.799,381.163,57.799z M241.214,26.139c19.037,0,34.927,13.645,38.443,31.66h-76.879 C206.293,39.783,222.184,26.139,241.214,26.139z M375.305,427.312c0,15.978-13,28.979-28.973,28.979H136.096 c-15.973,0-28.973-13.002-28.973-28.979V170.861h268.182V427.312z M410.135,115.744c0,15.978-13,28.979-28.973,28.979H101.266 c-15.973,0-28.973-13.001-28.973-28.979v-2.828c0-15.978,13-28.979,28.973-28.979h279.897c15.973,0,28.973,13.001,28.973,28.979 V115.744z'/%3e%3cpath d='M171.144,422.863c7.218,0,13.069-5.853,13.069-13.068V262.641c0-7.216-5.852-13.07-13.069-13.07 c-7.217,0-13.069,5.854-13.069,13.07v147.154C158.074,417.012,163.926,422.863,171.144,422.863z'/%3e%3cpath d='M241.214,422.863c7.218,0,13.07-5.853,13.07-13.068V262.641c0-7.216-5.854-13.07-13.07-13.07 c-7.217,0-13.069,5.854-13.069,13.07v147.154C228.145,417.012,233.996,422.863,241.214,422.863z'/%3e%3cpath d='M311.284,422.863c7.217,0,13.068-5.853,13.068-13.068V262.641c0-7.216-5.852-13.07-13.068-13.07 c-7.219,0-13.07,5.854-13.07,13.07v147.154C298.213,417.012,304.067,422.863,311.284,422.863z'/%3e%3c/g%3e%3c/g%3e%3c/svg%3e ");
border-color:#FB404B;
}
.delete:hover {
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg version='1.1' id='Capa_1' fill='#fff' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='482.428px' height='482.429px' viewBox='0 0 482.428 482.429' style='enable-background:new 0 0 482.428 482.429;' xml:space='preserve'%3e%3cg%3e%3cg%3e%3cpath d='M381.163,57.799h-75.094C302.323,25.316,274.686,0,241.214,0c-33.471,0-61.104,25.315-64.85,57.799h-75.098 c-30.39,0-55.111,24.728-55.111,55.117v2.828c0,23.223,14.46,43.1,34.83,51.199v260.369c0,30.39,24.724,55.117,55.112,55.117 h210.236c30.389,0,55.111-24.729,55.111-55.117V166.944c20.369-8.1,34.83-27.977,34.83-51.199v-2.828 C436.274,82.527,411.551,57.799,381.163,57.799z M241.214,26.139c19.037,0,34.927,13.645,38.443,31.66h-76.879 C206.293,39.783,222.184,26.139,241.214,26.139z M375.305,427.312c0,15.978-13,28.979-28.973,28.979H136.096 c-15.973,0-28.973-13.002-28.973-28.979V170.861h268.182V427.312z M410.135,115.744c0,15.978-13,28.979-28.973,28.979H101.266 c-15.973,0-28.973-13.001-28.973-28.979v-2.828c0-15.978,13-28.979,28.973-28.979h279.897c15.973,0,28.973,13.001,28.973,28.979 V115.744z'/%3e%3cpath d='M171.144,422.863c7.218,0,13.069-5.853,13.069-13.068V262.641c0-7.216-5.852-13.07-13.069-13.07 c-7.217,0-13.069,5.854-13.069,13.07v147.154C158.074,417.012,163.926,422.863,171.144,422.863z'/%3e%3cpath d='M241.214,422.863c7.218,0,13.07-5.853,13.07-13.068V262.641c0-7.216-5.854-13.07-13.07-13.07 c-7.217,0-13.069,5.854-13.069,13.07v147.154C228.145,417.012,233.996,422.863,241.214,422.863z'/%3e%3cpath d='M311.284,422.863c7.217,0,13.068-5.853,13.068-13.068V262.641c0-7.216-5.852-13.07-13.068-13.07 c-7.219,0-13.07,5.854-13.07,13.07v147.154C298.213,417.012,304.067,422.863,311.284,422.863z'/%3e%3c/g%3e%3c/g%3e%3c/svg%3e ");
background-color: #FB404B;
}
<a class="action-btn delete"> </a>
Vous pouvez convertir votre svg en URL de données ici
La solution choisie convient si vous voulez que jQuery traite tous les éléments svg de votre DOM et que celui-ci soit de taille raisonnable. Mais si votre DOM est volumineux et que vous décidez de charger certaines parties de votre DOM de manière dynamique, il n’a aucun sens de réanalyser tout le DOM pour mettre à jour les éléments svg. A la place, utilisez un plugin jQuery pour faire ceci:
/**
* A jQuery plugin that loads an svg file and replaces the jQuery object with its contents.
*
* The path to the svg file is specified in the src attribute (which normally does not exist for an svg element).
*
* The width, height and class attributes in the loaded svg will be replaced by those that exist in the jQuery object's
* underlying html. Note: All other attributes in the original element are lost including the style attribute. Place
* any styles in a style class instead.
*/
(function ($) {
$.fn.svgLoader = function () {
var src = $(this).attr("src");
var width = this.attr("width");
var height = this.attr("height");
var cls = this.attr("class");
var ctx = $(this);
// Get the svg file and replace the <svg> element.
$.ajax({
url: src,
cache: false
}).done(function (html) {
let svg = $(html);
svg.attr("width", width);
svg.attr("height", height);
svg.attr("class", cls);
var newHtml = $('<a></a>').append(svg.clone()).html();
ctx.replaceWith(newHtml);
});
return this;
};
}(jQuery));
Dans votre code HTML, spécifiez un élément svg comme suit:
<svg src="images/someSvgFile.svg" height="45" width="45" class="mySVGClass"/>
Et appliquez le plugin:
$(".mySVGClass").svgLoader();
pour: animations d'événement survol nous pouvons laisser les styles à l'intérieur du fichier svg, comme un
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<style>
rect {
fill:rgb(165,225,75);
stroke:none;
transition: 550ms ease-in-out;
transform-Origin:125px 125px;
}
rect:hover {
fill:rgb(75,165,225);
transform:rotate(360deg);
}
</style>
</defs>
<rect x='50' y='50' width='150' height='150'/>
</svg>