Related
Newbie to working with SVGs and I've run into a problem. Basically I'm using the Noun Project API pro version ( https://api.thenounproject.com/ ) to scrape icons as SVGs. Right now, I save the SVGs in a local folder. I want to be able to change the color of the icon (right now they download as black, and I want to change the color). I am loading the SVGs via the tag with a reference to the folder where the SVGs are being saved. I know it's fairly simple to change the color using vanilla JS (using document.getElementById('svgObject').contentDocument, and then accessing the inner document using a unique id). The problem is that the SVGs I save don't have any ID, and I don't know how to give them an ID. Right now, I'm looping through the folder and displaying the folder contents in a webpage. That's all working fine, but I can't manage to figure out how to change the color.
Basically, I just want all the icons in the folder to be set to a different color (for example, they could all be set to "red." They don't need to each have a different color). How do I do that without specifying the actual ID (since the SVG doesn't include an ID upon download), or else how do I add an ID to the SVG tag itself?
Assuming you have a bunch of svgs on your page and you'd like to change the color of each to the same color, you could do the following:
function changeSvgColors() {
const svgs = document.getElementsByTagName('svg')
for (let i = 0; i < svgs.length; i++) {
svgs[i].setAttribute('fill', 'red')
}
}
This will loop through every svg on your page and set their fill attribute to red.
Essentially, this is equivalent to doing this:
<svg viewBox="0 0 512 512" fill="red">
...
</svg>
Here's a quick demo:
JSFiddle
Alternatively, if you're using something like the img tag to load your svgs onto your page, using object instead would let you do the same thing as above.
Assuming your html looks like the following:
<div>
<object
data="http://localhost:5000/image.svg"
type="image/svg+xml"
></object>
</div>
Then,
function changeSvgColors() {
const objects = document.getElementsByTagName('object')
for (let i = 0; i < objects.length; i++) {
const object = objects[i]
const svg = object.contentDocument.rootElement
svg.setAttribute('fill', 'red')
}
}
One option would be using the svg as an image (or as an object if you prefer) and filters to change the color.
#theImage{filter: invert(27%) sepia(51%) saturate(2878%) hue-rotate(346deg) brightness(104%) contrast(97%);}
<img id="theImage" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/222579/bone300.svg" >
Please read this article: Solved with CSS! Colorizing SVG Backgrounds
Yet another option is using the svg as a mask. Keep in mind that the support for mask is not that good.
#theDiv{
display: inline-block;
width:300px;
height:134px;
-webkit-mask: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/222579/bone300.svg);
mask: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/222579/bone300.svg);
mask-size: cover;
background:red;
}
<div id="theDiv"></div>
But the best option would be to use the svg inline. You may save all your icons in a root svg element with width="0"; height="0"; position="absolute". Next you can use the icons with <use> and you change the color using the fill attribute <use fill="red" xlink:href="..... or styling it in CSS
You can give the SVGs a class instead of an id & set the color using javascript's getElementsByClassName
This Mozilla article explains:
https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName
Your best shot in achiveing this is to make use of the fill attibute for svg elements. Now, you can work this out two ways
Method 1:
Using img tag like this <img src="your_file.svg" />. In this case, you will need to edit the file directly, and that's it, it should reflect everywhere you reference the svg.
Method 2:
You can use svg as inline like below. The advantage in this approach is that if you have varying instance of the same svg, then this is the way to go. On the other side, if your svg stays same throughout the app, this goes against the DRY principle.
svg {
max-width: 200px;
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<path d="M25,7l28,36l40-18l-36,25l19,44l-27-37l-41,17l36-26z" fill="#cfc"/>
<ellipse cx="50" cy="50" rx="32" ry="30" stroke="#fc0" fill="none" stroke-width="11"/>
<path d="M63,2l-7,43l42,17l-45-6l-16,42l7,-45l-42-16l45,6z" fill="#3cc"/>
</svg>
If you don't mind having the svg all one color, you could set the fill attribute of the svg to currentcolor. Then it will take on the color of the element that it is in.
#span1 {
color: red;
}
#span2 {
color: blue;
}
#span3 {
color: green;
}
<body>
<span id=span1>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="150" height="150" fill-rule="evenodd" fill="currentcolor">
<title>HTML5 Logo</title>
<path d="M12,0 188,0 172,180 100,200 28,180Z M45,37 156,37 154,59 69,59 71,81 152,81 146,149 100,162 54,149 52,114 74,114 76,132 100,139 124,132 127,103 51,103Z"/>
</svg>
</span>
<span id=span2>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="150" height="150" fill-rule="evenodd" fill="currentcolor">
<title>HTML5 Logo</title>
<path d="M12,0 188,0 172,180 100,200 28,180Z M45,37 156,37 154,59 69,59 71,81 152,81 146,149 100,162 54,149 52,114 74,114 76,132 100,139 124,132 127,103 51,103Z"/>
</svg>
</span>
<span id=span3>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="150" height="150" fill-rule="evenodd" fill="currentcolor">
<title>HTML5 Logo</title>
<path d="M12,0 188,0 172,180 100,200 28,180Z M45,37 156,37 154,59 69,59 71,81 152,81 146,149 100,162 54,149 52,114 74,114 76,132 100,139 124,132 127,103 51,103Z"/>
</svg>
</span>
</body>
Essentially I needed to make the center "cut-out" keep a fixed shape and size regardless of vector scale. Is there a way to achieve this?
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" preserveAspectRatio="none" style="fill-rule:evenodd;" viewBox="0 0 2802 2657">
<path d="M290 4c-95,733 -191,1466 -286,2200 760,150 1520,300 2279,450 172,-223 343,-446 515,-669 -114,-572 -229,-1144 -343,-1716 -722,-88 -1444,-176 -2165,-264zm696 1027c-103,111 -205,222 -308,333 94,111 188,222 282,333 342,-205 684,-410 1026,-616 -333,-17 -667,-34 -1000,-51z"/>
</svg>
So I managed to do something after some editing to your SVG.
To achieve what you're asking you'll need to use / have :
- the SVG mask attribute
- A very large shape for the mask ( as much large as the max scale you want to use on the visible shape )
- The shape that you want to resize
- Resize the shape with transforms
Your SVG should looks like the following
<svg>
<defs>
<mask id="theMask">
<path fill="#ffffff" d=""/>
</mask>
</defs>
<g mask="url(#theMask)">
<path fill="#ffffff" id="shapetoresize" d=""/>
</g>
</svg>
I posted a pen as a "Proof of concept"
Feel free to fork it and use it to achieve what you're trying to do.
Codepen
note: as pointed out by #thioutp , The JS is only for demo purposes, you don't need GSAP to achieve this.
Premise: This code (although it may be considered a hack) has been working for about 2 years, but all of a sudden this has stopped working in google chrome (the rendering of the gradient colors in svgs) and I don't know when exactly that has stopped, but still works in Safari! (see the 2 attached screenshots at the bottom).
I have an ng-repeat in which I render items with timeseries data and a different svg icon per item that I render dynamically via ng-include based on the item property that coincides with the name of the icon like "icon.type.svg".
<div ng-repeat="item in items">
<div class="pull-right">
<ng-include src="'img/icons/items/'+ item.type + '.svg'"></ng-include>
<h4><a>{{item.name}}</a></h4>
.....
</div>
</div>
All the svg follow the same strategy and a sample code can be seen below:
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 40.6 60.2" style="enable-background:new 0 0 40.6 60.2;" xml:space="preserve" ng-attr-height="{{myIcon.height || '60px'}}">
<style type="text/css">
</style>
<defs>
<linearGradient id="grad" gradientTransform="rotate(90)">
<stop ng-attr-offset="{{getOffset(item.level)}}" stop-color="#34495e">
</stop>
<stop offset="0%" stop-color="{{itemFillColor}}">
</stop>
</linearGradient>
</defs>
<path class="st0" d="M40.6,0H0v39v0.6c0,1.2-0.1,13.3,8.1,18.3c4,2.4,8.7,2.3,12.5,2.3c3.5,0,7.4-0.1,10.9-2
c8.5-4.6,9.1-17.1,9.1-18.5v-0.6V0z"/>
<path class="st1" d="M5.4,53.4"/>
<path d="M39,1.7H1.5v36.8v0.6c0,1.1-0.1,12.6,7.5,17.3c3.7,2.3,8,2.2,11.5,2.2c3.2,0,6.8-0.1,10.1-1.9c7.8-4.3,8.4-16.1,8.4-17.5
v-0.6V1.7z" fill="url(#grad)"/>
</svg>
Then in my controller I have a simple method (getOffset) as follows:
$scope.getOffset = function(level) {
if (!level) return '100%';
return (100 - level) + '%';
};
The way the icon work is kind of backward. They are 100% green (variable itemFillColor), and then the navy blue color, which is the actual body of the item, is then lowered from the top down to render the actual level.
Chrome Rendering
Safari Rendering
With Chrome it seems like that whatever "level" the first container has (in this case is actually 0 (null)), is applied to all subsequent items in the ng-repeat.
Can someone advise me on how to solve this or know why this started to happen all of a sudden?
Is there a better way otherwise to do this or go around the issue?
Would love to hear why this happened.
Edit: To "prove" that my hunch is that is nothing wrong with the svg per se, I use the same svg and do the exact same approach in a page with a single item selected (no ng-repeat, but a single ng-include) and the result is correct:
Single Page view
I am pretty new at editing and embedding SVG to webpages. My client is a graphic designer so he created an svg logo for his website and wanted me embed it to his nav bar.
I am also using Bootstrap and as part of the development I am using bootstrap tooltips. However, once i embed the svg and edit the co-ordinates via viewbox, I encountered a problem with one of my tooltips.
The tooltip seems to be buggy with this particular logo. The rest of them are okay but for some reason I cannot figure out how to fix this one.
I have uploaded the website today for testing purposes and you can see the issue on http://edizorac.com/
The problematic logo is the second one on the navigation bar and you can also see my code through web inspector.
Any help/recommendation is appreciated!!
Thanks in advance :)
Update:
Sorry for any inconveniences. The svg code is as follows:
<li>
<a href="#projects">
<span>
<svg version="1.2" baseProfile="tiny" x="0px" y="0px" width="70px" height="60px" viewBox="50 40 90 120" data-toggle="tooltip" title="Projects">
<polygon fill="none" stroke="#ffffff" stroke-miterlimit="10" points="33.493,81.046 96.934,46.112 88.647,82.874 57.203,117.371 52.946,86.674 99.573,74.629 81.325,117.276 37.909,101.254"/>
<polyline fill="none" stroke="#ffffff" stroke-miterlimit="10" points="96.451,81.928 114.361,106.172 112.365,112.871 82.791,113.852 "/>
<line fill="none" stroke="#ffffff" stroke-miterlimit="10" x1="95.854" y1="47.473" x2="37.909" y2="101.254"/>
</svg>
</span>
</a>
</li>
May I suggest you have a read over this and also have a look at this to help you with SVG's
Change the first line of your SVG block to:
<svg version="1.2" baseProfile="tiny" x="0px" y="0px" width="40px" height="40px" viewBox="30 47 85 70" data-toggle="tooltip" title="" data-original-title="Projects">
You made the following mistakes:
You had the height and width set incorrectly compared to the other SVG's (Height you had defined: 60px & Width you had defined 70px)
Your viewBox property was set incorrectly so the elements didn't align properly
I have been searching for a good example on how to animate a svg path morph. I know how to do do pretty complex ones using SMIL, but snap.svg is new and shiny, and everyone seems to love it, so I'd like to take a look. I can't find a good example anywhere on how to do n animated path morph anywhere. Hopefully a snap.svg guru could point me in the right direction?
here's a link to the svg image and the code for it:
link to image
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="400">
<path id="thing" d="M94.2,265.7L82,203.4c43.3-15.6,83.8-29.2,137.1-20.2c61.5-27.6,126.1-56.9,202.6-46.1c18.7,18.9,21.5,39.8,12.2,62.3C322.7,231.9,208.2,247.6,94.2,265.7z">
<animate id="myAnimationElement" dur="2s" begin="0" repeatCount="indefinite" attributeName="d"
values="M94.2,265.7L82,203.4c43.3-15.6,83.8-29.2,137.1-20.2c61.5-27.6,126.1-56.9,202.6-46.1c18.7,18.9,21.5,39.8,12.2,62.3C322.7,231.9,208.2,247.6,94.2,265.7z;
M179.4,83.5l62.4-11.8c15.3,43.4-76,102.6-22.6,111.5c61.5-27.6,126.1-56.9,202.6-46.1c18.7,18.9,21.5,39.8,12.2,62.3C250.6,296.7,52.4,259.2,179.4,83.5z;"/>
</path>
</svg>
Not quite sure if you mean you just want the current animation in Snap or something different. Just to give an example of how one would typically do some animation...
s = Snap(400, 620);
var path = s.path("M94.2,265.7L82,203.4c43.3-15.6,83.8-29.2,137.1-20.2c61.5-27.6,126.1-56.9,202.6 46.1c18.7,18.9,21.5,39.8,12.2,62.3C322.7,231.9,208.2,247.6,94.2,265.7z");
path.animate({ d: "M179.4,83.5l62.4-11.8c15.3,43.4-76,102.6-22.6,111.5c61.5-27.6,126.1-56.9,202.6-46.1c18.7,18.9,21.5,39.8,12.2,62.3C250.6,296.7,52.4,259.2,179.4,83.5z" }, 1000, mina.bounce);
jsfiddle
Edit: There needs to be the same amount of points on the path to morph from/to.
If you're looking for something only for SVG Morph, you may not need the entire SnapSVG library, you can use KUTE.js. Really, take a look at this (Best viewed in Chrome and Firefox):
<div style="width: 220px">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 600">
<path id="rectangle" fill="indigo" d="M38.01,5.653h526.531c17.905,0,32.422,14.516,32.422,32.422v526.531
c0,17.905-14.517,32.422-32.422,32.422H38.01c-17.906,0-32.422-14.517-32.422-32.422V38.075C5.588,20.169,20.104,5.653,38.01,5.653z"></path>
<path id="star" style="visibility:hidden" d="M301.113,12.011l99.25,179.996l201.864,38.778L461.706,380.808
l25.508,203.958l-186.101-87.287L115.01,584.766l25.507-203.958L0,230.785l201.86-38.778L301.113,12.011"></path>
</svg>
</div>
<script id="core" src="https://cdn.jsdelivr.net/kute.js/1.5.5/kute.min.js"></script>
<script id="svg" src="https://cdn.jsdelivr.net/kute.js/1.5.5/kute-svg.min.js"></script>
<script>
var tween = KUTE.to('#rectangle', { path: '#star' }, {duration: 1500, yoyo: true, repeat: 1, morphIndex: 127}).start();
document.addEventListener('click', function(){
!tween.playing && tween.start();
}, false);
</script>
It's free and really easy to use.