Setting a background position works on Chrome, Safari and Firefox, but not on IE 8-11. What's wrong here?
DEMO on Dabblet
DEMO on Webdevout
.logo {
display: block;
width: 200px;
border: 2px solid red;
background: url("layout/logo.png") center right no-repeat; /* fallback image */
background-image: url('data:image/svg+xml;base64,...'), none; /* two bg to only use svg on supported browsers. IE 11 uses this image */
background-position: center right;
background-repeat: no-repeat;
background-size: auto 100%;
}
UPDATE:
IE seems to ignore background positioning on SVG images. Here's a WORKAROUND DEMO
I guess the best way of doing this with a guaranty of cross-browser working is by making the background image using a pattern and then set the attributes like x,y,width or height to position it as you want directly or later in jquery via the attr method for example: $("#img1").attr("width","500");
<defs>
<pattern id="img1" patternUnits="userSpaceOnUse" width="100" height="100">
<image xlink:href="layout/logo.png" x="0" y="0" width="100" height="100" />
</pattern>
</defs>
and set the pattern to fill the SVG path like this
<path fill="url(#img1)" id="my_svg" d="M5,50 l0,100 l100,0 l0,-100 l-100,0
M215,100
a50,50 0 1 1 -100,0 50,50 0 1 1 100,0
M265,50
l50,100 l-100,0 l50,-100
z"/>
or you can set the background in this very way but in the CSS like this
#my_svg{
fill:url(#img1);
}
There is also an alternative way which i do not recommend but may be the answer in some cases and that is the transform="translate(x,y)" for the image in pattern or the patternTransform="translate(x,y)" for the pattern which is quit like the css version if it..for example:
<pattern id="img1" patternUnits="userSpaceOnUse" width="100" height="100" patternTransform="translate(0,0)">
<image xlink:href="layout/logo.png" x="0" y="0" width="100" height="100" transform="translate(0,0)" />
</pattern>
anyway there is always good practice to know many different ways of doing something in our job to be able to survive ;-)
Related
I'm trying to build an SVG image with content that is 100% the width of the container, minus 60px for some text.
If I was using HTML, or SVG with javascript, I would have no problem doing this. But I feel like there should be a way to do this using SVG (and CSS if needed).
I want the equivalent of this (Codepen here):
<svg width="100%" height="100%">
<rect fill="#ccc" x="0" y="0" width="100%" height="100%"></rect>
<text x="100%" y="50%" stroke="black" text-anchor="end">Y-axis</text>
<svg width="100%" height="100%">
<!-- This rect represents the entirety of the contents of the graph -->
<rect x="0" y="0" style="width: calc(100% - 60px)" height="100%" fill="#c88"></rect>
</svg>
</svg>
In the above snippet, the inner <rect> resizes to be 100% - 60px the width of the container element. However, this trick only works for a single element - if you replace that <rect> with a complex SVG structure it no longer works.
Things I've tried:
Doing a transform: scale() via CSS on the <rect> - I can't figure out what to put into the scale() to make it behave like 100% - 60px.
Changing the width of the nested <svg> element
<svg width="calc(100% - 60px)"> doesn't work - can't do calc() inside the width attribute
<svg width="100%" style="width: calc(100% - 60px);"> (with or without the width attribute) - doesn't work - the CSS "width" property is ignored whether or not the width attribute is present.
I'm starting to think what I want to do isn't possible right now with SVG, but it doesn't seem like an uncommon use case. Is there any way to do this?
As discussed in the comments, you might have some luck achieving the same by making your graph area 100% of the viewBox, but place the SVG in a container with 60px of padding on the right to account for the text space.
Moving your text (and background rect) to x="100%" with its text-anchor="start", in addition to letting the SVG overflow, you can get a pretty close result without needing to transform your graphic, since you have a fixed 60px value you can consistently rely on:
div {
padding-right: 60px;
}
svg {
overflow: visible;
}
<div>
<svg width="100%" height="100%">
<rect fill="#ccc" x="100%" y="0" width="60px" height="100%"></rect>
<text x="100%" y="50%" stroke="black" text-anchor="start">Y-axis</text>
<rect x="0" y="0" width="100%" height="100%" fill="#c88"></rect>
</svg>
</div>
PS: Maybe you would prefer your text to have text-anchor="middle", and transform it in CSS with transform: translateX(30px) to place it in the centre of the "text" area — might look cleaner that way:
div {
padding-right: 60px;
}
svg {
overflow: visible;
}
text {
transform: translateX(30px);
}
<div>
<svg width="100%" height="100%">
<rect fill="#ccc" x="100%" y="0" width="60px" height="100%"></rect>
<text x="100%" y="50%" stroke="black" text-anchor="middle">Y-axis</text>
<rect x="0" y="0" width="100%" height="100%" fill="#c88"></rect>
</svg>
</div>
I have an SVG:
<svg xmlns="https://www.w3.org/2000/svg" width="100%" height="100%">
<defs>
<pattern id="stripes" patternUnits="userSpaceOnUse" width="7" height="6" patternTransform="rotate(45)">
<line x1="1" y="0" x2="1" y2="7" stroke="#fffa72" stroke-width="1.5" />
</pattern>
</defs>
<rect width="100%" height="100%" fill="#fffeea" />
<rect width="100%" height="100%" fill="url(#stripes)" />
</svg>
It tested it with an external tool and it works nicely:
Now I'd like to use it in background-image. I encoded the SVG with https://www.url-encode-decode.com/
I then take the encoded SVG and put it into my SCSS:
background-image: url('data:image/svg+xml;charset=utf8,%3Csvg+xmlns%3D%22https%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22+width%3D%22100%25%22+height%3D%22100%25%22%3E%0D%0A++%3Cdefs%3E%0D%0A++++%3Cpattern+id%3D%22stripes%22+patternUnits%3D%22userSpaceOnUse%22+width%3D%227%22+height%3D%226%22+patternTransform%3D%22rotate%2845%29%22%3E%0D%0A++++++%3Cline+x1%3D%221%22+y%3D%220%22+x2%3D%221%22+y2%3D%227%22+stroke%3D%22%23fffa72%22+stroke-width%3D%221.5%22+%2F%3E%0D%0A++++%3C%2Fpattern%3E%0D%0A++%3C%2Fdefs%3E%0D%0A++%3Crect+width%3D%22100%25%22+height%3D%22100%25%22+fill%3D%22%23fffeea%22+%2F%3E%0D%0A++%3Crect+width%3D%22100%25%22+height%3D%22100%25%22+fill%3D%22url%28%23stripes%29%22+%2F%3E%0D%0A%3C%2Fsvg%3E');
this doesn't work. the background remain empty. I know my HTML works fine because when I plug a different background image with a SVG I found online, it works nicely:
background-image: url('data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cdefs%3E%3Cpattern%20id%3D%22a%22%20patternUnits%3D%22userSpaceOnUse%22%20width%3D%225%22%20height%3D%225%22%20patternTransform%3D%22rotate(45)%22%3E%3Cpath%20stroke%3D%22%23fffa72%22%20d%3D%22M1%200v5%22%2F%3E%3C%2Fpattern%3E%3C%2Fdefs%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22url(%23a)%22%2F%3E%3C%2Fsvg%3E');
What am I doing wrong?
Your xmlns value is incorrect. It's http://www.w3.org/2000/svg, not https://.
body {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25'%3E%3Cdefs%3E%3Cpattern id='stripes' patternUnits='userSpaceOnUse' width='7' height='6' patternTransform='rotate(45)'%3E%3Cline x1='1' y='0' x2='1' y2='7' stroke='%23fffa72' stroke-width='1.5' /%3E%3C/pattern%3E%3C/defs%3E%3Crect width='100%25' height='100%25' fill='%23fffeea' /%3E%3Crect width='100%25' height='100%25' fill='url(%23stripes)' /%3E%3C/svg%3E");
}
html, body {height: auto;}
<div></div>
Documentation: https://www.w3.org/2000/svg - yep, with https:// :D
In embedded <svg> elements most browsers default the attribute to http://www.w3.org/2000/svg when it doesn't resolve (you can basically delete it and it still works). But they don't do it for background-image base64 encoded SVGs.
In short, it won't work as image if it's invalid.
Maybe this is not the answer to your exact question,
but you might make your background using a linear-gradient with a way simpler code:
body {
background: linear-gradient(135deg, #fff 2px, #fffa72, #fff 5px, #fff 9px, #fffa72, #fff 12px) 0 0 / 10px 10px;
}
Save your svg to a file.svg and try using https://www.developertoolkits.com/base64/encoder
It takes images and turns them into data urls that you can then drop inline or in css like you are trying to do.
The website you are using is a url encoder.
I've created an svg for use as a clip-path on an image, and it appears perfect in Firefox, however it doesn't work in Chrome, and I'm wondering what the problem is.
Chrome should support an inline svg clip-path according to this.
And full support according to MDN.
<style>
img {
width: 40%;
height: auto;
display: inline;
}
.clip {
-webkit-clip-path: url('#clip');
clip-path: url('#clip');
}
</style>
<p>Left image should be clipped, right image is not.</p>
<img src="https://i.imgur.com/nnHdzO6l.jpg" class="clip">
<img src="https://i.imgur.com/nnHdzO6l.jpg" >
<svg version="1.1"
baseProfile="full"
height="400" width="400"
xmlns="http://www.w3.org/2000/svg">
<defs>
<clipPath id="clip"
clipPathUnits="objectBoundingBox"
transform="scale(0.0025, 0.0025)">
<!-- https://css-tricks.com/scaling-svg-clipping-paths-css-use/ -->
<circle cx="50%" cy="50%" r="50%" />
<rect width="82.8%" height="82.8%" y="17.2%" x="8.6%" />
</clipPath>
</defs>
</svg>
External SVG files are not supported by Chrome at the moment.
You can check this here:
https://caniuse.com/#search=CSS%20clip
Here is what they say about the Partial support for Chrome:
Partial support refers to supporting shapes and the url(#foo) syntax
for inline SVG, but not shapes in external SVGs.
Here is a thing.
I have a 700x700px image that i would need to mask with SVG.
For Chrome and Safari i did that by using -webkit-mask-box-image with external SVG and it works properly.
For Firefox, i used clip-path property, and again, it functions properly.
The responsive part is problem.On Chrome&Safari, that part is working nicely, but on Firefox only the main image is resized, mask stays the same.
I am a complete newbie at this and i tried tons of solutions that i found online and i really couldn’t make it work.
<style>
body {
background: yellow;
}
.img-mask {
-webkit-mask-box-image: url('http://imgh.us/mask_3.svg');
mask-border: url('http://imgh.us/mask_3.svg');
clip-path: url(#mask);
}
</style>
<img src="http://gto-live.com/wp-content/uploads/2015/04/charm-elegance-colorful-sofa-living-room-decor-718x718.jpg" class="img-mask">
enter code here
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="700" height="700" viewBox="0 0 700 700">
<clipPath id="mask">
<path d="M718.004,358.999 C718.004,160.726 557.272,-0.007 358.998,-0.007 C160.725,-0.007 -0.007,160.726 -0.007,358.999 C-0.007,557.272 160.725,718.005 358.998,718.005 C557.272,718.005 718.004,557.272 718.004,358.999 Z"/>
</clipPath>
</svg>
Any help would really really be gratefully appreciated!
Fiddle can be found here https://jsfiddle.net/y7zaw4bz/1/
You need to use objectBoundingBox units (and make the path run from 0 to 1) e.g.
body {
background: yellow;
}
img {
width: 100%;
}
.img-mask {
-webkit-mask-box-image: url('http://imgh.us/mask_3.svg');
mask-border: url('http://imgh.us/mask_3.svg');
clip-path: url(#mask);
}
<img src="http://gto-live.com/wp-content/uploads/2015/04/charm-elegance-colorful-sofa-living-room-decor-718x718.jpg" class="img-mask">
<svg preserveAspectRatio="xMidYMid" width="700" height="700" viewBox="0 0 700 700">
<clipPath id="mask" clipPathUnits="objectBoundingBox">
<path transform="scale(0.0014)" d="M718.004,358.999 C718.004,160.726 557.272,-0.007 358.998,-0.007 C160.725,-0.007 -0.007,160.726 -0.007,358.999 C-0.007,557.272 160.725,718.005 358.998,718.005 C557.272,718.005 718.004,557.272 718.004,358.999 Z"/>
</clipPath>
</svg>
Here I've scaled the path to correct the units 0.0014 is roughly 1 / 700
I have created a fairly simple shape using an SVG element which is then put into my CSS using clip-path. It should make the corners rounded for me but for some reason only 1 of the corners does the effect perfectly.
This is the shape:
<svg height="500" width="500">
<path fill="#555555" d="M50,0 L450,0 Q500,0 500,50 L500,400 Q500,450 450,450 L200,450 L175,500 L150,450 L50,450 Q0,450 0,400 L0,50 Q0,0 50,0z" />
</svg>
This is what happens when i use it as a clip-path
body {
background: #555;
}
img {
clip-path: url(#svgPath);
-webkit-clip-path: url(#svgPath);
}
<svg height="0" width="0">
<defs>
<clipPath id="svgPath">
<path fill="#FFFFFF" d="M50,0 L450,0 Q500,0 500,50 L500,400 Q500,450 450,450 L200,450 L175,500 L150,450 L50,450 Q0,450 0,400 L0,50 Q0,0 50,0z" />
</clipPath>
</defs>
</svg>
<img src="https://dummyimage.com/500" />
It seems to work perfectly within FireFox but shows the corners aren't cut correctly in Chrome apart from the bottom right corner.
The default units for the clip-path is userSpaceOnUse and this seems to calculate the coordinates of the path with reference to the root element. This is the reason why the clip-path seems like it is producing an incorrect output. Nullifying the margin and padding on the root element or absolutely positioning the element (like in the below snippet) should solve the issue.
body {
background: #555;
}
img {
position: absolute;
top: 0px;
left: 0px;
clip-path: url(#svgPath);
-webkit-clip-path: url(#svgPath);
}
<svg height="0" width="0">
<defs>
<clipPath id="svgPath">
<path fill="#FFFFFF" d="M50,0 L450,0 Q500,0 500,50 L500,400 Q500,450 450,450 L200,450 L175,500 L150,450 L50,450 Q0,450 0,400 L0,50 Q0,0 50,0z" />
</clipPath>
</defs>
</svg>
<img src="http://lorempixel.com/500/500/" />
However, in a real life scenario the actual element that has to be clipped could be present anywhere within the body and hence I think it is a much better approach to use the objectBoundingBox as the units like in the below snippet:
body {
background: #555;
}
img {
clip-path: url(#svgPath);
-webkit-clip-path: url(#svgPath);
}
<svg height="0" width="0">
<defs>
<clipPath id="svgPath" clipPathUnits="objectBoundingBox">
<path fill="#FFFFFF" d="M0.1,0 L0.9,0 Q1,0 1,0.1 L1,0.8 Q1,0.9 0.9,0.9 L0.4,0.9 L0.35,1 L0.3,0.9 L0.1,0.9 Q0,0.9 0,0.8 L0,0.1 Q0,0 0.1,0z" />
</clipPath>
</defs>
</svg>
<img src="https://dummyimage.com/500" />
As mentioned in the question itself, this behavior is visible only in Chrome and not Firefox for reasons unknown to me. Firefox produces an output similar to the expected one even when (a) extra padding + margin is added to the body and (b) when the image itself is wrapped inside another container which also has padding + margin.
The only case where Firefox's output matches with Chrome is when a padding is added directly to the img tag itself. I believe this happens because padding is part of the element and thus affects the coordinates.