SVG graph with variable width - graph

I am trying to create an SVG graph with a fixed width to the left for the x axis labels and then a variable width to fill the remaining space for the actual graph results. The image below shows what I am trying to achieve. Thus far I have been unable to work out how to create the fixed width and variable width area.
Any help with this would be much appreciated!
Many thanks.

I would nest a couple of SVG elements inside your main like so:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="500px" height="500px" >
<svg width="100">
<rect x="0" y="0" width="100%" height="100%" fill="red" />
</svg>
<svg x="100" >
<rect x="0" y="0" width="100%" height="100%" fill="blue" opacity="0.5"/>
</svg>
</svg>
NOTE I made the blue SVG element translucent so you can see that none of the red SVG was behind it.
I would also recommend using viewBox to give you more control over your drawing...
EDIT:
OK then I need to ask you a question about aspect ratios. If you take a square (width = height) and chop off a fixed portion from ONE side you no longer have a square and you have to think about what that means to your graph.
I believe this SVG will demonstrate more or less what you want:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="600px" height="500px" viewBox="0 0 1200 1000">
<svg width="200">
<rect x="0" y="0" width="100%" height="100%" fill="red" opacity="0.5"/>
</svg>
<svg x="200" width="1000" height="1000" viewBox="0 0 100 100">
<rect x="0" y="0" width="100%" height="100%" fill="blue" opacity="0.5"/>
<rect x="80%" y="10%" width="10%" height="50%" fill="green"/>
<rect x="10" y="10" width="70" height="40" fill="gray"/>
</svg>
NOTE the aspect ratio (AR) of the outermost SVG's dimensions MUST match the outermost viewBox's AR but can have different values. Likewise for the second inner SVG, but now you are dealing with a slice of the total that is a true square and not a rectangle. You can vary the width and height of the outer most SVG and so long as you maintain the same AR all the code on the inside will not have to change - it will all scale automajically :)
Also note the different scales in use and the different value types I used for co-ordinates. Because my second inner SVG's viewBox set the user co-ordinates to 100 X 100, 10% and 10 amount to the same thing...
You could also set the preserveAspectRatio="none" or some other value to achieve different effects but for a graph I kinda think lining things up is important so I wouldn't.
One final note - you could (and in your case should) omit the viewBox on the inner SVG. That way the scale is consistent on all parts of your graph. I was just showing off the power of viewBox :)
It just occurred to me that you may prefer a rectangle to a square so here is a code sample for that:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="800px" height="400px" viewBox="0 0 1600 800" >
<svg width="200">
<rect x="0" y="0" width="100%" height="100%" fill="red" opacity="0.5"/>
</svg>
<svg x="200" width="1400" height="800" viewBox="0 0 175 100" >
<rect x="0" y="0" width="100%" height="100%" fill="blue" opacity="0.5"/>
<rect x="80%" y="10%" width="10%" height="50%" fill="green"/>
<rect x="10" y="10" width="70" height="40" fill="gray"/>
</svg>
NOTE the width of the inner SVG is set to 175 so that the aspect ratio of 1400/800 is maintained.

Related

Responsive SVG Mask

I have looked at several other answers on Stack:
Responsive SVG Clip Path or Mask Image
Responsive SVG image mask
But none seem to work for me. I have an SVG mask based on a path. I want it to expand to fill all available space (or contract) whilst keeping the aspect ratio of the path.
svg {
width: 100%;
height: 100%;
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%" viewBox="0 0 237.859 264.582" preserveAspectRatio="xMinYMin meet">
<defs>
<pattern id="triangle-image" patternUnits="userSpaceOnUse" width="3918" height="3918">
<image xlink:href="images/aspirational-photos/christian-buehner-JQFHdpOKz2k-unsplash.jpg" x="0" y="0" width="100%" height="100%" />
</pattern>
<pattern id="triangle-pattern" width="100%" height="100%" patternUnits="userSpaceOnUse">
<path id="triangle-path" d="M237.844,32.946c-0.001-9.981-4.42-19.279-12.121-25.512c-7.82-6.331-17.85-8.78-27.518-6.721
c-3.373,0.717-6.639,1.979-9.707,3.751l-129.11,74.56l-42.942,24.8C6.147,109.771-0.001,120.42,0,132.312
c0,11.892,6.149,22.541,16.449,28.487l84.623,48.833l87.457,50.486c7.179,4.143,15.193,5.435,23.174,3.736
c15.41-3.279,26.166-16.532,26.156-32.222L237.844,32.946z"
fill="#fff" fill-rule="evenodd" width="100%" height="100%" />
</pattern>
<mask id="triangle-mask" width="100%">
<rect x="0" y="0" width="100%" height="100%" fill="url(#triangle-pattern)" />
</mask>
</defs>
<rect x="0" y="0" width="100%" height="100%" fill="url(#triangle-image)" mask="url(#triangle-mask)"/>
</svg>
The SVG seems to obey the path values no matter what I do. Is there a way to make it expand or contract like an image would?
I've simplified your code in the sense that I'm applying the mask to the image instead of using patterns. Also the mask is the path. I'm not using width="100%" since the width af an svg element will take all the space available i.e 100%.
I would like to understand why you need it height="100%"
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 240 265">
<defs>
<mask id="triangle-mask">
<path d="M237.844,32.946c-0.001-9.981-4.42-19.279-12.121-25.512c-7.82-6.331-17.85-8.78-27.518-6.721
c-3.373,0.717-6.639,1.979-9.707,3.751l-129.11,74.56l-42.942,24.8C6.147,109.771-0.001,120.42,0,132.312
c0,11.892,6.149,22.541,16.449,28.487l84.623,48.833l87.457,50.486c7.179,4.143,15.193,5.435,23.174,3.736
c15.41-3.279,26.166-16.532,26.156-32.222L237.844,32.946z"
fill="#fff" />
</mask>
</defs>
<image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/222579/beagle400.jpg" mask="url(#triangle-mask)"/>
</svg>
Alternatively if you need all those patterns please remove width="100%"for the mask in your code.

svg - keep fill texture original size when resizing a path

I define a svg <pattern> like this:
<svg height="10" width="10" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<pattern id="circles-1_4" patternUnits="userSpaceOnUse" width="10" height="10">
<image xlink:href="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIxMHB4IiBoZWlnaHQ9IjEwcHgiIHZpZXdCb3g9IjAgMCAxMCAxMCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMTAgMTAiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxjaXJjbGUgY3g9IjEuMjUiIGN5PSIxLjI1IiByPSIxLjI1Ii8+PHJlY3QgZmlsbD0ibm9uZSIgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIi8+PC9zdmc+"
x="0" y="0" width="10" height="10">
</image>
</pattern>
</defs>
</svg>
visually, this is the equivalent of this:
So then I call my css for svg as a fill like this:
svg #VISUEL-3 * {fill: url(#circles-1);}
I get a pretty good result:
But when I display my graphic smaller (1/4 in this i.e), the fill adapt like this
It's difficult to see on the screenshots because the scale is broken due to the width 100% of stackoverflow but the vector-effect:non-scaling-stroke works perfectly so the strokes have the same size between the first and the second screenshot and the number "1,2,3,4,5,6" as well.
So as you can see the fill has adapted...
Is it possible to keep the same pattern size (same size of dots) like in css? That look messy visually when I have two graphics that are not of the same size and are next to each other.
Is my method right to obtain that? (I'm ready to change my method..)
The pattern elements are applied to the referencing element before transformations on the element or one of its parents are applied - so the pattern size gets transformed as well. The only way to counteract this is to write a seperate pattern element for each scale you us it at, including a patternTransform with the inverse scale. Fortunately, there is a mechanism for cloning patterns with a xlink:href attribute.
.simple {
fill: url(#dots);
}
.quarter {
fill: url(#quadrupleDots);
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="150">
<defs>
<pattern id="dots" patternUnits="userSpaceOnUse" width="10" height="10">
<circle r="1.25" cx="1.25" cy="1.25" />
</pattern>
<pattern id="quadrupleDots" xlink:href="#dots" patternTransform="scale(4)" />
</defs>
<rect id="shape" class="simple" x="20" y="20" width="100" height="100" />
<rect class="quarter" x="800" y="80" width="100" height="100" transform="scale(0.25)" />
</svg>

Scaling SVG image but not the mask

I am trying to render a image where the bottom part is cut of stylishly. The image should scale and fill the container so it is always 100% wide, but remains 500px high.
The current state almost works, however I do not want the mask to scale on the y-axis as it does now. The mask should stay a fixed height and only scale on the x-axis. The effect right now is that the mask cuts of a way to big piece of the image in large formats.
<svg viewBox="0 0 100 50" preserveAspectRatio="xMinYMax slice" width="100%" height="500px">
<defs>
<mask id="clip">
<path d="M0,45 100,40 100,0 0,0Z" fill="white"></path>
</mask>
</defs>
<image xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="https://placeimg.com/1500/500/animals" mask="url(#clip)" width="100%" height="100%" x="0" y="0" preserveAspectRatio="xMidYMid slice"></image></svg>
You are not very explicit about how you want the mask to behave when the image gets wider.
Here's one solution. Is this what you wanted? The mask angles from full height on the left, to 90% height on the right. No matter how wide the image is.
<svg width="100%" height="500px">
<defs>
<mask id="clip" maskContentUnits="objectBoundingBox">
<path d="M0,1 1,0.9 1,0 0,0Z" fill="white"></path>
</mask>
</defs>
<image xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="https://placeimg.com/1500/500/animals"
width="100%" height="100%" x="0" y="0" preserveAspectRatio="xMidYMid slice" mask="url(#clip)"></image>
</svg>

Center an element of a SVG mask

I'm a newbie in SVG so it's probably an easy question. I'm trying to make an SVG Mask with a simple triangle shape inside a rectangle. What I want to achieve is to get the rectangle responsive with his width but the triangle should
- get a fixed size
- be always at the center of the viewport
You'll understand better with my snippet:
.header-arrow {
height: 70px;
}
svg {
height: inherit;
}
#arrow-down-alpha {
transform: translateX(calc(50vw - 130px/2));
}
<div class="header-arrow">
<svg width="100%">
<defs>
<mask id="myMask" x="0" y="0" width="100%" height="100%">
<rect fill="white" x="0" y="0" width="100%" height="100%" />
<polygon id="arrow-down-alpha" fill="black" x="00" y="0" width="165px" height="100%" points="55.91 37.8 111.81 0 0 0 55.91 37.8" />
</mask>
</defs>
<rect id="base-mask" mask="url(#myMask)" x="0" y="0" width="100%" height="100%" />
</svg>
</div>
It's workning right now in chrome, but the translateX (or translate) is not working in firefox and edge. I've tried to use the transform SVG attribute but it seems that I can't use percentages values.
I'm not realy familiar with the viewbox but I'm not sure it will help in this case.
Thanks anyway for any kind of help !
Here's one way to achieve what you want without relying on new units or calc(). It should be cross-browser compatible also.
How it works:
We wrap the triangle in a nested SVG. We use an SVG because it has an x attribute which can take percentages.
We position this nested SVG at x="50%". It is now centred in the mask (roughly, see next step).
We move the triangle shape so it is centred at x=0. That's so that it is not offset from the centre of the mask.
We set overflow="visible" on the nested SVG so the part of the triangle that is now off the left of the SVG (ie. x < 0) are not clipped.
.header-arrow {
height: 70px;
}
svg {
height: inherit;
}
<div class="header-arrow">
<svg width="100%">
<defs>
<mask id="myMask" x="0" y="0" width="100%" height="100%">
<rect fill="white" x="0" y="0" width="100%" height="100%" />
<svg x="50%" overflow="visible">
<polygon fill="black" points="0 38 56 0 -56 0" />
</svg>
</mask>
</defs>
<rect id="base-mask" mask="url(#myMask)" x="0" y="0" width="100%" height="100%" />
</svg>
</div>

svg - flip upside down ex. graph

I'm trying to work out a simple svg example - creating bar graph.
However, I don't have clear grasp of how it works. I rotated an existing graph
upside down but seems like there is a small offset. Corresponding jsfiddle here - http://jsfiddle.net/rhvP8/2/
<div style="width:300px;height:300px;">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" style="width:100%;height:100%" viewBox="0 0 300 300">
<g>
<rect width="14.55" height="40%" x="0" y="0" fill="black"></rect>
<rect width="14.55" height="20%" x="50" y="0" fill="green"></rect>
<rect width="14.55" height="80%" x="100" y="0" fill="red"></rect>
<rect width="14.55" height="90%" x="150" y="0" fill="yellow"></rect>
<rect width="14.55" height="10%" x="200" y="0" fill="pink"></rect>
<rect width="14.55" height="60%" x="250" y="0" fill="orange"></rect>
</g>
</svg>
</div>
<div style="width:300px;height:300px;">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" style="width:100%;height:100%" viewBox="0 0 300 300">
<g transform="rotate(180)">
<rect width="14.55" height="40" x="-50" y="-300" fill="black"></rect>
<rect width="14.55" height="20" x="-100" y="-300" fill="green"></rect>
<rect width="14.55" height="35" x="-150" y="-300" fill="red"></rect>
<rect width="14.55" height="90" x="-200" y="-300" fill="yellow"></rect>
<rect width="14.55" height="10" x="-250" y="-300" fill="pink"></rect>
<rect width="14.55" height="60" x="-300" y="-300" fill="orange"></rect>
</g>
</svg>
</div>
The thing you need to remember is that the rotate() transform will rotate an object about the coordinates (0,0), which in this case is the top left corner of the graph. Since the graph is 300p wide and 300px tall, rotating through 180° causes the graph to spin off beyond the top left corner. A translate transform can be used to readjust the coordinates so that the drawing appears within the viewbox again. Hopefully this illustration will explain:
Here's an updated JSfiddle with a few other fixes: http://jsfiddle.net/rhvP8/4/
An alternative to squeamish's solution is just to use the version of rotate that takes the rotation origin as well: rotate(angle x y).
Since you know your graph is 300x300, using rotate(180 150 150) works fine.
Demo here
Easy way: the scaleY() CSS function, defines a transformation that resizes an element along the y-axis (vertically).
svg {
transform: scaleY(-1);
}
View browser compatibility here: https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/scaleY()#browser_compatibility

Resources