I've been playing around with SVGs in websites, and I've been trying to get filters to work, but I can't seem to get it right.
The problem is that the svg disappears completely once I apply a defined filter. I've tried to apply the filter inline, just to see if it worked, like this:
<symbol id="circle" viewBox="0 0 400 209.603" filter="url('#blur-filter')">
...
</symbol>
but with no success.
Ultimately, my goal is that I would be able to apply the filters via CSS, but I can't seem to get it to work, and this is the first time I've really played around with SVGs, so I don't know if I'm making some obvious mistake.
Code:
.svg-circle:hover {
filter: url("#blur-filter");
}
.svg-grey {
fill: #333;
}
<svg xmlns="http://www.w3.org/2000/svg" style="display:none">
<defs>
<filter id="blur-filter">
<feGaussianBlur in="SourceGraphic" stdDeviation="2" />
</filter>
<symbol id="circle" viewBox="0 0 400 209.603">
<circle cx="100" cy="100" r="100" />
</symbol>
</defs>
</svg>
<svg>
<use xlink:href="#circle" class="svg-circle svg-grey"/>
</svg>
I want the filter to be applied when I hover over the element. My other question is how I can incorporate this with CSS transitions, so that the blur gets applied gradually, like other css3 transitions.
I also want the filters to be global, so they can be reused across multiple svg images whenever I want, so define once, and reuse.
I've also created a Codepen to demonstrate my problem.
Remove your style="display:none" and add width:0 to your first svg
.svg-circle:hover {
filter: url("#blur-filter");
}
.svg-grey {
fill: #333;
}
svg:first-of-type {
width:0
}
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="blur-filter">
<feGaussianBlur in="SourceGraphic" stdDeviation="2" />
</filter>
<symbol id="circle" viewBox="0 0 400 209.603">
<circle cx="100" cy="100" r="100" />
</symbol>
</defs>
</svg>
<svg>
<use xlink:href="#circle" class="svg-circle svg-grey"/>
</svg>
Remove the display: none; on the definitions SVG and give it 0 dimensions. This should do it. Somehow the filter may be inheriting that display: none.
.svg-circle:hover {
filter: url("#blur-filter");
}
.svg-grey {
fill: #333;
}
<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0">
<defs>
<filter id="blur-filter">
<feGaussianBlur in="SourceGraphic" stdDeviation="2" />
</filter>
<symbol id="circle" viewBox="0 0 400 209.603">
<circle cx="100" cy="100" r="100" />
</symbol>
</defs>
</svg>
<svg>
<use xlink:href="#circle" class="svg-circle svg-grey"/>
</svg>
As for the transition, I don't think you can do that by using referenced filters.
html, body {
margin: 0;
padding: 0;
}
.svg-circle:hover {
filter: url("#blur-filter");
}
.svg-grey {
fill: #333;
}
<svg width="0" height="0">
<defs>
<filter id="blur-filter">
<feGaussianBlur in="SourceGraphic" stdDeviation="2" />
</filter>
<symbol id="circle" viewBox="0 0 400 209.603">
<circle cx="100" cy="100" r="100" />
</symbol>
</defs>
</svg><svg width="400" height="210">
<use xlink:href="#circle" class="svg-circle svg-grey"/>
</svg>
Related
Can I put svg resources used in the website behind the end of body, in order to keep
them outside of what will be rendered?
In short: Is it legal to do the following?
<!DOCTYPE html>
<html>
<head>...</head>
<body>
<svg xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 315.424 315.424" >
<use href="#arrow" fill="rgb(0,44,89)" />
</svg>
</body>
<svg>
<g id="arrow">
<path d="M311.929,222.266l-96.119-67.342c-1.413-0.99-2.783-1.513-4.307-1.513c-3.307,0-6.471,2.512-6.471,7.313v41.05H19.886
c-4.962,0-8.854,4.132-8.854,9.094v35.563c0,4.962,3.892,9.343,8.854,9.343h185.146v40.81c0,4.801,3.167,7.19,6.474,7.19
c0.001,0-0.089,0-0.089,0c1.524,0,3.032-0.461,4.445-1.451l96.09-67.306c2.214-1.55,3.473-3.864,3.473-6.375
S314.142,223.815,311.929,222.266z" />
</g>
</svg>
</html>
As mentioned before in the comments:
It's not valid and many browser won't display your elements.
So inserting your svg assets at the top or bottom of your <body> is the preferrable method.
Caution: Hiding your asset svg via display:none will break some referenced definitions like:
filters
gradients
masks and clip-paths
It works flawlessly for shape elements (like icons)
Example
function displayNone() {
document.querySelector('#svgAssets').style.display = 'none';
}
svg {
border: 1px dotted #ccc;
height: 10em;
display: inline-block;
}
<p><button onclick="displayNone()">Set display:none</button></p>
<svg viewBox="0 0 315.424 315.424">
<use href="#arrow" fill="red" />
</svg>
<svg viewBox="0 0 100 100">
<use href="#circle" fill="green" />
</svg>
<svg viewBox="0 0 200 100">
<use href="#ellipse" fill="url(#gradient)" />
</svg>
<svg id="svgAssets" style="visibility:visible; width:0; height:0" aria-hidden="true">
<defs>
<linearGradient id="gradient">
<stop stop-color="red" offset="0%"/>
<stop stop-color="orange" offset="100%"/>
</linearGradient>
</defs>
<symbol id="arrow" viewBox="0 0 315.424 315.424">
<path d="M311.929,222.266l-96.119-67.342c-1.413-0.99-2.783-1.513-4.307-1.513c-3.307,0-6.471,2.512-6.471,7.313v41.05H19.886 c-4.962,0-8.854,4.132-8.854,9.094v35.563c0,4.962,3.892,9.343,8.854,9.343h185.146v40.81c0,4.801,3.167,7.19,6.474,7.19 c0.001,0-0.089,0-0.089,0c1.524,0,3.032-0.461,4.445-1.451l96.09-67.306c2.214-1.55,3.473-3.864,3.473-6.375 S314.142,223.815,311.929,222.266z" />
</symbol>
<symbol id="circle" viewBox="0 0 100 100">
<circle cx="50%" cy="50%" r=50% />
</symbol>
<symbol id="ellipse" viewBox="0 0 200 100">
<ellipse cx="100" cy="50" rx="100" ry="50" />
</symbol>
</svg>
In the above example I'm using <symbol> elements which could be used as an alternative to <defs>. They also support different viewBox properties for each icon.
If you just need to place icons via <use> you could also use external file references like so:
<svg viewBox="0 0 315.424 315.424">
<use href="svgIcons.svg#arrow" fill="red" />
</svg>
However, your svg files need to be same domain (or send with appropriate CORS headers)
If the purpose is not to have the original #arrow rendered in the document, you might include it inside the svg in the body, wrapped around defs.
Demo in the snipped below.
<svg xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 315.424 315.424">
<defs>
<g id="arrow">
<path d="M311.929,222.266l-96.119-67.342c-1.413-0.99-2.783-1.513-4.307-1.513c-3.307,0-6.471,2.512-6.471,7.313v41.05H19.886
c-4.962,0-8.854,4.132-8.854,9.094v35.563c0,4.962,3.892,9.343,8.854,9.343h185.146v40.81c0,4.801,3.167,7.19,6.474,7.19
c0.001,0-0.089,0-0.089,0c1.524,0,3.032-0.461,4.445-1.451l96.09-67.306c2.214-1.55,3.473-3.864,3.473-6.375
S314.142,223.815,311.929,222.266z" />
</g>
</defs>
<use href="#arrow" fill="rgb(0,44,89)" />
</svg>
I have an svg which takes its parent width, but when I scale or rotate the parent the svg's drop shadow gets cut. The left one is fine, but when the parent is the same width and height of the svg (right one) it behaves different. I have overflow visible and filterUnits="userSpaceOnUse" http://jsfiddle.net/xrsknjfv/
scale.addEventListener("mousedown", function(){
this.parentElement.classList.toggle("scale");
})
svg{
fill: yellow;
overflow: visible;
width: 100%;
}
body > div{
position: absolute;
left: 50px;
top: 50px;
}
div > div{
display: inline-block;
width: 150px;
transition: 5s all;
}
div > div:nth-of-type(1){
padding: 15px;
}
div.scale > div{
transform: scale(0);
}
<div>
<button id="scale">
scale
</button>
<div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 409.96 254.59">
<defs>
<filter filterUnits="userSpaceOnUse" id="f1">
<feDropShadow filterUnits="userSpaceOnUse" dx="1.5" dy="0.8" stdDeviation="1"></feDropShadow>
</filter>
<filter filterUnits="userSpaceOnUse" id="f2">
<feDropShadow filterUnits="userSpaceOnUse" dx="-2" dy="1.8" stdDeviation="1"></feDropShadow>
</filter>
<filter filterUnits="userSpaceOnUse" id="f3">
<feDropShadow flood-opacity="0.1" stdDeviation="10" dy="-4" dx="-2" filterUnits="userSpaceOnUse"></feDropShadow>
</filter>
</defs>
<g style="filter: url(#f2);">
<g style="filter: url(#f3);">
<path d="M167.1,52.17S209.77-28.24,301.66,10.6c0,0,59.62,23.52,56.34,96.81,0,0,55.45,16.41,51.79,79.86,0,0-.3,61.95-67.65,67.28H220.75q-72.56,0-145.12,0C29.57,254.66-2.8,215,.19,170.31c0,0,7.74-70,81.58-68.37C81.77,101.94,101.46,42.87,167.1,52.17Z" style="filter: url(#f1);"></path>
</g>
</g>
</svg>
</div>
<div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 409.96 254.59">
<defs>
<filter filterUnits="userSpaceOnUse" id="f1">
<feDropShadow filterUnits="userSpaceOnUse" dx="1.5" dy="0.8" stdDeviation="1"></feDropShadow>
</filter>
<filter filterUnits="userSpaceOnUse" id="f2">
<feDropShadow filterUnits="userSpaceOnUse" dx="-2" dy="1.8" stdDeviation="1"></feDropShadow>
</filter>
<filter filterUnits="userSpaceOnUse" id="f3">
<feDropShadow flood-opacity="0.1" stdDeviation="10" dy="-4" dx="-2" filterUnits="userSpaceOnUse"></feDropShadow>
</filter>
</defs>
<g style="filter: url(#f2);">
<g style="filter: url(#f3);">
<path d="M167.1,52.17S209.77-28.24,301.66,10.6c0,0,59.62,23.52,56.34,96.81,0,0,55.45,16.41,51.79,79.86,0,0-.3,61.95-67.65,67.28H220.75q-72.56,0-145.12,0C29.57,254.66-2.8,215,.19,170.31c0,0,7.74-70,81.58-68.37C81.77,101.94,101.46,42.87,167.1,52.17Z" style="filter: url(#f1);"></path>
</g>
</g>
</svg>
</div>
</div>
Overflow shadows aren't retained during transform transitions - so you need to add margin/padding to your box to make sure they're ok.
Incidentally, you can make your SVG a lot terser
You don't need to re-declare filters within each inline SVG fragment
You only need to declare your filterUnits in the filter element itself (not each primitive)
You can use self-closing tags for feDropshadow ("/>")
You can get rid of the style and just declare a filter attribute directly.
<div>
<button id="scale">
scale
</button>
<div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 409.96 254.59">
<defs>
<filter id="f1" >
<feDropShadow dx="1.5" dy="0.8" stdDeviation="1"/>
</filter>
<filter id="f2">
<feDropShadow dx="-2" dy="1.8" stdDeviation="1"/>
</filter>
<filter id="f3" >
<feDropShadow stdDeviation="10" dy="-4" dx="-2"/>
</filter>
</defs>
<g filter="url(#f2)">
<g filter=" url(#f3)">
<path d="M167.1,52.17S209.77-28.24,301.66,10.6c0,0,59.62,23.52,56.34,96.81,0,0,55.45,16.41,51.79,79.86,0,0-.3,61.95-67.65,67.28H220.75q-72.56,0-145.12,0C29.57,254.66-2.8,215,.19,170.31c0,0,7.74-70,81.58-68.37C81.77,101.94,101.46,42.87,167.1,52.17Z" filter= "url(#f1)"></path>
</g>
</g>
</svg>
</div>
<div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 409.96 254.59">
<g filter= "url(#f2)">
<g filter= "url(#f3)">
<path d="M167.1,52.17S209.77-28.24,301.66,10.6c0,0,59.62,23.52,56.34,96.81,0,0,55.45,16.41,51.79,79.86,0,0-.3,61.95-67.65,67.28H220.75q-72.56,0-145.12,0C29.57,254.66-2.8,215,.19,170.31c0,0,7.74-70,81.58-68.37C81.77,101.94,101.46,42.87,167.1,52.17Z" filter= "url(#f1)"></path>
</g>
</g>
</svg>
</div>
</div>
I am having a problem with setting up shadow for a svg with mask applied to it. This is the code and jsfiddle: http://jsfiddle.net/3kxnmhfL/
.watch-video-svg {
width: 50px;
height: 50px;
}
<div class="watch-video-svg">
<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" height="100%" width="100%" viewBox="0 0 100 100">
<defs>
<filter id="shadow">
<feDropShadow dx="4" dy="8" stdDeviation="4"/>
</filter>
<mask id="cut-off-bottom">
<circle cx="50%" cy="50%" r="50%" fill="white"/>
<polygon points="31,20, 31,77, 80,50" fill="black"/>
<!-- <rect x="0" y="0" width="200" height="200" fill="url(#Gradient)" /> -->
</mask>
</defs>
<circle cx="50%" cy="50%" r="50%" fill="red" mask="url(#cut-off-bottom)" filter="url(#shadow)" />
</svg>
</div>
I'd like the shadow to appear only around svg circle and not containing div.
What could be the reason for shadow not showing up?
SVG filters have a "filter region". The filter region defines the area of pixels that the browser uses to store the result of the filters. The default filter region is the bounds of the element (that the filter is applied to) plus a margin around it to allow for filter elements that have results larger than the original element.
By default, that margin is 10% of the width and height, applied to all four sides. However in your case, a stdDeviation of "4" causes the blur to extend further than that 10% margin allowance. The result is a clipped blur, even though the viewBox has been enlarged enough to cater for the full blur.
To fix, this, you just need to bump up the filter region size. An allowance of 20% seems to work okay:
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
In addition, I've simplified the SVG by getting rid of the unnecessary mask. I also tweaked the viewBox to include the part of the blur that extends to the left.
.watch-video-svg {
width: 500px;
height: 500px;
background: linen;
}
<div class="watch-video-svg">
<svg xmlns="http://www.w3.org/2000/svg" height="100%" width="100%" viewBox="-5 0 120 120">
<defs>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="4" dy="8" stdDeviation="4"/>
</filter>
</defs>
<circle cx="50" cy="50" r="50" fill="red" filter=url(#shadow) />
<polygon points="31,20, 31,77, 80,50" fill="white"/>
</svg>
</div>
You need to adjust your mask. Since your element is already a circle you don't need a circle in the mask, a rect to fill all the width/height is enough.
You need to also adjust the viewBox to leave some space for the shadow:
.watch-video-svg {
width: 50px;
height: 50px;
}
<div class="watch-video-svg">
<svg xmlns="http://www.w3.org/2000/svg" height="100%" width="100%" viewBox="0 0 120 120">
<defs>
<filter id="shadow">
<feDropShadow dx="4" dy="8" stdDeviation="4"/>
</filter>
<mask id="cut-off-bottom">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<polygon points="31,20, 31,77, 80,50" fill="black"/>
</mask>
</defs>
<circle cx="50" cy="50" r="50" fill="red" mask="url(#cut-off-bottom)" filter=url(#shadow) />
</svg>
</div>
I have the following simple example. It is stored in image.svg:
<svg>
<defs>
<g id="shape">
<circle cx="100" cy="100" r="100" />
</g>
</defs>
</svg>
However, putting this code in a HTML file doesn't load anything. Why is that?
<svg>
<use xlink:href="#shape" x="10" y="10" />
</svg>
What am I doing wrong? I can't seem to make it work.
If you are using elements from another document, you have to specify the document!
<use xlink:href="#shape" x="10" y="10" />
This means "use the #shape element from the current document".
To import from another document, you need to put the reference to the SVG file in the xlink:href attribute:
<use xlink:href="image.svg#shape" x="10" y="10" />
Obviously you need to check the path is correct here. Note that this is not supported in any version of Internet Explorer, though polyfills are available.
For external svg files you need the namespace ... and I have added a fill to render the circle otherwise it will be transparent:
<svg xmlns="http://www.w3.org/2000/svg" >
<symbol id="shape" width="200" height="200" viewbox="0 0 200 200">
<circle cx="100" cy="100" r="100" fill="currentColor" />
</symbol>
<text y="20">Symbol above will not render unless referenced by use element</text>
</svg>
Then when you reference it you need to use the correct namespace for xlink:
svg.defs-only {
display:block; position: absolute;
height:0; width:0; margin: 0; padding: 0;
border: none; overflow: hidden;
}
svg {
color: orange;
stroke: red;
}
.purple {
color: purple;
stroke: black;
}
<svg class="defs-only" xmlns="http://www.w3.org/2000/svg" >
<symbol id="shape" width="50" height="50" viewbox="0 0 50 50">
<circle cx="25" cy="25" r="20" fill="currentColor" stroke="inherit" />
</symbol>
</svg>
<svg xmlns:xlink="http://www.w3.org/1999/xlink">
<use xlink:href="#shape" x="10" y="10" />
<use xlink:href="#shape" x="80" y="10" class="purple" />
</svg>
If you are referencing an external file, you need to put the filename before the # e.g. image.svg#shape making sure you get the path correct of course.
Note, not all browsers support fragment identifiers - notably IE and Edge - you need to use a javascript polyfill like svg4everybody for those browsers.
Workaround - use svg inline only
You need to have the use-tag inside the SVG with the shape you want to use:
<svg>
<defs>
<g id="shape">
<circle cx="100" cy="100" r="100" />
</g>
</defs>
<use xlink:href="#shape" x="10" y="10" />
</svg>
SVG 2 (when implemented in browsers) will allow to reference another SVG file without any fragment identifier:
New in SVG 2: An href without a fragment allows an entire SVG document to be referenced without having to ensure that it has an ID on its root element.
Before (SVG 1.1):
<!-- my-vector.svg -->
<svg id="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<circle r="10" cx="12" cy="12" />
</svg>
<use href="my-vector.svg#icon"></use>
After (there will be no need to define id="..." on the svg):
<!-- my-vector.svg -->
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<circle r="10" cx="12" cy="12" />
</svg>
<use href="my-vector.svg"></use>
SVG 2 seems to be in the process of development in major browsers (see this Chrome feature and specifically this Chromium issue: Issue 366545: [SVG2] Allow to reference entire files).
I have a SVG canvas which user can select and resize some <image> elements inside. And I use preserveAspectRatio="xMidYMid meet" attribute value to keep the original aspect ratio. The original image sources are mostly 256x256px size. On Firefox and IE-11, when I resize <image> elements to a smaller size than their original size, they look very pixelated. On Chrome they look pretty smooth. I wonder if there are any css or svg features which could help me to make them look smoother on Firefox and IE too.
Thank you.
EDIT: Adding sample..
https://jsfiddle.net/p8km6nhc/7/
<svg viewBox="-170 -87 1678 869" style="width: 100%; height: 100%; overflow: hidden; left: 0px; top: -0.800003px;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<defs>
<filter id="varlikItemShadow1">
<feGaussianBlur stdDeviation="3" in="SourceGraphic"></feGaussianBlur>
<feOffset dy="1" dx="1"></feOffset>
<feMerge>
<feMergeNode></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
</defs>
<g>
<g transform="matrix(1,0,0,1,0,0)">
<g transform="matrix(1,0,0,1,158,256)">
<g title="" data-original-title="" data-rounded="yes">
<text style="font:0px arial;" x="0" y="1" stroke="none" transform="matrix(1,0,0,1,0,0)" fill="#ffffff" fill-opacity="0.111111">[[VarlikId=3999]]</text>
<rect filter="url(#varlikItemShadow1" stroke="#2b5987" stroke-width="2" fill-opacity="0.9" fill="#ffe8f6" ry="10" rx="10" y="0" x="0" height="140" width="1270"></rect>
<path d="M0 0 L 1268 0 1268 138 0 138Z" stroke="none" stroke-width="0" fill="none" fill-opacity="0" transform="matrix(1,0,0,1,0,0)"></path>
<image image-rendering="optimizeQuality" preserveAspectRatio="xMidYMid meet" x="14" y="14" width="1242px" height="66px" xlink:href="https://deviantshare.azurewebsites.net/img/test.png"></image>
<text style="font:32px arial;" x="0" y="30" stroke="none" transform="matrix(1,0,0,1,591,94)" fill="#2b5987">3. Kat</text>
</g>
</g>
</g>
</g>
</svg>
RESULT :
As it clearly looks like an issue with Firefox and IE rendering, thought of trying a workaround to come over this.
Instead of using <image> element of SVG, tried using <img> tag of HTML and embedded it using <foreignObject> element of SVG.
Instead of :
<image image-rendering="optimizeQuality" preserveAspectRatio="xMidYMid meet"
x="14" y="14" width="1242px" height="66px"
xlink:href="https://deviantshare.azurewebsites.net/img/test.png"></image>
Used:
<foreignObject x="600" y="14" width="100" height="100">
<body>
<img src="https://deviantshare.azurewebsites.net/img/test.png"
type="image/svg+xml" width="66px" height="66px">
</body>
</foreignObject>
But one pending issue is IE doesn't support foreignObject yet!
Codepen.io working example