Origin of CSS transform for svg:use - svg

Is there a way to set the origin of CSS animation (transform - rotation) relatively to x,y of the element <use/> with CSS?
There might be several <use/> in a given <svg/> at different places:
absolute position of each element might not be hard coded in CSS.
.spin {
transition:4s linear;
-webkit-transition:4s linear;
}
.spin:hover {
transform-origin: 0% 0%;
-webkit-transform-origin: 0% 0%;
transform:rotateZ(360eg);
-webkit-transform:rotateZ(360deg);
}
<body>
<svg width="200" height="200" viewBox="0 0 200 200" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" >
<defs>
<path id="shape1" d="M0,0 V20 H20 Z" fill="red" stroke="red" stroke-width="0" />
</defs>
<circle cx="0" cy="0" r="80" fill="none" stroke="green"/>
<use xlink:href="#shape1" x="40" y="40" class="spin" />
</svg>
</body>

Animating the transform of a <use> is complicated by the fact that you are using the x and y attributes. The spec says that:
The x and y properties define an additional transformation (translate(x,y), ...
This extra transform component can mess with the transform you are animating, and produce unexpected effects.
The simple fix is to remove the x and y attributes, and use a parent <g> element to set the position of the path.
<g transform="translate(40,40)">
<use xlink:href="#shape1" class="spin" />
</g>
Updated example
.spin {
transition:4s linear;
-webkit-transition:4s linear;
}
.spin:hover {
transform-origin: 0px 0px;
-webkit-transform-origin: 0px 0px;
transform:rotateZ(360eg);
-webkit-transform:rotateZ(360deg);
}
<body>
<svg width="200" height="200" viewBox="0 0 200 200" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" >
<defs>
<path id="shape1" d="M0,0 V20 H20 Z" fill="red" stroke="red" stroke-width="0" />
</defs>
<circle cx="0" cy="0" r="80" fill="none" stroke="green"/>
<g transform="translate(40,40)">
<use xlink:href="#shape1" class="spin" />
</g>
</svg>
</body>

Related

How to rotate svg circle

I want to use the same svg, but instead want the half circle svg to be rotated 90 degrees. How do I do this? Thanks.
<svg width="100" height="100">
<circle cx="50" cy="50" r="50" fill="grey" />
<path d="M0,50 a1,1 0 0,0 100,0" fill="orange" />
</svg>
SVG syntax
must not use units in the rotate() function
can state the rotation center only as part of the attribute
<svg width="100" height="100">
<circle cx="50" cy="50" r="50" fill="grey" />
<path d="M0,50 a1,1 0 0,0 100,0" transform="rotate(90, 50 50)" fill="orange" />
</svg>
CSS syntax
must use units for rotate() function and transform origin
can state the rotation center only as CSS transform-origin
path {
transform: rotate(90deg);
transform-origin: 50px 50px;
}
<svg width="100" height="100">
<circle cx="50" cy="50" r="50" fill="grey" />
<path d="M0,50 a1,1 0 0,0 100,0" fill="orange" />
</svg>
To use the same SVG (which is what is desired and differentiates this answer from other answers) you can reference the original SVG from a <use> element in a new SVG and apply a CSS rotation.
.rotate90 {
transform: rotate(90deg);
}
<svg id="mySVG" width="100" height="100">
<circle cx="50" cy="50" r="50" fill="grey" />
<path d="M0,50 a1,1 0 0,0 100,0" fill="orange" />
</svg>
<svg class="rotate90" width="100" height="100">
<use href="#mySVG"/>
</svg>

SVG reduced path with fill pattern

I try to draw a shape with a path that must be reduced like in this solution Find Parallel or Offset SVG path
I use the filter "erode" but with pattern it doesn't work : the pattern is deformed.
Is there a way to do this without the bezier.js solution, with pure SVG/CSS ?
Here is a sample of my problem
I want to have the shape of the right with the pattern of the left.
<!DOCTYPE html>
<html>
<body>
<svg>
<defs>
<pattern id="circ" x="0" y="0" width="30" height="30" patternUnits="userSpaceOnUse">
<rect fill="blue" width="100%" height="100%" />
<circle cx="10" cy="10" r="10" fill="green" />
</pattern>
<filter id="erode">
<feMorphology in="SourceGraphic" operator="erode" radius="10"/>
</filter>
<path id="thing" d="M 0,0 H 50 A 35,35 0 1 0 100,50 V 75 C 50,125 0,85 0,85 Z" />
</defs>
<use href="#thing" fill="url(#circ)" width="400" height="400" filter="#erode"/>
<use x="100" href="#thing" filter="url(#erode)" fill="url(#circ)" width="400" height="400" />
</svg>
</body>
</html>
Yes. You can use a mask.
svg {
width: 300px;
}
.purple {
fill: rebeccapurple;
}
.reduce-me {
mask: url(#reducer);
}
#reduce-amount {
stroke-width: 5px;
}
<svg viewBox="0 0 100 100">
<path class="purple"
d="M 50,10 Q 100,10, 50,50 Q 0,90, 50,90
Q 100,90, 50,50 Q 0,10, 50,10 Z"/>
</svg>
<svg viewBox="0 0 100 100">
<defs>
<!-- the shared path that is used by both the purple path and the mask -->
<path id="shared-path"
id="p" d="M 50,10 Q 100,10, 50,50 Q 0,90, 50,90
Q 100,90, 50,50 Q 0,10, 50,10 Z" />
<!-- a mask that shrinks the shape by half the stroke-width -->
<mask id="reducer">
<use id="reduce-amount" xlink:href="#shared-path"
fill="white" stroke="black"/>
</mask>
</defs>
<!-- the shape that gets reduced -->
<use class="purple reduce-me" xlink:href="#shared-path"/>
</svg>
How this works
If we just render what the mask looks like (on the right) we can see how this works.
svg {
width: 300px;
}
.purple {
fill: rebeccapurple;
}
.reduce-me {
mask: url(#reducer);
}
#reduce-amount {
stroke-width: 10px;
}
<svg viewBox="0 0 100 100">
<path class="purple"
d="M 50,10 Q 100,10, 50,50 Q 0,90, 50,90
Q 100,90, 50,50 Q 0,10, 50,10 Z"/>
</svg>
<svg viewBox="0 0 100 100">
<defs>
<!-- the shared path that is used by both the purple path and the mask -->
<path id="shared-path"
id="p" d="M 50,10 Q 100,10, 50,50 Q 0,90, 50,90
Q 100,90, 50,50 Q 0,10, 50,10 Z" />
</defs>
<use id="reduce-amount" xlink:href="#shared-path"
fill="white" stroke="black"/>
</svg>
We are using the same shape as a mask. However the mask has a thick black stroke around it. Black in a mask makes things transparent. The rest of the mask is white, which stays visible.
You can alter the amount of the shape reduction by changing the stroke-width value in the .reduce-amount class.
The disadvantages of this method are:
1. you need a mask for every different path shape
2. you can't set the stroke style of the reduced size shape. However you could simulate a stroke colour by overlaying two paths with different reduction amounts.
You can extend the filter to make this work. There seems to be a bug eroding pattern filled shapes - it's not taking the minimum of the alpha channels in the radius correctly. But if you start with SourceAlpha rather than SourceGraphic and then create your mask using component transfers it seems to work.
<svg>
<defs>
<pattern id="circ" x="0" y="0" width="30" height="30" patternUnits="userSpaceOnUse">
<rect fill="blue" width="100%" height="100%" />
<circle cx="10" cy="10" r="10" fill="green" />
</pattern>
<filter id="erode3">
<feMorphology in="SourceAlpha" result="eroded"
operator="erode" radius="10"/>
<feComponentTransfer>
<feFuncR type="discrete" tableValues="1 0"/>
<feFuncG type="discrete" tableValues="1 0"/>
<feFuncB type="discrete" tableValues="1 0"/>
</feComponentTransfer>
<feComposite operator ="in" in="SourceGraphic"/>
</filter>
<path id="thing" d="M 0,0 H 50 A 35,35 0 1 0 100,50 V 75 C 50,125 0,85 0,85 Z" />
</defs>
<use href="#thing" fill="url(#circ)" width="400" height="400" filter="url(#erode3)"/>
<use x="100" href="#thing" filter="url(#erode)" fill="url(#circ)" width="400" height="400" />
</svg>

svg clipping mask not working

How can I make my clipping masks work?
I have 4 paths positioned at 0,0. I translate them and scale them so they are positioned at the same position as the referenced clipping mask.
Thanks!
<svg xmlns="http://www.w3.org/2000/svg" id="svg" width="600" height="800"
style="margin: 10px; border: 1px solid #ddd; background-color: white">
<g id="grid">
<path d="M1.1,0.4l0-0.3L1.1,0.9L0.5,0.8l0-0.5l0.3,0.2L0.6,0.1l-0.2,0l0,0.6l0.1,0.4L0.3,0.8L0,0.2l0,0.2l0.3,0.3l0.1,0.1"
stroke="black" stroke-width="0.0033333333333333335" fill="black"
transform="translate(0, 0) scale(300, 400) " clip-path="url(#grid-rect-0)"></path>
<path d="M-0.4,0.7l1,0.1l-0.6,0.2l0.1,0l0.4,0L0.4,0.8L0.4,1l0.1,0.1l0-0.4l0.1-0.7l0.1,0.2L0.6,0.9L1.2,0L1.1,1.1l0.4-0.9"
stroke="black" stroke-width="0.0033333333333333335" fill="black"
transform="translate(300, 0) scale(300, 400) " clip-path="url(#grid-rect-1)"></path>
<path d="M0,1l0.5,0L0.4,0.8L0.2,0.9l0.4,0.2l0.2-0.7L0.7,0.7l0-0.6l0.1,0.6L1,0.9l0.1,0L1,0.2L1,1l0.2-0.2L1.1,0.5"
stroke="black" stroke-width="0.0033333333333333335" fill="black"
transform="translate(0, 400) scale(300, 400) " clip-path="url(#grid-rect-2)"></path>
<path d="M0.8,0.8L1,0L0.7,0.7l0.2-0.3L1.1,0L0.7,0.6l0.1-0.4L0.6,0.5l0,0.3l-0.2,0L0.2,0.1l0.4,0.6L0.3,1.1l-0.3,0l0.5-0.6"
stroke="black" stroke-width="0.0033333333333333335" fill="black"
transform="translate(300, 400) scale(300, 400) " clip-path="url(#grid-rect-3)"></path>
</g>
<defs>
<clipPath id="grid-rect-0">
<rect x="0" y="0" width="300" height="400"></rect>
</clipPath>
<clipPath id="grid-rect-1">
<rect x="300" y="0" width="300" height="400"></rect>
</clipPath>
<clipPath id="grid-rect-2">
<rect x="0" y="400" width="300" height="400"></rect>
</clipPath>
<clipPath id="grid-rect-3">
<rect x="300" y="400" width="300" height="400"></rect>
</clipPath>
</defs>
https://jsfiddle.net/eek0bnmv/
The problem is that the translate and scale is also applied to the clip-path when the translate and scale is applied to an element. If you adjust the x/y/width/height on the clip path definition to something like 0.2/0.2/0.5/0.5 then you'll see the clip-path take effect.
You may want to express your clip path using %'s by specifying clipPathUnits="objectBoundingBox" in your clipPath element. That way, you may only need one clipPath definition.

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>

SVG: About using <defs> and <use> with variable text values

I have the following SVG source code that generates a number of boxes with texts:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/2001/REC-SVG-20050904/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="600" height="600">
<defs>
</defs>
<title>Draw</title>
<g transform="translate(50,40)">
<rect width="80" height="30" x="0" y="-20" style="stroke: black; stroke-opacity: 1; stroke-width: 1; fill: #9dc2de" />
<text text-anchor="middle" x="40">Text</text>
</g>
<g transform="translate(150,40)">
<rect width="80" height="30" x="0" y="-20" style="stroke: black; stroke-opacity: 1; stroke-width: 1; fill: #9dc2de" />
<text text-anchor="middle" x="40">Text 2</text>
</g>
<g transform="translate(250,40)">
<rect width="80" height="30" x="0" y="-20" style="stroke: black; stroke-opacity: 1; stroke-width: 1; fill: #9dc2de" />
<text text-anchor="middle" x="40">Text 3</text>
</g>
</svg>
As you can see, I repeated the <g></g> three times to get three such boxes, when SVG has <defs> and <use> elements that allow reusing elements using id references instead of repeating their definitions. Something like:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/2001/REC-SVG-20050904/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="600" height="600">
<defs>
<marker style="overflow:visible;fill:inherit;stroke:inherit"
id="Arrow1Mend" refX="0.0" refY="0.0" orient="auto">
<path transform="scale(0.4) rotate(180) translate(20,0)"
style="fill-rule:evenodd;stroke-width:2.0pt;marker-start:none;"
d="M 0.0,-15.0 L -20.5,0.0 L 0.0,15.0 "/>
</marker>
<line marker-end="url(#Arrow1Mend)" id="systemthread" x1="40" y1="10" x2="40" y2="410" style="stroke: black; stroke-dasharray: 5, 5; stroke-width: 1; "/>
</defs>
<title>Draw</title>
<use xlink:href="#systemthread" transform="translate(50,40)" />
<use xlink:href="#systemthread" transform="translate(150,40)" />
<use xlink:href="#systemthread" transform="translate(250,40)" />
</svg>
Unfortunately I can't do this with the first SVG code since I need the texts to be different for each box, while the <use> tag simply duplicates 100% what's defined in <defs>.
Is there any way to use <defs> and <use> with some kind of parameters/arguments mechanism like function calls?
I was searching for an answer to my own SVG question. Your question helped me solve my answer, so I am answering yours.
.... Read your question more closely. Included TWO code samples
Sample #1. Boxes with text
Sample #2. Arrows with text
Sample 1
<html>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="600" height="900">
<defs>
<g id="my_box"
desc="my rectangle template">
<rect width="80" height="30" x="0" y="-20" style="stroke: black; stroke-opacity: 1; stroke-width: 1; fill: #9dc2de" />
</g>
</defs>
<g transform="translate(50 40)">
<text text-anchor="middle" x="40"> This little box went to market </text>
<use xlink:href="#my_box" />
</g>
<g transform="translate(150 140)">
<use xlink:href="#my_box" />
<text text-anchor="middle" x="40"> This little box stayed home </text>
</g>
<g transform="translate(250 240)">
<use xlink:href="#my_box" />
<text text-anchor="middle" x="40"> This little box had roast beef </text>
</g>
</svg>
</html>
Note in sample 1 that the order of the box and text are important.
Sample 2
<html>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="600" height="900">
<defs>
<g id="arrow"
desc="arrow with a long dashed tail">
<marker style="overflow:visible;fill:inherit;stroke:inherit"
id="Arrow1Mend" refX="0.0" refY="0.0" orient="auto">
<path transform="scale(0.4) rotate(180) translate(20,0)"
style="fill-rule:evenodd;stroke-width:2.0pt;marker-start:none;"
d="M 0.0,-15.0 L -20.5,0.0 L 0.0,15.0 "
desc="The actual commands to draw the arrow head"
/>
</marker>
<line transform="translate(0 -450)"
marker-end="url(#Arrow1Mend)"
x1="40" y1="10" x2="40" y2="410"
style="stroke: black; stroke-dasharray: 5, 5; stroke-width: 1; "
desc="This is the tail of the 'arrow'"
/>
</g>
</defs>
<g transform="translate(100 450)">
<text> Text BEFORE xlink </text>
<use xlink:href="#arrow" />
</g>
<g transform="translate(200 550)">
<use xlink:href="#arrow" />
<text> More to say </text>
</g>
<g transform="translate(300 650)">
<use xlink:href="#arrow" />
<text> The last word </text>
</g>
<g transform="translate(400 750)">
<use xlink:href="#arrow" />
<text> Text AFTER xlink </text>
</g>
</svg>
</html>
A way to achieve this with the current svg recommendation is not known to me.
But there is a working draft for a svg 2.0 module, see: SVG Referenced Parameter Variables. The example with the flowers there is exactly what you are looking for I suppose! But then you probably have to wait until june 2010 or even longer until this is a W3C recommendation and supported by clients I assume.
For now you could probably solve it with scripting.

Resources