My question: Can svg <marker> elements inherit color from the <line> they are referenced on?
The background: I have a D3 graph that has different styled lines, and I want my lines to have arrows at the end.
So at the top of my <svg> I have const defs = svg.append('defs'); and then further along I generate my defs using a generator function:
function makeDefs(defs: Selection<SVGDefsElement, unknown, null, undefined>, color: string, status: string) {
const markerSize = 3
const start = defs.append
.append('marker')
.attr('id', `arrow-start-${color}-${status}`)
.attr('viewBox', '-5 -10 20 20')
.attr('markerWidth', markerSize)
.attr('markerHeight', markerSize)
.attr('orient', 'auto-start-reverse');
start
.append('path')
.attr(
'd',
status === 'PUBLISHED' ? customPaths.arrowLarge : customPaths.arrowSmall
)
.attr('stroke', color)
.attr('fill', color);
}
And use it like so:
makeDefs(defs, 'red', 'DRAFT');
And then I add the markers to my lines with:
// d3 code to draw the lines etc
line.attr(
'marker-start',
(d) =>
`url(
#arrow-start-${d.color}-${d.status}
)`
);
This all works great, my arrows have lines. But my current setup feels burdensome and clunky. I have about 20 colors and 3 statuses. With my current setup that would be 60 different:
makeDefs(defs, 'one-of-20-colors', 'one-of-3-statues');
My understanding of markers is that they can inherit color using the currentcolor attribute. Currently my <defs> sit up top underneath my main <svg> so any color inherited is inherited directly from that top level svg which is not what I want. The issue in my case is my <line> elements are the elements who's color I want to inherit, but according to the MDN docs <line>s cannot have <defs> as children, thus leaving me with the only option, of defining all my <defs> up front all at once.
Is there a trick or some attribute I'm missing here?
Any way to pass color to my marker when doing:
line.attr(
'marker-start',
(d) =>
`url(
#arrow-start-${d.color}-${d.status}
)`
);
?
For what is is worth, I'm currently wrapping all my <line>s in <g>. I suppose I could wrap them in <svg>s instead, and apply the fill and stroke there, and then define my <defs> per svg container? I tried this briefly and swapping the <g> for an <svg> broke a lot, but I'm not even sure if it would work, or be better for that matter.
I search for a easy and efficient way to center all childs in a container using basic package. (Because mixing basic and jewel layouts leads to some unwanted sides effects)
Must I use CSS, beads ? If both are usable what is difference and can I have code sample of each ?
For exemple could you tell me how to modify this code to center the text label:
<js:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:j="library://ns.apache.org/royale/jewel"
xmlns:js="library://ns.apache.org/royale/basic" xmlns:local="*">
<js:valuesImpl>
<js:SimpleCSSValuesImpl />
</js:valuesImpl>
<js:initialView>
<js:View>
<js:beads>
<js:VerticalLayout/>
</js:beads>
<js:Label text="How to center me on horizontal axis ?"/>
</js:View>
</js:initialView>
</js:Application>
Thanks to yishayw, as I'm not familiar with how to add css, I find how to do it and put the full working code here. I would expect that js|View would trigger the style but looking on css with browser I saw that style name for first "view" is "royale"
<js:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:j="library://ns.apache.org/royale/jewel"
xmlns:js="library://ns.apache.org/royale/basic" xmlns:local="*">
<fx:Style>
#namespace j "library://ns.apache.org/royale/jewel";
#namespace js "library://ns.apache.org/royale/basic";
.royale {
align-items :center;
}
js|Label {
color: #ff0000;
}
</fx:Style>
<js:valuesImpl>
<js:SimpleCSSValuesImpl />
</js:valuesImpl>
<js:initialView>
<js:View >
<js:beads>
<js:VerticalFlexLayout />
</js:beads>
<js:Label text="How to center me on horizontal axis ?"/>
</js:View>
</js:initialView>
</js:Application>
Regards
Try using VerticalFlexLayout and adding align-items: center to the style of the container.
I'm trying to create an SVG element with a width defined by a percentage of the parent and a fixed value, say 50% + 20px. For normal html elements, in the CSS you can use calc(50% + 20px). Is there an equivalent way to do this for embedded SVGs? Specifically, I'm using snap.svg, though I'm not sure if this capability exists with SVGs in general.
EDIT:
Tried setting <svg> width with percentages and px, which I couldn't get to work. I tried both:
<svg width='calc(50% + 20px)'>...</svg>
<svg width='50% + 20px'>...</svg>
I also tried setting it in CSS:
svg {
width: calc(50% + 20px);
}
It should be possible with the upcoming SVG2 as width etc. become geometry properties and then you can style them with calc
I recently came across Skrollr, an interesting library to achieve a parallax effect. I am using it to control some opacities and would be ideal to make a 'path' ( provided as an SVG image - including coloring and stroke width etc - from the designers ).
I am hoping to animate a SVG image (path) as the user scrolls down as though the line is drawn. This is a nice example from skrollr which uses a similar effect as expected. The used example has a 'path', but what I have is an svg image - which I notice to have many paths. Skrollr seems to be needing an inline SVG path as it seems.
How can I get a similar effect as in the example specified. To get it, I will have to convert the given svg file in to some thing similar to ( from example ):-
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="900px" height="1200px">
<path
style="fill:none;stroke:#333333;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6000;stroke-dashoffset:0"
data-0="stroke-dashoffset:6000;" data-end="stroke-dashoffset:0;"
d="m 199.16266,227.29566 c 0,0 -35.71429,-184.285714 37.14286,-210.000004 72.85714,-25.7142792 95.40137,3.16127 134.28572,42.85715 C 462.01038,153.47988 433.14019,231.62843 612.01981,188.72422 740.78435,157.8401 648.46996,-124.25341 423.44838,73.009946 397.57737,95.689556 342.01981,227.29566 322.01981,268.72423 c -20,41.42857 -15.71429,142.85714 64.28571,222.85714 80,80 143.73919,-10.78923 207.14286,17.14286 89.11717,39.26002 175.71428,70 214.28571,198.57143 38.57143,128.57142 -224.28571,45.71428 -311.42857,50 -87.14286,4.28571 -174.7636,-13.7114 -273.33504,69.14573 -98.57143,82.85724 -123.427376,147.71354 -133.893126,125.93684 -13.988987,-29.1077 -55.031934,-20.6196 -72.436974,2.587 -35.52138,47.36187 48.898892,49.25187 59.501803,81.06057 11.228801,33.6863 -55.491303,91.6685 -70.7122836,61.2265 -14.3563298,-28.7127 55.6559746,-11.2104 68.9875946,-11.2104 59.313946,0 106.207266,-47.3062 135.388156,-93.99577 9.07249,-14.516 16.34065,-34.5597 6.03641,-50.0161 -24.31744,-36.4761 -61.10674,32.1704 -64.67586,50.0161 -1.78804,8.94027 -18.73345,93.13327 -18.9716,93.13327 -15.39093,0 28.03002,-116.70147 77.61105,-83.6473 22.80402,15.2025 -31.82409,33.8676 -43.11725,31.0443 -2.54514,-0.6362 -14.1213,-7.4374 -16.38456,-5.174 -1.98676,1.9867 9.07416,13.8365 10.34816,16.3845 2.87494,5.7499 10.51739,15.8661 17.24688,18.1092 69.57702,23.1924 68.47583,-63.69675 106.93079,-50.8783 25.47788,8.4926 17.93869,61.2265 13.79751,61.2265 -4.54633,0 1.86112,-32.5889 2.58704,-36.2185 1.63481,-8.1741 -8.336,-25.008 0,-25.008 23.1702,0 56.38131,-4.3117 84.50982,-4.3117 6.92267,0 20.69627,8.6474 20.69627,1.7246 0,-22.13767 -106.83933,7.883 -73.29932,52.6031 15.39517,20.5269 45.97363,7.0247 56.91476,-11.2104 4.27342,-7.1224 5.92963,-41.2859 6.03643,-41.3927 3.30032,-3.3003 25.38957,-5.1906 31.04441,-7.7611 8.17799,-3.71727 116.56888,-61.61957 80.19809,-70.71237 -62.06519,-15.5162 -84.81857,132.80117 -47.42898,132.80117 39.83429,0 168.06934,-127.94377 127.62705,-141.42457 -48.77358,-16.2579 -78.2302,99.41297 -51.74069,125.90247 20.35735,20.3573 58.34681,-22.9907 73.29932,-37.9432 37.20757,-37.20767 16.38455,62.9731 16.38455,52.603 0,-50.488 -2.86125,-72.28997 41.39257,-61.2265 18.42188,4.6055 41.59056,-2.80515 61.22649,0 33.65343,4.8077 18.48038,96.0268 157.85652,147.0903"
/>
</svg>
Resources:
Image: https://gist.github.com/ziyan-junaideen/3f3ffc99d6812ff78717
Example: https://github.com/Prinzhorn/skrollr/blob/master/examples/path.html
I want to put a rectangle around a text in SVG.
The height of the text is known to me (the font-size attribute of the text element). But the width is dependent on the actual content. Using getBBox() or getComputedTextLength() should work. But this only works after rendering.
Is there a way to specify that in an other way? For example defining the x and width attributes relative to other values? I didn't find anything like that in the SVG Spec.
Figuring where text ends presumably requires roughly the same underlying code path as the rendering itself implements - going through the width of each character based on the font and style, etc... As I am not aware the SVG standards define a method for directly getting this information without doing the actual full rendering, till such methods emerge or are reported here by others, the approach should be to render invisibly before doing the actual rendering.
You can do that in a hidden layer (z-index, opacity and stuff) or outside the visible viewport, whichever works best in experimentation. You just need to get the browser do the rendering to find out, so you render invisibly for that sake, then use getComputedTextLength()
I know this is old, but a few ideas:
If you can choose a mono-spaced font, you know your width by a simple constant multiplication with glyph count
If you are bound to proportional fonts, you can find an average glyph size, do the math as with mono-space, and leave enough padding. Alternatively you can fill the padding with text element textLength attribute. If the constant is chosen carefully, the results are not very displeasing.
EDIT: As matanster found it to be hacky
Predetermine glyph widths with getComputedTextLength() and build a lookup table. Downside is that it does not account for kerning, but if your cache size is not a problem, you can append glyph-pair widths to this lookup.
Going beyond that is to find some way to do server side rendering: Is there a way to perform server side rendering of SVG graphics using React?
It is possible using canvas with measureText():
// Get text width before rendering
const getTextWidth = (text, font) => {
const element = document.createElement('canvas');
const context = element.getContext('2d');
context.font = font;
return context.measureText(text).width;
}
// Demo
const font = '16px serif';
const text = 'My svg text';
const textWidth = getTextWidth(text, font);
document.body.innerHTML = `
<svg>
<text x="0" y="20" font="${font}">${text}</text>
<rect x="0" y="30" width="${textWidth}" height="4" fill="red" />
</svg>
`;
Adapted from https://stackoverflow.com/a/31305410/1657101