SVG - rotate <text> in relation to <rect> - css

I'm writing a legend for d3. Ten rectangles plotted from left to right. The outcome I would like is to position the text above its relevant cell, positioned vertically, but at a slight angle to the right. I have applied the rotation, but there's something about this that I'm missing, as it treats them as a group and rotates them all in a line, instead of rotating them relative to their sibling cell.
Can someone recommend some attributes or style tips or perhaps a different manner of grouping the elements so that the text elements rotate individually around their own centers, and not as a single line?
Here's the current state of my code:
<svg preserveAspectRatio="xMinYMin meet" viewBox={`0 0 800 70`}>
<g transform={`translate(${[dms.marginTop, dms.marginLeft].join(',')})`}>
<g>
{range(10).map((d) => (
<>
<rect
key={`${d}_legendCell`}
width={cellSize - 1.5}
height={cellSize - 1.5}
fill={colorScale(Number(legendBands(String(d))) + interval)}
x={d * cellSize}
></rect>
<text
key={`${d}_legendLabel`}
fontWeight="300"
fontSize="12"
width="100"
y={cellSize * d}
transform="rotate(290)"
dy=".85rem"
>
{Number(legendBands(String(d))).toFixed(1)}
</text>
</>
))}
</g>
</g>
</svg>
TIA!

If I read your code correctly you're currently aligning your <text> elements vertically and rotate them - better use horizontal offsets
You could instead place your label elements with specific x coordinates relative to your current rect's center.
Example 2nd cell
<rect x="10" y="0" width="10" height="10" fill="magenta"/>
<text x="15" y="5" width="10" dominant-baseline="central" text-anchor="middle" transform="rotate(-45, 15, 5)">02</text>
The text element's x-value (15) is the center point of the current cell/rect.
dominant-baseline="central" and text-anchor="middle" just simplify the horizontal and vertical alignment relative to the preceding <rect>.
We copy this x-value to the transformation:
transform="rotate(-45, 15, 5)"
This way we ensure the label is rotated around the rect's center.
Also, this method is quite robust, considering that some browsers still have problems with transform-origin and transform-box (especially some versions of Safari)
Static svg example
svg {
display: block;
border: 1px solid #ccc;
}
text {
font-size: 5px
}
<svg width="50%" viewBox="0 0 30 10">
<rect x="0" y="0" width="10" height="10" fill="green"/>
<text x="5" y="5" width="10" dominant-baseline="central" text-anchor="middle" transform="rotate(-45, 5, 5)">01</text>
<rect x="10" y="0" width="10" height="10" fill="magenta"/>
<text x="15" y="5" width="10" dominant-baseline="central" text-anchor="middle" transform="rotate(-45, 15, 5)">02</text>
<rect x="20" y="0" width="10" height="10" fill="cyan"/>
<text x="25" y="5" width="10" dominant-baseline="central" text-anchor="middle" transform="rotate(-45, 25, 5)">03</text>
</svg>
<p>Add gaps</p>
<svg width="50%" viewBox="0 0 32 10">
<rect x="0" y="0" width="10" height="10" fill="green"/>
<text x="5" y="5" width="10" dominant-baseline="central" text-anchor="middle" transform="rotate(-45, 5, 5)">01</text>
<rect x="11" y="0" width="10" height="10" fill="magenta"/>
<text x="16" y="5" width="10" dominant-baseline="central" text-anchor="middle" transform="rotate(-45, 16, 5)">02</text>
<rect x="22" y="0" width="10" height="10" fill="cyan"/>
<text x="27" y="5" width="10" dominant-baseline="central" text-anchor="middle" transform="rotate(-45, 27, 5)">03</text>
</svg>

Related

Why isn't my SVG text centered vertically in Microsoft Edge?

I have a SVG that is well presented in Firefox and Chrome, but in Edge the text doesn't center vertically.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<rect fill="#ff0000" x="0" y="0" width="64" height="64" rx="8" ry="8" />
<text font-size="32px" alignment-baseline="middle" dominant-baseline="center" fill="#fff" text-anchor="middle" lengthAdjust="spacingAndGlyphs" textLength="85%" x="32" y="32">
dev
</text>
</svg>
This is what it looks like when its well presented:
And this is how Edge is presenting it:
This is what you can do: You keep alignment-baseline="baseline". This works correctly on Edge. Then you offset the text on y with dy = font-size/4
Also I've removed lengthAdjust="spacingAndGlyphs" textLength="85%"from your code since this was offseting the text to the right in Edge.
svg{width:90vh;}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<rect fill="#ff0000" x="0" y="0" width="64" height="64" rx="8" ry="8" />
<text font-size="32px" dy="8" dominant-baseline="baseline" text-anchor="middle" fill="#fff" x="32" y="32">dev</text>
</svg>
For the most reliable cross-browser behaviour, you should consider not using the x-baseline attributes at all. Instead, position the text baseline exactly where you want it.
For example:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<rect fill="#ff0000" x="0" y="0" width="64" height="64" rx="8" ry="8" />
<text font-size="32px" fill="#fff" text-anchor="middle" lengthAdjust="spacingAndGlyphs" textLength="85%" x="32" y="39">
dev
</text>
</svg>

SVG change cursor from `cursor:wait` into e.g. `cursor:help` after hovering n seconds (loading time for tooltip) without scripting?

There has been a question about changing the cursor using before on this site, but it asked for a solution using Javascript, and it wasn't specific about when to trigger the action to change the cursor.
My question is about SVG and CSS/SMIL without the use of other scripting languages such as Javascript.
When hovering over a certain object, how to change the cursor from cursor:wait into e.g. cursor:help? The change should be triggered after an exact amount of seconds of being hovered over the object.
The use-case is clearly presented in a minimal snippet below.
MWE SNIPPET
#MOUSE_OVER_THESE{
cursor:wait;
}
<svg id="SVG"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="250"
height="175"
viewBox="0 0 250 175">
<text font-size="10" x="10" y="20">
<tspan
x="10" dy="0">Hover over the objects below. Can the cursor</tspan><tspan
x="10" dy="12">change from "cursor:wait" into e.g. "cursor:help"</tspan><tspan
x="10" dy="12.5">after about 1 second, (which will be right</tspan><tspan
x="10" dy="12.5">about when the tooltip appears on certain</tspan><tspan
x="10" dy="12.5">browsers) without using any scripting language?</tspan></text>
<g id="MOUSE_OVER_THESE">
<rect x="50" y="100" width="60" height="50" fill="red">
<title>This is a tooltip.</title>
</rect>
<rect x="150" y="100" width="60" height="50" fill="blue">
<title>This is another tooltip.</title>
</rect>
</g>
</svg>
I can not afford to use scripting languages such as Javascript, so I am wondering if there is a more native SVG with CSS/SMIL approach.
Here is a trick using transition and a hidden element.
UPDATE
The mouse need to be moved slightly in order to see the cursor change
.hide {
transition:1s visibility 1s;
cursor:wait;
}
.hide:hover {
visibility:hidden;
}
#MOUSE_OVER_THESE {
cursor:help;
}
#MOUSE_OVER_THESE:hover + .hide {
display:none;
}
<svg id="SVG"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="250"
height="175"
viewBox="0 0 250 175">
<text font-size="10" x="10" y="20">
<tspan
x="10" dy="0">Hover over the objects below. Can the cursor</tspan><tspan
x="10" dy="12">change from "cursor:wait" into e.g. "cursor:help"</tspan><tspan
x="10" dy="12.5">after about 1 second, (which will be right</tspan><tspan
x="10" dy="12.5">about when the tooltip appears on certain</tspan><tspan
x="10" dy="12.5">browsers) without using any scripting language?</tspan></text>
<g id="MOUSE_OVER_THESE">
<rect x="50" y="100" width="60" height="50" fill="red">
<title>This is a tooltip.</title>
</rect>
<rect x="150" y="100" width="60" height="50" fill="blue">
<title>This is another tooltip.</title>
</rect>
</g>
<rect class="hide" x="50" y="100" width="160" height="50" fill="transparent"></rect>
</svg>

Applying coloured opacity to SVG image

I have an SVG that contains a <g> element, within this is a <path>, within here I specify a fill of #image0
Later in the same SVG I have my <def>, within here is my <pattern> and then the <image> tag that corresponds to the fill of #image0
The value of the href of the <image> is a base64 encoded image.
I would like to apply a coloured, semi transparent overlay on top of the image. e.g. a #F5A9A9 50% opacity overlay.
I have tried adding style="opacity: 0.5" to the <image> tag -- this applies the opacity but obviously no colour. I suspect the answer is along the lines of background-color: rgba(245,169,169, 0.5) but I am unsure where to position this,
<g class="g-item">
<path class="st0" d="M1839.1,1394.2c0,0,22.7,6.7,30,18c0,0,111.3,7.1,122.6-130.5V801.1l-827.8,260l0,226 c4.9,107,118.7,125,118.7,125c3.6-17.3,27-19.5,27-19.5L1839.1,1394.2z" data-id="0" style="fill: url("#image0");"></path>
</g>
<defs class="g-def">
<pattern id="image0" width="1" height="1" y="0" x="0" patternContentUnits="objectBoundingBox" viewBox="0 0 1 1" preserveAspectRatio="xMidYMid slice">
<image preserveAspectRatio="xMidYMid meet" width="1" height="1" x="0" y="0" xlink:href="data:image/png;base64,iVBORw0KGgoAA.....AElFTkSuQmCC" data-naturalWidth="960" data-naturalHeight="960"></image>
</pattern>
</defs>
I would like to apply a coloured, semi transparent overlay on top of the image. e.g. a #F5A9A9 50% opacity overlay.
Well, you can do just that: paint a semi-transparent rectangle on top of the image:
<defs class="g-def">
<pattern id="image0" width="1" height="1" y="0" x="0"
patternContentUnits="objectBoundingBox"
viewBox="0 0 1 1" preserveAspectRatio="xMidYMid slice">
<image preserveAspectRatio="xMidYMid meet"
width="1" height="1" x="0" y="0"
xlink:href="data:image/png;base64,iVBORw0KGgoAA.....AElFTkSuQmCC"
data-naturalWidth="960" data-naturalHeight="960"></image>
<rect width="1" height="1" fill="#F5A9A9" opacity="0.5"></rect>
</pattern>
</defs>

Confusion about the order of CSS transform

All:
When I try CSS transform, something about the order of scale and translate confused me:
<svg>
<rect x="10" y="10" width="20" height="30" style="stroke: #3333cc; fill:none;"/>
<rect x="0" y="0" width="20" height="30" style="stroke: #000000; fill:none;" transform="scale(2) translate(10, 10)"/>
</svg>
<svg>
<rect x="10" y="10" width="20" height="30" style="stroke: #3333cc; fill:none;"/>
<rect x="0" y="0" width="20" height="30" style="stroke: #000000; fill:none;" transform="translate(10, 10) scale(2)"/>
</svg>
These two give different effects. Could anyone give me some explanation about how the CSS transform be processed and rendered?
Thanks
From W3C
x = ""
The x-axis coordinate of the side of the rectangle which has the smaller x-axis coordinate value in the current user coordinate system.
and Mozilla Developer Network:
This attribute indicates an x-axis coordinate in the user coordinate
system. The exact effect of this coordinate depend on each element.
The reason they don't look the same is because scaling the element also scaled the user coordinate system.
I've added two more SVG elements so we can see what it looks like with just the first transform applied to it.
<svg>
<rect x="10" y="10" width="20" height="30" style="stroke: #3333cc; fill:none;"/>
<rect x="0" y="0" width="20" height="30" style="stroke: #000000; fill:none;" transform="scale(2)"/>
</svg>
<svg>
<rect x="10" y="10" width="20" height="30" style="stroke: #3333cc; fill:none;"/>
<rect x="0" y="0" width="20" height="30" style="stroke: #000000; fill:none;" transform="scale(2) translate(10, 10)"/>
</svg>
<svg>
<rect x="10" y="10" width="20" height="30" style="stroke: #3333cc; fill:none;"/>
<rect x="0" y="0" width="20" height="30" style="stroke: #000000; fill:none;" transform="translate(10, 10)"/>
</svg>
<svg>
<rect x="10" y="10" width="20" height="30" style="stroke: #3333cc; fill:none;"/>
<rect x="0" y="0" width="20" height="30" style="stroke: #000000; fill:none;" transform="translate(10, 10) scale(2)"/>
</svg>
In the first one the scale is applied first. Now both the rect and the user coordinate system is 2x as big as the purple rect. So when it is moved to 10,10 that is not the same location as it is for the purple rect.
In the second, the rect is moved first. It has the same coordinate system as the purple rect so it ends up in the exact same place. It is then scaled so it's upper left hand corner stays in the same place.
Every transform function like scale, translate represents a matrix. And matrix concatenation/multiplication is not commutative. So M1 x M2 must not be equal to M2 x M1.
E.g. a scaling matrix (column major order), scale(2, 2):
m11=2.000 m21=0.000 m31=0.000
m12=0.000 m22=2.000 m32=0.000
m13=0.000 m23=0.000 m33=1.000
E.g. a translation matrix, translate(10, 10):
m11=1.000 m21=0.000 m31=10.000
m12=0.000 m22=1.000 m32=10.000
m13=0.000 m23=0.000 m33=1.000
Scale * Translate:
m11=2.000 m21=0.000 m31=20.000
m12=0.000 m22=2.000 m32=20.000
m13=0.000 m23=0.000 m33=1.000
Translate * Scale:
m11=2.000 m21=0.000 m31=10.000
m12=0.000 m22=2.000 m32=10.000
m13=0.000 m23=0.000 m33=1.000

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