SVG & XLink: Rendering of Circle is inconsistent? - svg

I have inline SVG showing a simple circle, which works as expected:
<svg width="200" height="200" viewBox="-25 -25 50 50" >
<circle cx="0" cy="0" r="15" />
</svg>
This same code renders differently if I include the circle via XLink:
<svg width="200" height="200" viewBox="-25 -25 50 50">
<use xlink:href="#circle"/>
</svg>
Here is an example: Fiddle
Why would these two examples render differently?
Thanks.

Simply add overflow="visible" to your symbol
refined answer based on Robert Longson's Comment:
Even better is to move your circle inside the symbol to a position so that it's inside the viewport.
<svg style="display:none;">
<symbol id="circle">
<circle cx="25" cy="25" r="15" />
</symbol>
</svg>
<svg width="200" height="200" viewBox="-25 -25 50 50">
<use x="-25" y="-25" xlink:href="#circle" />
</svg>
<svg width="200" height="200" viewBox="-25 -25 50 50">
<circle cx="0" cy="0" r="15" />
</svg>

Related

svg changes when converted to symbol

I have this dummy svg showing a cirle with some grey figure inside
<svg viewBox="0 0 86 86" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<circle id="path-1" cx="43" cy="43" r="43"></circle>
</defs>
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g transform="translate(-585.000000, -391.000000)">
<g transform="translate(585.000000, 391.000000)">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<use id="Oval" fill="currentcolor" xlink:href="#path-1"></use>
<g id="Group" mask="url(#mask-2)" fill="#b8b8b8">
<g transform="translate(21.500000, 27.823529)" id="Page-1">
<path d="M0.5,0.176470588 L0.5,58.1764706 L15.5,58.1764706 L15.5,49.1764706 L29.5,49.1764706 L29.5,31.1764706 L7.5,31.1764706 L7.5,37.1764706 Z"></path>
</g>
</g>
</g>
</g>
</g>
when I convert it to a sprite using gulp-svg-sprite with mode symbol I get this result
<?xml version="1.0" encoding="UTF-8" ?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><symbol viewBox="0 0 86 86" id="company"><defs><circle id="aa" cx="43" cy="43" r="43"/></defs><g fill="none" fill-rule="evenodd"><mask id="ab" fill="#fff"><use xlink:href="#aa"/></mask><use fill="currentcolor" xlink:href="#aa"/><g mask="url(#ab)" fill="#b8b8b8"><path d="M22 28v58h15v-9h14V59H29v6z"/></g></g></symbol></svg>
Now the grey figure breaks out of the circle and this even happens if I copy root defs- and g-tags from my original svg directly into the symbol-tag. I have also tried inserting a clipPath in the symbol version but with no luck.
What am I missing here?
UPDATED: Simplifying your svg may work ... try with the examples below, one using symbols, one without (in case the gulp sprite code cannot have nested symbols) ... it may be an issue with the defs area.
svg {
width: 100px;
}
<svg viewBox="0 0 86 86" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<symbol id="c">
<circle cx="43" cy="43" r="43" fill="currentColor" />
</symbol>
<mask id="mask" color="#fff">
<use xlink:href="#c" />
</mask>
</defs>
<use xlink:href="#c" color="#666" />
<path fill="#999" mask="url(#mask)" d="M10 0v60h30v-10h20v-20h-30v9z" />
</svg>
<svg viewBox="0 0 86 86" xmlns="http://www.w3.org/2000/svg">
<defs>
<mask id="mask2" color="#fff">
<circle cx="43" cy="43" r="43" fill="#FFF" />
</mask>
</defs>
<circle cx="43" cy="43" r="43" fill="currentColor" />
<path fill="#999" mask="url(#mask2)" d="M10 0v60h30v-10h20v-20h-30v9z" />
</svg>
It is probably not a complete answer but I managed to get it work;
First I extended my defs with a clipPath using my circle
<defs>
<circle id="circle" cx="43" cy="43" r="43"></circle>
<clipPath id="clippath"><use overflow="visible" xlink:href="#circle" /></clipPath>
</defs>
Then, using the clipPath on my first group and switching from gulp-svg-sprite to gulp-svgstore (which moves the defs to the top, above the symbols) gives me what I expect both as single svg and as an svg symbol sprite.

How can I construct an SVG scene with user coordinates to fill the screen

I am displaying a diagrammatic map using SVG and it must be proportional (i.e., not skewed x/y).
The image fills the whole screen so the outer item is always 100%/100%. In other words I want the viewport to always fill the browser windows (within the constraints of aspect ratio), for example:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:ev="http://www.w3.org/2001/xml-events" >
<defs>
<solidColor xml:id="solidBlack" solid-color="black" />
<solidColor xml:id="solidLightGray" solid-color="lightgrey" />
</defs>
<style>
.Border { fill:none; stroke:red; stroke-width="2px" }
.TitleBlock { fill:none; stroke:black; stroke-width="1px" }
</style>
<svg x="0" y="0" width="100%" height="100%" viewbox="0 0 1000 1000" >
<rect x="2" y="2" width="99%" height="99%" class="Border" />
<svg viewbox="500 500 500 500">
<circle cx="30" cy="20" r="10px" fill="red" />
<circle cx="450" cy="30" r="10px" fill="blue" />
<circle cx="40" cy="490" r="10px" fill="green" />
</svg>
</svg>
</svg>
When this renders, the values are being interpreted as pixels, not user coordinates. So, for example, the green dot should be near the bottom of the screen (since it is at the bottom of the viewbox) but in actuality it is in the middle of the screen because it is interpreting 490 as 490 pixels.
To further clarify, the 1000 x 1000 dimensions of the total map area are user coordinates. For example, suppose I am making a space game and the game area is 1000 parsecs x 1000 parsecs. Any particular view of this map area should fill the screen.
Update
Based on the answer one fundamental problem is that to fill the screen I need to use 100% in the outer svg element. However, even after doing this, the image is not zooming in and using user coordinates:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:ev="http://www.w3.org/2001/xml-events"
width="100%" height="100%"
viewBox="0 0 100000 100000" >
<defs>
<solidColor xml:id="solidBlack" solid-color="black" />
<solidColor xml:id="solidLightGray" solid-color="lightgrey" />
</defs>
<style>
.Border { fill:none; stroke:red; stroke-width="2px"; }
.TitleBlock { fill:none; stroke:black; stroke-width="1px"; }
}
</style>
<rect y="2" width="100%" height="100%" class="Border" />
<svg viewBox="50000 50000 100000 100000" >
<circle cx="52000" cy="52000" r="10px" fill="red" />
<circle cx="95000" cy="53000" r="10px" fill="blue" />
<circle cx="54000" cy="99000" r="10px" fill="green" />
</svg>
</svg>
This does not even appear at all, indicating it is using PIXELS not user coordinates.
I think you might be confused about how viewBox works. It describes the bounds of the document that should be scaled to fit the viewport (width & height). It doesn't perform a transform.
Your viewBox of "500 500 500 500" describes the area (500,500) -> (1000,1000). So your coordinate of (10,490) will normally end up way outside the viewport.
Having said that, it is not at all clear what you are actually trying to achieve with your nested set of <svg> elements.
I'm going to assume you are wanting to display a portion of your 1000x1000 world map so it fills the screen? Lets' start by simplifying your original SVG.
.Border { fill:none; stroke:red; stroke-width="2px" }
.TitleBlock { fill:none; stroke:black; stroke-width="1px" }
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:ev="http://www.w3.org/2001/xml-events"
width="100%" height="100%"
viewBox="0 0 1000 1000">
<defs>
<solidColor xml:id="solidBlack" solid-color="black" />
<solidColor xml:id="solidLightGray" solid-color="lightgrey" />
</defs>
<rect x="2" y="2" width="99%" height="99%" class="Border" />
<circle cx="30" cy="20" r="10px" fill="red" />
<circle cx="450" cy="30" r="10px" fill="blue" />
<circle cx="40" cy="490" r="10px" fill="green" />
</svg>
Here we are displaying the whole world map in an SVG that is 100%x100%. Due to the way percentage width/height values are processed for SVGs and because the viewBox is square. This normally results in an SVG that is 100% width and whose height is equivalent to the width, resulting in an SVG that may end up off the bottom of the page.
If you want the SVG viewport to match the screen exactly you should use a width/height in pixels, not percents. For the sake of fitting in the snippet window, let's choose a tiny screen of 512x384 (1/2 of a 1024x768 screen).
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="512" height="384"
viewBox="0 0 1000 1000">
<rect x="-50%" y="-50" width="200%" height="200%" fill="#ffc"/>
<rect x="2" y="2" width="99%" height="99%" fill="none" stroke="red" stroke-width="2" />
<circle cx="30" cy="20" r="10px" fill="red" />
<circle cx="450" cy="30" r="10px" fill="blue" />
<circle cx="40" cy="490" r="10px" fill="green" />
</svg>
Here I've also added a light yellow rectangle to show the dimensions of the viewport (screen) and how the viewBox has resulted in the "world" being scaled to fit the screen.
If you want the world to fill the screen and not have the blank areas to the left and right you need to change the preserveAspectRatio attribute. By default it is set to xMidYMid meet which means scale the viewBox area up to neatly fit the viewport and centre it. If we change it to xMidYMid slice, it will scale it up to fill the widest part of the viewport. That results in the top and bottom of your "world" going off screen.
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="512" height="384"
viewBox="0 0 1000 1000"
preserveAspectRatio="xMidYMid slice">
<rect x="-50%" y="-50" width="200%" height="200%" fill="#ffc"/>
<rect x="2" y="2" width="99%" height="99%" fill="none" stroke="red" stroke-width="2" />
<circle cx="30" cy="20" r="10px" fill="red" />
<circle cx="450" cy="30" r="10px" fill="blue" />
<circle cx="40" cy="490" r="10px" fill="green" />
</svg>
Now, if you want to view a portion of your "world", for example the top left, you would set the viewBox to "0 0 500 500". If you want to see the whole of that area, use `preserveAspectRatio="xMidYMid meet":
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="512" height="384"
viewBox="0 0 500 500"
preserveAspectRatio="xMidYMid meet">
<rect x="-50%" y="-50" width="200%" height="200%" fill="#ffc"/>
<rect x="2" y="2" width="99%" height="99%" fill="none" stroke="red" stroke-width="2" />
<circle cx="30" cy="20" r="10px" fill="red" />
<circle cx="450" cy="30" r="10px" fill="blue" />
<circle cx="40" cy="490" r="10px" fill="green" />
</svg>
If you don't want to see parts of the world outside of that 500x500 area, and can live with part of it being offscreen, use xMidYMid slice.
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="512" height="384"
viewBox="0 0 500 500"
preserveAspectRatio="xMidYMid slice">
<rect x="-50%" y="-50" width="200%" height="200%" fill="#ffc"/>
<rect x="2" y="2" width="99%" height="99%" fill="none" stroke="red" stroke-width="2" />
<circle cx="30" cy="20" r="10px" fill="red" />
<circle cx="450" cy="30" r="10px" fill="blue" />
<circle cx="40" cy="490" r="10px" fill="green" />
</svg>
Here, your three circles end up just off screen.
Does this help?

SVG: set fill color for part of a shape

I have an ellipse shape, which I want to split into 4 sections. Is it possible to independently color these sections or is it necessary to create 4 separate custom shapes ?
(I'd post a picture but I don't have the rep yet). Hope its clear what I mean.
Edit you can see code here http://jsfiddle.net/KQ3eH/
<html>
<head>
<style>
.anellipse {
fill:rgb(255,255,255);
stroke-width:2;
stroke:rgb(0,0,0);
}
.aline {
stroke:rgb(0,0,0);
stroke-width:2;
}
</style>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="960"
height="640">
<ellipse class="anellipse" cx="480" cy="320" rx="478" ry="318"/>
<line class="aline" x1="2" y1="320" x2="958" y2="320" stroke-linecap="round" stroke-dasharray="1, 4"/>
<line class="aline" x1="480" y1="2" x2="480" y2="638" stroke-linecap="round" stroke-dasharray="1, 4"/>
</svg>
</body>
</html>
You can do multicolor fills using a pattern.
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="960" height="640">
<defs>
<pattern id="pat" width="1" height="1" viewBox="0 0 1 1" preserveAspectRatio="none">
<rect width=".5" height=".5" fill="red" />
<rect x=".5" width=".5" height=".5" fill="green" />
<rect y=".5" width=".5" height=".5" fill="orange" />
<rect x=".5" y=".5" width=".5" height=".5" fill="blue" />
</pattern>
</defs>
<ellipse class="anellipse" cx="480" cy="320" rx="478" ry="318" fill="url(#pat)"/>
<line class="aline" x1="2" y1="320" x2="958" y2="320" stroke-linecap="round" stroke-dasharray="1, 4" />
<line class="aline" x1="480" y1="2" x2="480" y2="638" stroke-linecap="round" stroke-dasharray="1, 4" />
</svg>
See example.
You can't do multi-colour fills (well not more than two colours anyway). So you basically have to split it up into four sections.
Splitting up the ellipse shape itself is a bit tricky. A much easier way would be to draw four coloured rectangles and use the ellipse as a clipping path.
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="960" height="640">
<defs>
<clipPath id="myell">
<ellipse class="anellipse" cx="480" cy="320" rx="478" ry="318" />
</clipPath>
</defs>
<g clip-path="url(#myell)">
<rect x="2" y="2" width="478" height="318" fill="red"/>
<rect x="480" y="2" width="478" height="318" fill="green"/>
<rect x="2" y="320" width="478" height="318" fill="orange"/>
<rect x="480" y="320" width="478" height="318" fill="blue"/>
</g>
</svg>
Demo here

How can i scale a shape without scaling its pattern?

I have an svg shape which uses a pattern. I want the pattern to NOT scale when i scale the shape.
Here's a fiddle with a minimal example, the bigger circle should show the pattern like the smaller one:
http://jsfiddle.net/cTMrQ/6/
<svg style="position: absolute" width="100%" height="100%" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events">
<defs>
<pattern id="checkerPattern" patternUnits="userSpaceOnUse" x="0" y="0" width="4" height="4">
<image x="0" y="0" xlink:href="http://inwonderland.at/new/lines.png" width="4" height="4" />
</pattern>
<circle fill="url(#checkerPattern)" id="c" cx="50" cy="50" r="50" />
</defs>
<use x="100" y="100" xlink:href="#c" />
<use x="200" y="100" xlink:href="#c" transform="scale(2)" />
</svg>
In the end the shape will be a complex path and the image in the pattern will be a scan of a piece of paper, so just drawing a bigger circle instead of scaling it won't work.
Update
To clarify what i want, here are two images:
this is what it looks like, no matter what i try, when i scale the shape:
http://inwonderland.at/new/ihave.png
this is what i want:
http://inwonderland.at/new/iwant.png
i want the background image (bitmap image) to always have its natural size.
You can't get what you want using a pattern, the transform always happens after the fill, and you can't just move the pattern fill into a wrapper either. My suggestion is to use a filter and apply the filter on a wrapper - like so:
<svg style="position: absolute" width="100%" height="100%" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events">
<defs>
<circle fill="url(#checkerPattern)" id="c1" cx="50" cy="50" r="50" />
<filter id="linepattern" x="0%" y="0%" height="100%" width="100%">
<feImage xlink:href="http://inwonderland.at/new/lines.png" result="pattern" width="4" height="4"/>
<feTile/>
<feComposite operator="in" in2="SourceGraphic"/>
</filter>
</defs>
<use filter="url(#linepattern)" x="100" y="100" xlink:href="#c1" />
<use filter="url(#linepattern)" x="200" y="100" xlink:href="#c1" transform="scale(2)" />
<g filter="url(#linepattern)">
<use x="50" y="100" xlink:href="#c1" transform="scale(2)" />
</g>
</svg>
Using viewport
1:1 no zoom
<svg width="800" height="400" viewBox="0 0 800 400">
2:1 zoom double size
<svg width="800" height="400" viewBox="0 0 400 200">
The following elements can use the viewBox attribute
<svg>
<symbol>
<image>
<marker>
<pattern>
<view>
viewbox is fully animatable; and you can zoom into any center point.
<animate attributeName="viewBox" begin="1s" dur="1s"
values="0 0 600 400; 250 180 300 200" fill="freeze" />
Transform a parent tag
Yes an SVG can be a child element but more commonly shapes made with multible tags are placed inside a group tag.
Transform scale can be used with tags which are parents IE the group tag.
<g transform="scale(1.5)">
/* draw your shape inside the g tag */
<use x="100" y="100" xlink:href="#c" />
<use x="200" y="100" xlink:href="#c" />
</g>
So using your above example scale the shape in a parent tag.
Update
To scale image but not patterns in other words move patterns, or icons, on background image that scales.
<g transform="scale(2)">
/* draw your shape inside the g tag */
<use x="100" y="100" xlink:href="#c" transform="scale(.5)" />
<use x="200" y="100" xlink:href="#c" transform="scale(.5)"/>
</g>
Update full svg
I had to move things around a bit, One full size, (lets call it a map), with an overlay of 1 half size map in the upper left corner. setting the full screen to render between 0 and max of 600. Setting a viewport the same but with the width set to 300 scales it down. I do need to double the radius for this example of scaling.
<svg viewBox="0 0 600 600" style="position: absolute" width="100%" height="100%" version="1.1" baseProfile="full"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events">
<defs>
<pattern id="checkerPattern" patternUnits="userSpaceOnUse" x="0" y="0" width="4" height="4">
<image x="0" y="0" xlink:href="http://inwonderland.at/new/lines.png" width="4" height="4" />
</pattern>
<circle fill="url(#checkerPattern)" id="c" cx="50" cy="50" r="50" />
<circle fill="url(#checkerPattern)" id="c2" cx="50" cy="50" r="100" />
</defs>
<use x="100" y="100" xlink:href="#c" transform="scale(.5)"/>
<use x="200" y="100" xlink:href="#c" transform="scale(1)"/>
<rect width="600" height="600" style="fill: none; stroke: black;" />
<svg viewBox="0 0 600 600" width="300" height="300" x="300">
<use x="100" y="100" xlink:href="#c2" transform="scale(.5)"/>
<use x="200" y="100" xlink:href="#c2" transform="scale(1)"/>
<rect width="600" height="600" style="fill: none; stroke: black;" />
</svg>
</svg>
This example is scaled using the same circle pattern. The radius does not need to be changed here because the location is not in the tag being scaled. I'm making use of svg tags here but other tags can be used.
<svg viewBox="0 0 600 600" style="position: absolute" width="100%" height="100%" version="1.1" baseProfile="full"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events">
<defs>
<pattern id="checkerPattern" patternUnits="userSpaceOnUse" x="0" y="0" width="4" height="4">
<image x="0" y="0" xlink:href="http://inwonderland.at/new/lines.png" width="4" height="4" />
</pattern>
<circle fill="url(#checkerPattern)" id="c" r="50" cx="50" cy="50" />
</defs>
<svg x="100" y="100"><use xlink:href="#c" transform="scale(.5)"/></svg>
<svg x="200" y="100"><use xlink:href="#c" transform="scale(1)"/></svg>
<rect width="600" height="600" style="fill: none; stroke: black;" />
<svg viewBox="0 0 600 600" width="300" height="300" x="300">
<svg x="100" y="100"><use xlink:href="#c" transform="scale(1)"/></svg>
<svg x="200" y="100"><use xlink:href="#c" transform="scale(2)"/></svg>
<rect width="600" height="600" style="fill: none; stroke: black;" />
</svg>
</svg>

How to Exclude Area inside of clippath in Svg

I want to hide anything that outside a rectangle. (this i have achieved with clipping successfully). but another condition is that, 'also hide anything that comes inside the black big circle'. Now how i can achieve that?
in below example, 'yellow circle' must be eliminated'.
see below images for detail
Original:-
Desired:-
Below is my Svg code:-
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="500" height="500">
<g>
<rect x="50" y="50" width="200" height="200" stroke="1" fill="red"/>
<circle cx="180" cy="150" r="30" stroke="blue" />
</g>
<g clip-path = "url(#clip1)">
<circle cx="180" cy="150" r="10" stroke="blue" fill="yellow" />
</g>
<clipPath id = "clip1">
<rect x="50" y="50" width="200" height="200" stroke="1" fill="red"/>
</clipPath>
</svg>
Erik Dahlström is right, your clip can include the entire rectangle and the cutout for the circle. This way, anything you associate with #clip1 as the clip-path will not be visible inside your circle area. Here is what it looks like for your example:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="500" height="500">
<g>
<rect x="50" y="50" width="200" height="200" stroke="1" fill="red"/>
<circle cx="180" cy="150" r="30" stroke="blue" />
</g>
<g clip-path = "url(#clip1)">
<circle cx="180" cy="150" r="10" stroke="blue" fill="yellow" />
</g>
<clipPath id = "clip1">
<path d="M 50,50 l200 0 l0 200 l-200 0z M150,150 a30,30 1 0,0 60,0z M210,150 a30,30 1 0,0 -60,0z"/>
</clipPath>

Resources