Using an SVG I am trying to rotate it by 30 degrees and then translate that horizontally 100. The issue is that when I rotate the coordinate system, it is rotated and so when I try to translate I will be translating the object along the rotated x-axis which is now at 30 degrees to the horizontal.
Is there any way to translate horizontally after rotation?
<g transform="rotate(30 50 50) translate(100 0)">
<rect x="0" y="0" fill="blue" width="100" height="100" />
</g>
I want to create control buttons for rotate and translate up, down, left, right. These will create a string which will use data binding to the transform property of the svg and so transform the object sequentially. But the translations need to be relative to the viewport not how the object has been rotated.
I am using the vue.js framework for the data binding.
I also do not want to use css.
Thanks,
If you want the rotate to happen first, you have to put that last in your transform attribute.
<g transform="translate(100 0) rotate(30 50 50)">
<rect x="0" y="0" fill="blue" width="100" height="100" />
</g>
Why? Because the above snippet is equivalent to
<g transform="translate(100 0)">
<g transform="rotate(30 50 50)">
<rect x="0" y="0" fill="blue" width="100" height="100" />
</g>
</g>
You can think of transforms as happening from the inside out. The rectangle is affected by the inner rotate transform before the outer translate transform.
Or to think of it another way, the outer transform creates a new coordinate system that is shifted 100 to the right. Within that there is a separate coordinate system that is rotated 30 degrees. Into that the rectangle is drawn.
Related
I am needing some help understanding how to "unflip/unrotate" and image fill in SVG for a path. When I fill a path with an image and then rotate and fill the path with an image, the image also flips and rotates. But I'd like to keep the image upright and non-flipped, regardless of the rotation and flipping. The size of the picture is the bounding box of the rotated shape.
So, for example, say I have this path and this picture:
If the path is only rotated (in this case, 315 degrees), it's easy to unrotate the image by just reversing the angle in the pattern that is used for a fill (i.e. 45 degrees).
<svg name="rotate only" x="0" y="0" width="100.08" height="200" overflow="visible" fill="url(#fillImages0sp15)" stroke="#4472C4" stroke-miterlimit="8" stroke-width="2.25">
<defs>
<image id="bgImage" preserveAspectRatio="none" width="159.113" height="159.113" xlink:href="THE IMAGE URL"></image>
<pattern id="fillImages0sp15" x="-38.362" y="11.598" width="159.113" height="159.113" patternTransform="rotate(45,50.04,100)" patternUnits="userSpaceOnUse">
<use xlink:href="#bgImages0sp15"></use>
</pattern>
</defs>
<path d="M0,149.96 25.02,149.96 25.02,0 75.06,0 75.06,149.96 100.08,149.96 50.04,200 Z " transform="rotate(315,50.04,100)"></path>
</svg>
But if there any kind of flip on the path (horizontal, vertical, or both), it doesn't work by just reversing the transformation on the pattern used for the image fill. For example, if the image is rotated 315 degrees and flipped vertical, the path has transform="rotate(45,50.04,100) translate(0,200), scale(1,-1)" for flipping vertically. That works. But the image fill needs to get reset back to be upright and not flipped. So the patternTransform should just be the same transformation. But this isn't working. This is the result I get.
<svg name="flipV" x="0" y="0" width="100.08" height="200" overflow="visible" fill="url(#fillImages0sp14)" stroke="#4472C4" stroke-miterlimit="8" stroke-width="2.25">
<defs>
<image id="bgImages" preserveAspectRatio="none" width="159.113" height="159.113" xlink:href="THE IMAGE URL"></image>
<pattern id="fillImages0sp14" x="-20.671" y="-370.711" width="159.113" height="159.113" patternTransform="rotate(45,50.04,100) translate(0,200) scale(1,-1)" patternUnits="userSpaceOnUse">
<use xlink:href="#bgImages"></use>
</pattern>
</defs>
<path d="M0,149.96 25.02,149.96 25.02,0 75.06,0 75.06,149.96 100.08,149.96 50.04,200 Z " transform="rotate(45,50.04,100) translate(0,200) scale(1,-1)"></path>
</svg>
Notice the path has transform="rotate(45,50.04,100) translate(0,200) scale(1,-1) and the fill pattern with the image has patternTransform="rotate(45,50.04,100) translate(0,200) scale(1,-1). This produces the wrong results.
In fact, here's all of it. This is what I'm hoping to achieve:
Does anyone know how to set the patternTransform so that it can "unflip/unrotate" the filled image? Is it that the translate in the patternTransform needs to be calculated differently?
Rather than filling the arrow, <svg ... fill="url(#fillImages0sp14)", transforming it and then trying to somehow separate it from its fill, untransform that, and then refill it, I'd just display the image, but masked by the transformed arrow.
I don't understand why the orange border still shows up. I've made the black rectangle overly large (which helped), and I've changed the overflow="...", but neither made it disappear.
Edit: Your global stroke attributes were messing up the mask. Moving them to the displayed arrow (the only thing using that stroke) fixed the orange border issue.
P.S. xlink is deprecated. Just use href.
P.P.S. I had to add a final translate to center the transformed arrow over the image. It's easier and more accurate to move the center of the arrow to the center of the image first, and then do your transformations about that center.
<svg name="transformed" x="0" y="0" width="159.113" height="159.113" overflow="visible" xmlns="http://www.w3.org/2000/svg">
<defs>
<path id="Arrow" d="M0,149.96 25.02,149.96 25.02,0 75.06,0 75.06,149.96 100.08,149.96 50.04,200 Z" />
<use id="TransformedArrow" href="#Arrow" transform="translate(38.3625,-29.2893) rotate(45,50.04,100) translate(0,200) scale(1,-1)" />
<mask id="ArrowMask">
<!-- Everything under black will be invisible -->
<rect x="0" y="0" width="100%" height="100%" fill="black" />
<!-- Everything under white will be visible -->
<use href="#TransformedArrow" fill="white" />
</mask>
</defs>
<image width="159.113" height="159.113" mask="url(#ArrowMask)" href="https://i.stack.imgur.com/yDcGi.png" />
<use href="#TransformedArrow" fill="none" stroke="#4472C4" stroke-miterlimit="8" stroke-width="2.25" />
</svg>
I could probably manually fake it using a solid-edged drop shadow filter around the strokes, set to the background color, but that's neither resilient nor ideal.
Visually, instead of this:
I want to have this (if the circle is on top):
A posible solution would be creating a mask with a white rectangle and a black stroked <use> element that is using the circle.
Please note that the white rectangle is covering all the svg element and the stroke-width of the <use> element is wider than the stroke of the circle.
This way you create a hole in the rect that is letting you to see whatever you have in the background.
<svg fill="none" stroke="black" stroke-width="3">
<mask id="m">
<rect width="100%" height="100%" fill="white" />
<use xlink:href="#c" stroke-width="10" />
</mask>
<rect x="10" y="5" width="70" height="70" mask="url(#m)" />
<circle id="c" cx="80" cy="75" r="40" />
</svg>
I have a simple or complex SVG graphic. For example a rotated rectangle.
Without calculating you cannot know the minimal size of the viewbox, where the graphic fits into completely.
<svg viewBox="0 0 30 30">
<rect x="20" y="0" width="100" height="20" transform="rotate(45)" fill="black" />
</svg>
The result is, that the graphic does not fit into the viewbox.
Is there any method, how to get an the minimal size of the viewbox, where the graphic is shown completely?
Ideally I do not want to declare a size/ratio of a viewbox. I just want that the minimal size is a result of the content of the SVG graphics.
Is there any disadvantage, when I do not declare the viewBox attribute at all?
Thanks for your help.
One way to do it is wrapping the transformed rectangle in a <g> element and then get the value of the bounding box for theG. Next you use the values of the bounding box (BB) to reset the viewBox of theSVG. I hope it helps.
// the bounding box for the wrapping g
let BB = theG.getBBox();
theSVG.setAttributeNS(null, "viewBox", `${BB.x} ${BB.y} ${BB.width} ${BB.height}`)
svg{border:1px solid}
<svg id="theSVG" viewBox="0 0 30 30" width="300">
<g id="theG">
<rect x="20" y="0" width="100" height="20" transform="rotate(45)" fill="black" />
</g>
</svg>
I need to virtually divide the svg element into 9 equal grids for text positioning, like x=0, y=10; x=33.33%, y=10; x=0, y=33.33% + 10 and so on.
The last one in css can be done using calc(33.33% + 10) but how do I set it for the x and y of svg text. Is there a way to add percentage to pixel and assign it x and y of svg text or is there an alternative better way to proceed ahead. Please guide.
You need to have a place where you can use percentage values, and another where you can use pixel values. For the case of text elements, this is relatively easy. Position the text with percentage x/y values, and then move it with a transform attribute, which takes unitless numbers interpreted in the local coordinate system:
<svg>
<text x="33.3%" y="0%" transform="translate(0, 10)"></text>
<text x="0" y="33.3%%" transform="translate(5, 15)"></text>
<svg>
If the texts are always in the same place in relation to the grid, you could simplify:
<svg>
<g transform="translate(0, 10)">
<text x="33.3%" y="0%"></text>
<text x="0" y="33.3%%"></text>
</g>
<svg>
In more general cases the best and most semantic strategy might be to nest <svg> elements, where the inner elements represent single cells in the grid:
<svg>
<!-- width and height are not strictly needed for the nested
svg elements, they default to 100% -->
<svg x="33.3%" y="0%" width="33.3%" height="33.3%">
<text x="0" y="10"></text>
<circle cx="50" cy="20" r="20" />
</svg>
<svg x="0%" y="33.3%" width="33.3%" height="33.3%">
<text x="0" y="10"></text>
<rect x="0" cy="0" width="100" height="50" />
</svg>
</svg>
I would like to move a particular composite shape to a <Symbol> definition, and then re-use it. This helps make the SVG code neater, and provides some global control over the actual structure of that shape. The shape is symmetrical about a given mid-point; however, the x/y coordinates of subsequent <Use> statements need to reference the top-left corner rather than its natural original, and this means that all usage of the shape must be aware of its total size. Is there a way to position a Symbol by some origin other than its top-left corner?
Contrived example, purely to explain this better. The following concentric-circle Symbol has a natural origin. However, the subsequent Use statement has to offset its x/y coordinates by half the Symbol size in order to position it correctly (at 20,20 in this example). Ideally, the usage of the symbol should not have to know this information.
<defs>
<symbol id="ex">
<circle fill="green" cx="8" cy="8" r="8"/>
<circle fill="white" cx="8" cy="8" r="6"/>
<circle fill="green" cx="8" cy="8" r="4"/>
</symbol>
</defs>
<use xlink:href="#ex" x="12" y="12">
The downside to a symbol is that it cuts of rendering at the border of a viewport. (which is also an upside, since you can define a viewBox.) But you can avoid using it at all. Everything in a <defs> element is not rendered directly, so you can exchange the <symbol> for a <g> and center everything on the origin:
<defs>
<g id="ex">
<circle fill="green" cx="0" cy="0" r="8"/>
<circle fill="white" cx="0" cy="0" r="6"/>
<circle fill="green" cx="0" cy="0" r="4"/>
</g>
</defs>
<use xlink:href="#ex" x="12" y="12">
http://jsfiddle.net/nuzwn07n/
If I read your question correctly, you want to be able to say
<use xlink:href="#ex" x="20" y="20">
..and then the circle symbol is positioned with its center at [20,20]. The solution I propose requires you to know the size of the symbol, but only once (in the <symbol> declaration), and not on every <use> element.
1: In the <symbol>, put everything in a group which you translate so the center of the graphics lies on the top-left corner.
<symbol id="ex">
<g transform="translate(-8,-8)">
<circle ...
</g>
</symbol>
2: If you now <use> that symbol, you'll only see the quarter circle that's still within the symbol's "viewport". To display the whole circle, simply apply overflow="visible" to the <symbol>.
<symbol id="ex" overflow="visible">
<g transform="translate(-8,-8)">
<circle ...
</g>
</symbol>
http://jsfiddle.net/0ghucsrp/