Expand SVG path to fit container of all dimensions - svg

I'm trying to have a SVG path scale to fit the entire container element, without stretching or being trimmed. The SVG is the one below. As you can see, it's a simple border.
<svg preserveAspectRatio="none" viewBox="0 0 370 80" height="100%" width="100%">
<path vector-effect="non-scaling-stroke" d="M359,70C359,70,300,70,185,70C84,70,9,70,9,70C9,70,9,60,9,40C9,24,9,10,9,10C9,10,84,10,185,10C300,10,359,10,359,10C359,10,359,24,359,40C359,60,359,70,359,70C359,70,359,70,359,70"/>
</svg>
Then I have an element that could have different sizes, because it's responsive and because I use it in various cases where width or height can be different. I can't succeed in having the SVG that expands its path by always staying inside the viewport, but scaling without preserving aspect ratio. It doesn't seem a difficult logical thing to do, but I tried various options without success.
EDIT
I was able to scale this SVG, by setting whatever dimensions I wanted. Why does the first not work, but this works instead?
<svg preserveAspectRatio="none" viewBox="0 0 404 77" height="100%" width="100%">
<path vector-effect="non-scaling-stroke" d="m0,0l404,0l0,77l-404,0l0,-77z"/>
</svg>

The short answer is no. What you want to do (as I understand it) is not possible. In SVG you can scale to fit the container (using constant aspect ratio), or you can stretch (ignoring aspect ratio).
There is no way currently to keep some parts of the SVG static and stretch other parts. Unless, of course, you use Javascript to manipulate the contents of the SVG.
What you may want to do is consider using an SVG as the source image for a CSS border-image (see http://www.w3.org/TR/css3-background/#border-images). Perhaps that is the sort of thing you were after?

Related

Space between SVG stroke and fill

Filling and stroking a circle with the same color and a stroke-width exceeding a certain size, produces a strange transparent region “between” the two paint regions. What is going on?
This is happening in both Chrome and Firefox, so it may be to spec, but I couldn’t find any language in the spec about this behavior.
Fiddle
<svg viewBox="0 0 300 300">
<circle cx="100" cy="100" r="8"
stroke="#000" stroke-width="40"
fill="#000"/>
</svg>
Produces this rendering:
As Robert Longson noted, the issue shows up when a stroke overlaps itself in such a way that it creates a donut-hole when you convert the stroke outline to a separate path (depending on the winding-order / fill-rule calculations).
The gap between the fill and stroke in your particular example is caused by the "inside" edge of the stroke extending across the entire fill region and out the other side.
It gets really weird when you have dashed strokes, as shown in the examples from Tavmjong Bah's discussion article.
This is, unfortunately, neither according to the SVG spec nor against the spec. Instead, the spec at this point of time has left the matter undefined.
SVG working group discussion is here.
At this point, WebKit, Blink, and Firefox on Mac/Android draw strokes with cut-outs, using the Skia graphics library or Apple's CoreGraphics.
IE/Edge and Firefox on Windows/Linux just draw the total stroke, without cut-outs, as do Inkscape and Illustrator and most PDF-rendering software (the PDF spec itself is non-committal).
Everyone I've discussed this with agrees that the cut-outs are a sub-optimal result. But with so many browsers using rendering engines that do it this way, the SVG working group wasn't willing to make the more-intuitive stroke behavior a strict requirement. So instead, the SVG 2 spec has a warning-to-authors with a sample figure:
At this point, the best prospect for making a change would be to file issues (or contribute code) on the Skia library. If it was changed, that puts pressure on Apple to update to match, and the SVG spec would be able to make it official.
I found that adding stroke-linecap="round" style="stroke-dasharray: 1000" fixes the issue by introducing virtual dashes
<svg viewBox="0 0 300 300">
<circle cx="100" cy="100" r="8"
stroke="#000" stroke-width="40"
fill="#0F0" stroke-linecap="round" style="stroke-dasharray: 1000" />
</svg>
It's an artifact because the stroke width is so big it crosses the centre of the circle. It shouldn't happen but it's easily avoided by increasing r and decreasing the stroke-width.

How to adjust the resizing of a SVG?

I need some help to figure out how I can adjust the resizing of an SVG graphic when displayed on my web page.
Here are joined 2 screenshots of the graphics, one shows the graphics on a desktop wide screen, and the other on a smartphone
My problem is the graphics are too small on the smartphone. How can I make it bigger? I didn't anything about the responsiveness of this graphics, just the plain SVG in my web page. So I guess something can be made but I don't know what exactly.
Here is the beginning of the SVG graphics:
<svg viewbox="0 0 1920 632" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet">
<title>graphics</title>
<g style="isolation:isolate">
<g data-name="before animation starts" id="0482fa28-2c72-43bc-8627-b57f09f318d3">
<path id="main-oval" class="main-oval" d="M1354.63,260.62C1338.38,142.2,1144.68,71,922,101.54c-109.4,15-205.35,51.8-272.82,99.38-69.92,49.26-109.23,110-101,170.29,16.31,118.42,210,189.59,432.62,159.07,110.79-15.17,207.86-52.73,275.44-101.15C1324.59,380.18,1362.8,320.12,1354.63,260.62Z" data-name="oval"></path>...
Thanks
Attribute names in SVG are case-sensitive. So viewbox should be viewBox.
It is possible that there are other things wring, but I can't tell without seeing the complete SVG file.
Because your SVG has a viewBox attribute without a width and height attribute it is by default responsive and will scale to fit it's parent container.
In the case of your mobile view, it is the parent container that is restricting the size rather than anything in your SVG. Try inspecting the width, margin and padding of the parent container to ensure it is 100% wide and your SVG will scale to suit.
If the SVG is still not quite legible after that you will need to modify the graphic using CSS media queries to scale and transform specific elements. That is beyond the scope of the question so I won't go into it here.

SVG viewBox breaks 100% fill of viewPort while preserving aspect ratio

Q: How can I use the viewBox coordinate system whilst still filling the viewPort completely and preserving aspect ratio?
I'm new to svg programming, so hopefully I'm just mis-understanding a basic concept.
I want to create an interactive & responsive map with , based on a background image that the user uploads.
Here's the basic example I'm trying to get to work (JSFiddle):
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="200px"
height="400px"
preserveAspectRatio="xMidYMid meet"
style="border: 1px solid black;">
<image x="0" y="0" width="100%" height="100%"
preserveAspectRatio="xMidYMid meet"
xlink:href="http://www.bized.co.uk/sites/bized/modules/bized_cb_navigation/images/floorplan_info.gif">
</image>
</svg>
This works nicely. since however the viewPort changes, the image always fills it whilst maintaining its aspect ratio. (See wide-Screen example)
Next I add a coordinate system viewBox="0 0 100 100":
the wide-screen view still fills nicely
but the vertical-screen view now does not fill the viewPort anymore
If you take a different image that is wider than tall, then the wide-screen view breaks, and the vertical-screen view still works.
When I inspect the SVG in Chrome DOM Element inspector, for the first two examples without using viewBox="0 0 100 100" The svg element has the same size as the viewPort. Once the viewBox attribute is added, the element becomes a square with sides equal to the lesser of the viewPort's sides.
This behavior is explained in this Tutorial as:
"... the view box is scaled according to the smaller of the two aspect ratios..."
I need the viewBox attribute so that I can zoom and pan on the image within the viewPort.
This is because you effectively have two competing viewBox transformations.
Because of your square viewBox, you are fitting the image into a square, and then fitting the square into your SVG rectangle.
If you make your SVG viewBox the same dimensions as your image (or the same aspect ratio will do), then the problem will be resolved.
viewBox="0 0 155 210"
http://jsfiddle.net/2qexypLs/15/
http://jsfiddle.net/2qexypLs/16/
My current solution is to use JavaScript to dynamically set the viewBox width and height values to the same value as svg width and height values. That way the aspect ratio for x and y are the same and the fill returns to 100% of viewPort. (JsFiddle)
For the interactive elements layer of the map I have a separate coordinate system that is mapped to the background image scaling ratio when the svg viewPort is defined. That means all coordinates need to be recalculated on svg define/change width/height event.
After this first re-calculation, the map can be zoomed and panned by changing the viewBox parameters without any further calculations.

SVG mask doesn't work if a media query is present

TL;DR: I need to mask out a portion of one rectangle in SVG, based on the size and position of another existing rectangle, which will be changing dynamically. A Chrome bug is blocking the mask + use approach I tried. How can I do a mask or inverted clip path based on an existing shape?
Full Overview:
I'm using D3.js, and I am using the brush control to add a brush to a rectangle in an embedded SVG. By default, this adds some extra elements to the SVG, including a rect with class extent that shows the size of the brushed area.
Rather than have the brush extent be rendered as a semi-transparent overlay on top of the rectangle, as in most D3 examples, I am trying to "cut out" the extent from a semi-transparent overlay, so that the brush area shows the true color below. Per this question, I am trying to do this with a mask element, with a child use element referencing the extent. With some D3 magic, I now have a structure like this:
<svg width="100" height="100">
<g class="brush-layer inverted">
<defs>
<mask id="mask835">
<rect fill="#fff" width="100%" height="100%"></rect>
<use fill="#000" xlink:href="#extent848"></use>
</mask>
</defs>
<g class="brush" style="pointer-events: none;">
<rect class="overlay" mask="url(#mask835)" width="100%" height="17"></rect>
<rect class="extent" x="30" width="52" height="17" id="extent848"></rect>
</g>
</g>
</svg>
This works great... sort of. It turns out that there appears to be a tricky Chrome bug, which I've filed here, which prevents the mask from being applied if there's a #media query in the CSS. You can see the working version here and the failing version here (fails in Chrome, works in FF).
I need this to work in Chrome, and can't drop the #media query. I also need to make the use element work, because D3 will automatically resize the extent rectangle, and that's the shape I need to mask out.
So, how can I mask out a portion of one rect, based on another rect, without using the mask + use strategy above?
One possible workaround might be to use a custom clip-path, but it's probably not going to be as elegant. Some examples of how to do do this with clip-path can be found in this question.

Using a clipping path with a positioned object in Webkit

Consider this simple SVG file:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:x="http://www.w3.org/1999/xlink"
viewBox="0 0 353 150">
<defs>
<clipPath id="walk0"><rect width="44" height="70" /></clipPath>
<image id="img" x:href="http://phrogz.net/tmp/walking-girl2.png"
width="353" height="70" />
</defs>
<use x:href="#img" clip-path="url(#walk0)" />
<use x:href="#img" y="80" clip-path="url(#walk0)" />
</svg>
The intent is to have two copies of the spritesheet clipped to the same region, with the second copy 80 units lower down. This works as intended in Firefox (the clipping path is applied before the y offset). In Chrome and Safari, however, the second image is not shown. (The clipping path is applied using global SVG unit space, and hence shows an empty area of the image.)
1) Which one of these browsers is correct?, or
2) What is the simplest, standards-based way to achieve this goal?
I can work around the problem by using wrapping <g> elements with transforms; this works in both Firefox and Chrome. But I'm hoping that there's a simpler way to achieve the same results in a correct and cross-browser manner.
FWIW, I also tried setting clipPathUnits="objectBoundingBox" on the clipPath, but this always produced an unclipped image. This was true even when I wrapped the <image> in a <symbol> with an explicit viewBox, height and width and referenced that with the <use> instead of the <image>. Either I don't understand how objectBoundingBox is supposed to work, or support for it is currently broken. It is certainly possible that the answer is the former instead of the latter. ;)
The easiest, standards-compliant way to differentiate between these is to use the SVG test suite provided by W3.org. This suite provides tests for use structs that you can play with to determine compliance, among many others.
The problem is how your y value is being parsed, which is causing your figure to translate out of the second frame, but only in some browsers. This is the correct, cross-browser way to specify the desired translation:
<use x:href="#img" clip-path="url(#walk0)"transform="translate(0,80)"/>
I would assume the dubious parsing with respect to the current clipping pane is a regression.

Resources