SVG - Line at baseline - svg

I am drawing something here as you can see.
But there is a problem: the lines at the very top and very bottom of the image (the black "ruler") are only half the width because the "base" of the line is in the center.
For a text field, I was able to reposition the dominant-baseline and thus position my texts perfectly. However, a line (or path) doesn't seem to have a baseline.
How can I make sure my lines are at the very top and very bottom? Is the only way to do this to hard position them at the line width / 2? I tested this out, and it works perfectly, however, I'm not the biggest fan of hard coding these numbers. Is there a way not to hard code this?
Here's my code and my example image:
<svg width="200" height="200">
<defs>
<linearGradient id="gradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:rgb(0, 0, 255);"/>
<stop offset="50%" style="stop-color:rgb(255, 255, 255);"/>
<stop offset="100%" style="stop-color:rgb(255, 127, 0);"/>
</linearGradient>
</defs>
<rect width="20%" height="100%" fill="url(#gradient)" />
<line x1="22%" x2="22%" y1="0%" y2="100%" style="stroke: black;"/>
<line x1="22%" x2="24.5%" y1="0" y2="0" style="stroke: black;"/>
<line x1="22%" x2="24.5%" y1="50%" y2="50%" style="stroke: black;"/>
<line x1="22%" x2="24.5%" y1="100%" y2="100%" style="stroke: black;"/>
<text x="25%" y="0%" dominant-baseline="hanging">Top text</text>
<text x="25%" y="50%" dominant-baseline="middle">
<tspan x="25%" dy="-3%">middle text 1</tspan>
<tspan x="25%" dy="6%">middle text 2</tspan>
</text>
<text x="25%" y="100%">bottom text</text>
</svg>
Here's a Fiddle if you want to try it out. Just zoom in really far.
https://jsfiddle.net/jkom2x8f/1/

Your solution is the correct solution. It is the only real solution. There is nothing wrong with it.

It seems that after a lot of searching around and reading the comments, I concluded that hard coding the y positions to 0.5 and 199.5 is the only way to do it:
<svg width="200" height="200">
<defs>
<linearGradient id="gradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:rgb(0, 0, 255);"/>
<stop offset="50%" style="stop-color:rgb(255, 255, 255);"/>
<stop offset="100%" style="stop-color:rgb(255, 127, 0);"/>
</linearGradient>
</defs>
<rect width="20%" height="100%" fill="url(#gradient)" />
<line x1="22%" x2="22%" y1="0%" y2="100%" style="stroke: black;"/>
<line x1="22%" x2="24.5%" y1="0.5" y2="0.5" style="stroke: black;"/>
<line x1="22%" x2="24.5%" y1="50%" y2="50%" style="stroke: black;"/>
<line x1="22%" x2="24.5%" y1="199.5" y2="199.5" style="stroke: black;"/>
<text x="25%" y="0%" dominant-baseline="hanging">Top text</text>
<text x="25%" y="50%" dominant-baseline="middle">
<tspan x="25%" dy="-3%">middle text 1</tspan>
<tspan x="25%" dy="6%">middle text 2</tspan>
</text>
<text x="25%" y="100%">bottom text</text>
</svg>
Here's a fiddle:
https://jsfiddle.net/jkom2x8f/5/

Related

SVG pattern size relative to container shape

I am trying to create a svg pattern where size of it is relative to shape to which is applied. Pattern should consist of rect with linearGradient and text elements which repeats in x-axis n-times or every n pixels and is vertically aligned in middle of rect.
The results should look like this.
I have tried following approaches.
<svg width="100%" height="100">
<defs>
<linearGradient id="gradient" x1="0%" x2="0%" y1="0%" y2="100%">
<stop offset="0%" stop-color="#fff" />
<stop offset="100%" stop-color="#c6c6c6" />
</linearGradient>
<pattern id="pattern" width="25%" height="100%">
<rect width="100%" height="100%" fill="url(#gradient)"></rect>
<text x="10" y="50%" dx=50 fill="red">test</text>
</pattern>
</defs>
<rect width="1000" height="100" fill="url(#pattern)"></rect>
</svg>
This approach gives the result I want but the size of pattern is not relatve to shape to which is applied and fails when shape is resized, to solve this issue I have tried to create a pattern with patternContentUnits="objectBoundingBox" but then I am having problems positioning the text.
<svg width="100%" height="100%">
<defs>
<linearGradient id="gradient" x1="0%" x2="0%" y1="0%" y2="100%">
<stop offset="0%" stop-color="#fff" />
<stop offset="100%" stop-color="#c6c6c6" />
</linearGradient>
<pattern id="pattern" patternContentUnits="objectBoundingBox" width="25%" height="100%">
<rect width="1" height="1" fill="url(#gradient)"></rect>
<text x="0.1" y="0.5">test</text>
</pattern>
</defs>
<rect width="1000" height="100" fill="url(#pattern)"></rect>
</svg>
objectBoundingBox units are in the range [0, 1] so 10 is too big.
You'll need to use a suitable font-size too.
<svg width="100%" height="100%">
<defs>
<linearGradient id="gradient" x1="0%" x2="0%" y1="0%" y2="100%">
<stop offset="0%" stop-color="#fff" />
<stop offset="100%" stop-color="#c6c6c6" />
</linearGradient>
<pattern id="pattern" patternContentUnits="objectBoundingBox" width="25%" height="100%">
<rect width="1" height="1" fill="url(#gradient)"></rect>
<text transform="scale(0.2, 1)" x="0.1" y="0.5" font-size="0.1">test</text>
</pattern>
</defs>
<rect width="1000" height="100" fill="url(#pattern)"></rect>
</svg>

Why is my rotated SVG shifted?

I try to build a reflection of my text with gradient. But the reflection (the second text) has an off set and by applying the rotation the letters are shifting.
body {
background-color: black;
}
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="1000" height="140" viewBox="0 0 1000 140">
<text x="260" y="70" font-size="60" fill=" #888888" style="text-anchor: right">Multimediale Kunst</text>
<defs>
<linearGradient id="linearGradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#000000" />
<stop offset="10%" stop-color="#666666" />
</linearGradient>
</defs>
<text rotate="180" x="310" y="80" font-size="60" fill="url(#linearGradient)" style="text-anchor: right">Multimediale Kunst</text>
</svg>
This is happening because the rotate attribute rotates individual start points. So wider characters end up further away from narrow characters and vice-versa.
The rotate attribute is the wrong approach anyway. If you want to vertically flip the text, then you should use a transform that scales the object on the y axis by -1.
body {
background-color: black;
}
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="1000" height="140" viewBox="0 0 1000 140">
<text x="260" y="70" font-size="60" fill=" #888888" style="text-anchor: right">Multimediale Kunst</text>
<defs>
<linearGradient id="linearGradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="50%" stop-color="#000000" />
<stop offset="100%" stop-color="#666666" />
</linearGradient>
</defs>
<text x="260" y="-80" font-size="60" fill="url(#linearGradient)" style="text-anchor: right" transform="scale(1,-1)">Multimediale Kunst</text>
</svg>

SVG linear gradients objectBoundingBox vs userSpaceOnUse

I am making two gradients: one in objectBoundingBox units and another in userSpaceOnUse. The idea is to make them look the same. But somehow they are different. Here is the svg file.
<svg width="500" height="500" viewBox="0 0 500 500" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="user-grad" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="200" y2="100">
<stop stop-color="orange" offset="0"/>
<stop stop-color="blue" offset="1"/>
</linearGradient>
<linearGradient id="box-grad" x1="0" y1="0" x2="1" y2="1">
<stop stop-color="orange" offset="0"/>
<stop stop-color="blue" offset="1"/>
</linearGradient>
</defs>
<rect x="0" y="0" width="200" height="100" fill="url(#user-grad)"/>
<rect x="250" y="0" width="200" height="100" fill="url(#box-grad)"/>
</svg>
Here is what it looks like
Shouldn't they look the same?
Shouldn't they look the same?
No. When using object bounding box coordinates, you are basically transforming a 1x1 square onto your rectangle. So the 0 to 1 coordinates are stretched to fit the rectangle. Thus causing the gradient to stretch also.
If you want them to look the same, you would need to apply a gradientTransform, to your userSpaceOnUse one, that applies the equivalent stretch.
<svg width="500" height="500" viewBox="0 0 500 500" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="user-grad" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="100" y2="100" gradientTransform="scale(2, 1)">
<stop stop-color="orange" offset="0"/>
<stop stop-color="blue" offset="1"/>
</linearGradient>
<linearGradient id="box-grad" x1="0" y1="0" x2="1" y2="1">
<stop stop-color="orange" offset="0"/>
<stop stop-color="blue" offset="1"/>
</linearGradient>
</defs>
<rect x="0" y="0" width="200" height="100" fill="url(#user-grad)"/>
<rect x="250" y="0" width="200" height="100" fill="url(#box-grad)"/>
</svg>

How would I rotate an SVG?

The 1st image is my code now. My question is, if I wanted to rotate the the whole thing so it looks like the below image, what code would I add to it?
https://jsfiddle.net/jw1euh11/
What it looks like now.
I want to Rotate it to This
<svg width="266" height="266" viewBox="0 0 266 266">
<text x="100" y="15" fill="red" transform="rotate(44, 10,40)">Play </text>
<text x="197" y="15" fill="red" transform="rotate(44, 10,40)">Play </text>
<line x1="264" y1="1" x2="0" y2="1"
style="stroke: #0059dd; stroke-width: 3;"/>
<line x1="265" y1="265" x2="265" y2="0"
style="stroke: #0059dd;stroke-width: 3; "/>
<line x1="100%" y1="265" x2="0" y2="265"
style="stroke: #0059dd ; stroke-width: 3;"/>
<line x1="1" y1="100%" x2="1" y2="0"
style="stroke:#0059dd;stroke-width: 3; "/>
<line x1="0" y1="0" x2="100%" y2="100%"
style="stroke:#0059dd; stroke-width:3" />
<line x1="0" y1="100%" x2="100%" y2="0"
style="stroke:#0059dd; stroke-width:3"/>
</svg>
You can use group <g>...</g> to rotate all together.
<svg width="266" height="266" viewBox="0 0 266 266">
<g transform="rotate(-44, 80, 120)">
<text x="100" y="15" fill="red" transform="rotate(44, 10,40)">Play </text>
<text x="197" y="15" fill="red" transform="rotate(44, 10,40)">Play </text>
<line x1="264" y1="1" x2="0" y2="1"
style="stroke: #0059dd; stroke-width: 3;"/>
<line x1="265" y1="265" x2="265" y2="0"
style="stroke: #0059dd;stroke-width: 3; "/>
<line x1="100%" y1="265" x2="0" y2="265"
style="stroke: #0059dd ; stroke-width: 3;"/>
<line x1="1" y1="100%" x2="1" y2="0"
style="stroke:#0059dd;stroke-width: 3; "/>
<line x1="0" y1="0" x2="100%" y2="100%"
style="stroke:#0059dd; stroke-width:3" />
<line x1="0" y1="100%" x2="100%" y2="0"
style="stroke:#0059dd; stroke-width:3"/>
</g>
</svg>

How to graph a political coordinates graph

I'm trying to program a graph like this one:
I tried with SVG, but it's not very good since I had to use 2 different rectangles and didn't manage to get only the 4 edges to be rounded.
Here's my code:
<svg width="400" height="250">
<defs>
<linearGradient id="solids" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
<stop offset="50%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
<stop offset="50%" style="stop-color:rgb(0,255,0);stop-opacity:1" />
<stop offset="100%" style="stop-color:rgb(0,255,0);stop-opacity:1" />
</linearGradient>
<linearGradient id="solids2" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:rgb(255,255,0);stop-opacity:1" />
<stop offset="50%" style="stop-color:rgb(255,255,0);stop-opacity:1" />
<stop offset="50%" style="stop-color:rgb(0,0,255);stop-opacity:1" />
<stop offset="100%" style="stop-color:rgb(0,0,255);stop-opacity:1" />
</linearGradient>
</defs>
<text x="135" y="12" style="fill:black;">Conservadorismo
<tspan x="150" y="240">Liberalismo</tspan>
<tspan x="20" y="125">Esquerda</tspan>
<tspan x="305" y="125">Direita</tspan>
</text>
<rect x="100" y="20" rx="20" ry="20" width="200" height="100" style="fill:url(#solids); opacity:0.76" />
<rect x="100" y="120" rx="20" ry="20" width="200" height="100" style="fill:url(#solids2); opacity:0.76" />
<line x1="100" y1="120" x2="300" y2="120" style="stroke:black;stroke-width:2" />
<line x1="200" y1="20" x2="200" y2="220" style="stroke:black;stroke-width:2" />
</svg>
What should I do to fix it or do it better?
I would use ordinary <rect> objects without rounded corners, and apply a clipPath to the whole drawing to round off the corners.
Here's a simple example:
<svg width="400" height="400" viewBox="0 0 400 400">
<defs>
<clipPath id="roundRect">
<rect x="10" y="10" rx="20" ry="20" width="380" height="380"/>
</clipPath>
</defs>
<g clip-path="url(#roundRect)">
<rect fill="#0a0" stroke="none" x="10" y="10" width="190" height="190"/>
<rect fill="#f00" stroke="none" x="200" y="10" width="190" height="190"/>
<rect fill="#0bf" stroke="none" x="10" y="200" width="190" height="190"/>
<rect fill="#fd0" stroke="none" x="200" y="200" width="190" height="190"/>
</g>
</svg>

Resources