How to make SVG scale only on X axis? - svg

I have the following SVG graphic that is currently scaling when the window is resized, but the aspect ratio is maintained. How could I get this to only scale on the X axis, and keep the Y at 80px?
<svg width="100%" viewBox="0 0 300 80">
<rect x="0" y="0" fill="yellow" height="80" width="100"/>
<rect x="100" y="0" fill="blue" height="80" width="100"/>
<rect x="200" y="0" fill="red" height="80" width="100"/>
</svg>
Thank you,

You have a couple of options. First, you could simply specify the height of the graphic, e.g. using CSS.
svg {
height: 80px;
width: 100%;
}
If that's not the effect you want, you can get more sophisticated with the preserveAspectRatio attribute. It's hard to say what value would work for you since it's not completely clear what you want (assuming the CSS approach above doesn't do it), but maybe something like:
<svg viewBox="0 0 300 80" preserveAspectRatio="none">
Check out the reference link for more details.

Related

Understand SVG viewport

I'm trying to understand the SVG viewport. Why are these three examples so different?
svg {
border: 2px solid red;
}
<div>
<svg>
<rect x="0" y="0" width="100" height="100" fill="blue" />
</svg>
<svg width="200" height="200">
<rect x="0" y="0" width="100" height="100" fill="blue" />
</svg>
<svg viewBox="0 0 200 200">
<rect x="0" y="0" width="100" height="100" fill="blue" />
</svg>
</div>
The first example is missing a width and height. The CSS specification says that replaced elements missing width and height values fallback to 300px x 150px so you'd see the top left 300 x 150 px of whatever you're drawing onto the canvas.
The second example has width and height so you'd see that part of the canvas.
The third example also has no width/height but oddly we're now going to use the 100% x 100% lacuna values for the width/height because we have a usable aspect ratio from the viewBox. The viewBox also scales the 200 x 200 internal co-ordinate system to fix into that canvas so everything looks bigger.

Why does Webkit/Safari (iOS, macOS) render my SVG transformations in a different order compared to Blink and Gecko?

I have a minimal SVG image of an asterisk icon:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<defs>
<style>
.a {
fill: red;
transform-box: fill-box;
transform-origin: center;
}
</style>
</defs>
<!-- x = 45 because it's 10px wide and centered around x=50, so x0 == 45, x1 == 55 -->
<rect class="a" x="45" y="0" width="10" height="100"/>
<rect class="a" x="45" y="0" width="10" height="100" transform="rotate(-45)"/>
<rect class="a" x="45" y="0" width="10" height="100" transform="rotate( 45)"/>
<rect class="a" x="45" y="0" width="10" height="100" transform="rotate(-90)"/>
</svg>
This renders correctly in Firefox 75 and Chrome 80, but not Safari on iOS nor macOS:
Firefox 75:
Chrome 80:
Safari on iOS 13
But on Safari, the transform-origin is seemingly ignored, or applied out-of-order compared to the transform="" attribute on each of the <rect> elements:
I'm told it renders the same broken image on macOS Safari (I don't have immediate access to macOS Safari right now so I can't post a screenshot from there, sorry).
I've searched around Google to see if Safari/WebKit is lacking support for transform-box, transform-origin or transform="rotate(deg)" but as far as I can tell they've all been fully supported by Safari for years - so I don't understand why it's applying the transformations out-of-order and causing the broken rendering.
After fiddling with the SVG in Safari, Safari's Web Inspector does seem to recognize the transform-origin SVG style property, but it doesn't actually use it when applying transformations, which is weird (e.g. using transform-origin: center; or transform-origin: 0px 0px or transform-origin: 50px 50px had no effect) - so a fix lies in changing the rotation centre using some other means.
As far as I can tell, macOS Safari and iOS Safari both only use transform-origin for CSS-based transformations using the transform property and not when using SVG attribute-based transformations using transform="" - whereas Blink and Gecko both use transform-origin for both attribute-based and CSS-based transformations.
Using a translate step
One approach is to add a translate step so the centre of rotation is centred in the canvas, then perform the rotation, then undo the translate step:
transform="translate( 50, 50 ) rotate(45) translate( -50, -50 )"
Like so:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
<rect fill="red" x="45" y="0" width="10" height="100" />
<rect fill="blue" x="45" y="0" width="10" height="100" transform="translate(50,50) rotate(-45) translate(-50,-50)" />
<rect fill="green" x="45" y="0" width="10" height="100" transform="translate(50,50) rotate( 45) translate(-50,-50)" />
<rect fill="orange" x="45" y="0" width="10" height="100" transform="translate(50,50) rotate(-90) translate(-50,-50)" />
</svg>
Better approach: Using rotate( angle, x, y ):
Rather than applying an initial translate step to the transform chain, I saw that the rotate transformation function supports specifying a centre-of-rotation using additional arguments - so this works in Safari, Chrome (+Edge +Opera), and Firefox:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
<rect fill="red" x="45" y="0" width="10" height="100" />
<rect fill="blue" x="45" y="0" width="10" height="100" transform="rotate(-45, 50, 50)" />
<rect fill="green" x="45" y="0" width="10" height="100" transform="rotate( 45, 50, 50)" />
<rect fill="orange" x="45" y="0" width="10" height="100" transform="rotate(-90, 50, 50)" />
</svg>

How to make SVG scaling correctly?

I have an SVG like this :
<svg
xmlns="http://www.w3.org/2000/svg"
id="demo"
style="bottom:0px;left:0px;right:0px;top:0px;margin:auto;position:absolute;"
viewBox="0 0 40 28">
<rect x="0" y="0" width="40" height="14" fill="blue"/>
<rect x="0" y="14" width="40" height="14" fill="green"/>
</svg>
I would expect whatever its size, it will contains only pure blue or pure green.
However, that's not the case :
The same with zoom on it :
As you can see, there this half-transparent white border between blue and green rectangle.
Where does it come from ? Can I avoid it ?
Thank you.
You can turn off antialiasing if you want via shapeRendering:crispEdges. You might not like the result if you apply it to things that do not consist solely of vertical or horizontal lines though.
The viewBox also forces the shape to maintain an aspect ratio which can cause the edges to be blank to maintain that aspect ratio. Again you can turn that off via preserveAspectRatio="none" but your shape will then distort to match the container aspect ratio.
<svg
xmlns="http://www.w3.org/2000/svg"
id="demo"
style="bottom:0px;left:0px;right:0px;top:0px;margin:auto;position:absolute;shape-rendering:crispEdges"
viewBox="0 0 40 28" preserveAspectRatio="none">
<rect x="0" y="0" width="40" height="14" fill="blue"/>
<rect x="0" y="14" width="40" height="14" fill="green"/>
</svg>
The issue is anti-aliasing. It's a characteristic of the renderer, not your SVG representation. There's no real answer, and there's not meant to be - a pixel is an image sample, not a little square.

Mask containing pattern not working in SVG

Based on this example: https://bl.ocks.org/jfsiii/7772281
I am trying to fill an SVG shape with a masked pattern.
I have two boxes. The first takes just the dot pattern. This works, but the color of the dots cannot be changed in css.
I have a second box, which has a fill and is then masked with the same pattern. This ought to result in blue dots.
But it's not masking properly. Or at least, it's doing so inconsistently. The first box will show in Chrome, but the second one will not, at least not until I go to inspect it. Then it decides to turn on. :/. Both show up in Firefox correctly.
Since the example I borrowed from works fine in Chrome, I assume I am doing something wrong.
<style>
.mask-dots {
mask: url(#mask-dots);
}
.pattern-dots {
fill: url(#pattern-dots)
}
.blue {
fill: blue;
</style>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="393px"
height="528px" viewBox="0 0 393 528" enable-background="new 0 0 393 528" xml:space="preserve">
<g id="underskirt-4">
<rect class="pattern-dots" width="100" height="100"/>
<rect x="110" class="blue mask-dots" width="100" height="100"/>
</g>
</svg>
<svg><defs><pattern id="pattern-dots" width="50" height="50"
patternUnits="userSpaceOnUse">
<path fill="white" d="M15.946,12.651c0,1.905-1.544,3.45-3.45,3.45c-0.953,0-1.815-0.386-2.439-1.011
c-0.624-0.624-1.01-1.486-1.01-2.439c0-1.905,1.544-3.45,3.45-3.45S15.946,10.746,15.946,12.651z"/>
</pattern>
<mask id="mask-dots">
<rect x="0" y="0" width="100%" height="100%" fill="url(#pattern-dots)" />
</mask>
</defs>
</svg>
Jsfiddle, with second box not displaying (at least not in Chrome.)
https://jsfiddle.net/qux8yt9g/

How I can get text with desired height and width?

I need to get text with desired height and width.
I tried to find something in documentation of svg but found only font-size and also I tried to use scale in such manner:
<text xmlns="http://www.w3.org/2000/svg" id="10996080909940" name="-1"
x="1782.9351809218" y="-751.796133712862" width="1" height="1" style="font:Arial;text-
anchor:start;stroke:#000000" transform="rotate(0) scale(2 2)"> SOME TEXT </text>
But I get too big size of text and in place not where I need.
If you mean you want the text to exactly fill an arbitrary width and height, then there isn't really an easy way to do it in SVG. You can't specifiy a width and height on the <text> element. At least not in the current SVG spec (1.1).
However there are several ways to achieve this effect with a bit of trickery.
One way is by using a transform, as you suggested:
<svg>
<text font-size="10px" font-family="Verdana" transform="translate(99,400) scale(3.5,13.7)">SQUASHED TEXT</text>
<rect x="100" y="300" width="300" height="100" fill="none" stroke="red" />
</svg>
A second way is by using an inner <svg> element and setting the viewBox to match the bounds of the text. You then set preserveAspectRatio="none".
<svg>
<svg x="100" y="100" width="300" height="100" viewBox="0.2 -7.3 86 7.3" preserveAspectRatio="none" overflow="visible">
<text font-size="10px" font-family="Verdana">SQUASHED TEXT</text>
</svg>
<rect x="100" y="100" width="300" height="100" fill="none" stroke="red" />
</svg>
This way is more verbose, but it has the advantage that once you have found the correct viewBox for a piece of text, you can make it fit any sized rectangle very easily. Just set the x,y,width and height of the inner <svg> to the size of the rectangle.
Demo here: http://jsfiddle.net/ZRgEF/3/

Resources