Skip to content Skip to sidebar Skip to footer

Svg Text Color With Correspond To Background

I am using D3.js to make a graph like on this image: Generally, all works fine, but I don't know how to make the labels visible when the bar doesn't cover all of it. My first idea

Solution 1:

The approach to use clip paths has already been described by squeamish ossifrage's answer. I have put together a working snippet doing it the d3 way:

var svg = d3.select("body")
    .append("svg")
    .attr({
        width: 400,
        height: 400
    });

var textOut = svg.append("text")
    .attr({
        x: 120,
        y: 66
    })
    .style({
        fill: "black",
        stroke: "none"
    })
    .text("Description");

var rect = svg.append("rect")
                    .attr({
                        id: "rect",
                        x: 50,
                        y: 50,
                        width: 100,
                        height: 20
                    })
                    .style({
                        fill: "limegreen",
                        stroke: "darkgreen"
                    });

svg.append("clipPath")
    .attr("id", "clip")
    .append("use")
    .attr("xlink:href", "#rect");

var textIn = svg.append("text")
    .attr({
        x: 120,
        y: 66
    })
    .style({
        fill: "white",
        stroke: "none",
        "clip-path": "url(#clip)"
    })
    .text("Description");
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

I've further shortened things by not setting up clipPaths in the defs section but instead linking to the rect which has already been drawn via xlink:href:

svg.append("clipPath")
    .attr("id", "clip")
    .append("use")
    .attr("xlink:href", "#rect");

This will result in an svg structure like the following:

<textx="120"y="66"style="fill: rgb(0, 0, 0); stroke: none;">Description</text><rectid="rect"x="50"y="50"width="100"height="20"style="fill: rgb(50, 205, 50); stroke: rgb(0, 100, 0);"/><clipPathid="clip"><usexmlns:xlink="http://www.w3.org/1999/xlink"xlink:href="#rect"/></clipPath><textx="120"y="66"style="fill: rgb(255, 255, 255); stroke: none; clip-path: url(#clip);">Description</text>

Solution 2:

This can be done quite easily with clipping masks. Here's a static SVG to illustrate the point:

<svgwidth="400"height="200"viewBox="0 0 400 200"><defs><clipPathid="clip_1"><rectwidth="50"height="38" /></clipPath><clipPathid="clip_2"><rectwidth="100"height="38" /></clipPath><clipPathid="clip_3"><rectwidth="150"height="38" /></clipPath><clipPathid="clip_4"><rectwidth="250"height="38" /></clipPath><clipPathid="clip_5"><rectwidth="300"height="38" /></clipPath></defs><rectwidth="400"height="200"fill="white"path="none" /><gtransform="translate(0,1)"><textx="10"y="28"font-family="Verdana"font-size="20"fill="red">Lorem ipsum dolor sit amet</text><gclip-path="url(#clip_1)"><rectwidth="50"height="38"fill="red" /><textx="10"y="28"font-family="Verdana"font-size="20"fill="white">Lorem ipsum dolor sit amet</text></g></g><gtransform="translate(0,41)"><textx="10"y="28"font-family="Verdana"font-size="20"fill="orange">Consectetur adipiscing elit</text><gclip-path="url(#clip_2)"><rectwidth="100"height="38"fill="orange" /><textx="10"y="28"font-family="Verdana"font-size="20"fill="white">Consectetur adipiscing elit</text></g></g><gtransform="translate(0,81)"><textx="10"y="28"font-family="Verdana"font-size="20"fill="green">Proin egestas suscipit justo</text><gclip-path="url(#clip_3)"><rectwidth="150"height="38"fill="green" /><textx="10"y="28"font-family="Verdana"font-size="20"fill="white">Proin egestas suscipit justo</text></g></g><gtransform="translate(0,121)"><textx="10"y="28"font-family="Verdana"font-size="20"fill="blue">Nam eget magna gravida eros</text><gclip-path="url(#clip_4)"><rectwidth="250"height="38"fill="blue" /><textx="10"y="28"font-family="Verdana"font-size="20"fill="white">Nam eget magna gravida eros</text></g></g><gtransform="translate(0,161)"><textx="10"y="28"font-family="Verdana"font-size="20"fill="purple">Accumsan tempor eget sed augue</text><gclip-path="url(#clip_5)"><rectwidth="300"height="38"fill="purple" /><textx="10"y="28"font-family="Verdana"font-size="20"fill="white">Accumsan tempor eget sed augue</text></g></g></svg>

Basically what you need to do is draw every text segment in two different colours, and use clipping masks to reveal the text that sits on the background. So for example, the first bar in this example is created as follows:

1: Define a clip path that exactly matches the shape of the foreground object:

<defs><clipPathid="clip_1"><rectwidth="50"height="38" /></clipPath><!-- more paths here --></defs>

2: Draw the text to be viewed against the background colour:

<textx="10"y="28"font-family="Verdana"font-size="20"fill="red">Lorem ipsum dolor sit amet</text>

3: Create a group containing the foreground object and text using the same coordinates as the clip path and background text, and include a clip-path parameter to crop the text where it extends beyond the foreground object:

<gclip-path="url(#clip_1)"><rectwidth="50"height="38"fill="red" /><textx="10"y="28"font-family="Verdana"font-size="20"fill="white">Lorem ipsum dolor sit amet</text></g>

It shouldn't be too hard to integrate this into your D3 code.

Solution 3:

Actually, this can be done using CSS only. There is a CSS property named mix-blend-mode, which

...describes how an element's content should blend with the content of the element's direct parent and the element's background.

So, it's just a matter of setting it in the CSS:

yourSelector {
    mix-blend-mode: someValue;
}

This is a demo:

text {
	mix-blend-mode: difference;
}
<svgwidth="400"heigth="200"><rectx="10"y="10"height="30"width="350"fill="green"></rect><rectx="10"y="50"height="30"width="150"fill="green"></rect><rectx="10"y="90"height="30"width="50"fill="green"></rect><textx="20"y="30"fill="white">I am a very very very very long long long long long text</text><textx="20"y="70"fill="white">I am a very very very very long long long long long text</text><textx="20"y="110"fill="white">I am a very very very very long long long long long text</text></svg>

There are two problems: you cannot control the colours precisely, and it is not supported by IE/Edge.

Post a Comment for "Svg Text Color With Correspond To Background"