How to prevent inconsistent thickness and gaps in SVG lines? - css

My use case is probably a bit unique, but I am creating a dynamically-sized square grid using DOM elements encased in a flexbox. Each DOM element has an SVG image background and a height/width equal to calc(100vmin / <gridSize>). The SVG elements are simple:
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
<path stroke-width="2" stroke="#000" stroke-linecap="square" d="M0,50 h100 M50,0 v50"/>
</svg>
However, I noticed that some lines appear darker than others, and there are gaps between some SVG lines. (See picture below) I'm assuming this is due to pixel rounding because when I resize the browser, the gaps and thicknesses jump around.
I tried to round the pixels from the calc, but I am using styled-components so I am unable to use SASS / LESS functions like floor / ceil. I also noticed making the stroke width thicker alleviates the problem, but I would prefer to keep my lines thin.
Are there other ways I can make the lines consistent?

It's antialiasing. Setting shape-rendering="crispEdges" will turn it off on most UAs.

vector-effect='non-scaling-stroke'
non-scaling-stroke
This value modifies the way an object is stroked. Normally stroking involves calculating stroke outline of the shapeʼs path in current user coordinate system and filling that outline with the stroke paint (color or gradient). The resulting visual effect of this value is that the stroke width is not dependent on the transformations of the element (including non-uniform scaling and shear transformations) and zoom level.
MDN web docs

Related

One path/shape SVG with mysterious left padding/white space. Not artboard.

I designed a shape in Illustrator and trimmed artboard to shape so no outside white space. I've done this twice to be sure.
When I open it in Chrome and inspect. If I hover over the whole SVG tag it's tight to the shape. If I hover over the path, there's a white space/padding left (and top, actually) - see screenshot.
It means when I place or animate the shape it's not right. I don't know how I can get rid of it - any ideas?
Code:
<div style="margin:100px">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 157.144 210.706">
<path fill="#444" d="M78.394 210.706c-11.247-13.16-22.162-25.304-32.368-38.017-16.016-19.95-30.158-41.092-39.98-64.948-19.25-46.76 9.943-94.135 52.535-105.196 45.651-11.856 91.876 19.016 97.966 65.671 2.028 15.534-1.288 29.968-7.385 44.017-11.15 25.692-27.51 48.023-45.258 69.398-7.965 9.593-16.436 18.766-25.51 29.075zm52.722-131.714c.046-29.268-23.232-52.857-52.283-52.982-28.984-.125-52.775 23.526-52.815 52.506-.041 29.231 23.339 52.858 52.341 52.895 29.291.038 52.712-23.233 52.757-52.419z"/>
</svg>
</div>
It seems Chrome is taking into account the control points when painting the bounding box:
You can avoid this by reordering your control points, and this creates a better shape for your pin:
Here's the Path, but I did it quickly, you might want to redraw it yourself with more care.
<path d="m78.394,211.00938c-11.247,-13.16 -22.162,-25.304 -32.368,-38.017c-16.016,-19.95 -44.72017,-56.5643 -45.74419,-94.98246c-1.02402,-38.41816 32.67117,-77.86771 78.32217,-77.89195c45.651,-0.02424 78.27532,41.16539 78.54978,79.92979c0.27446,38.7644 -35.50176,80.51162 -53.24976,101.88662c-7.965,9.593 -16.436,18.766 -25.51,29.075zm52.722,-131.714c0.046,-29.268 -23.232,-52.857 -52.283,-52.982c-28.984,-0.125 -52.775,23.526 -52.815,52.506c-0.041,29.231 23.339,52.858 52.341,52.895c29.291,0.038 52.712,-23.233 52.757,-52.419z" fill="#444"/>

How can I style a solid-color image to match the text color?

On a website where users can submit text posts containing images and also change various formatting aspects of the website, such as the text color, I'd like to allow them to submit images that match whatever text color the viewer has chosen.
For instance, if someone wants to include a complex mathematical formula in their post, they currently have to either use very strange notation or submit an image. Readers may not be able to see this image well or at all if they have selected certain background colors.
With the system I'm hoping to implement, someone could submit an image that's a solid color with a varying alpha channel. It would be styled with CSS to be currentColor.
Blend modes don't work because they also blend with elements behind the image. I would prefer not to complicate things by drawing with JavaScript on a canvas, because I'd have to set it up to update every time the user changes their formatting. I also can't just use JavaScript to inline an SVG because that would leave the site open to XSS attacks, and users may not want to go to the trouble of constructing an SVG file where every color is set to currentColor.
Is there a way to do this? What is it?
I tried using kahjav's pure-CSS answer, but the hue-rotate filter works in arcane ways, and I found it was far too difficult to figure out what combination of invert, brightness, and hue-rotate I would need to emulate a given CSS color. So, I came up with a method that actually uses currentColor, as I hoped I could.
I said it wouldn't work to let users specify an SVG file and then inline it with JavaScript, but I can use inline SVG with JavaScript. If I put an invert filter on the user's image and then use it as a mask on a rectangle with the fill set to currentColor, the results are exactly what I want. I do have to use JavaScript to generate the inline SVG, because several elements' widths and heights must be determined from the width and height of the contained image. However, I don't need to use JavaScript to update it every time the user selects a different theme.
Here's a JSFiddle to demonstrate. It generates inline SVG of the form:
<svg width="[width]" height="[height]" id="[id]" class="[class]" style="[style]">
<defs>
<filter id="invert" style="color-interpolation-filters:sRGB">
<feColorMatrix values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
</filter>
<mask id="img" maskUnits="userSpaceOnUse">
<image xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="[image source]" width="[width]" height="[height]" filter="url(#invert)" />
</mask>
</defs>
<rect mask="url(#img)" width="[width]" height="[height]" fill="currentColor" />
</svg>
You can add a CSS filter and use the hue rotate function:
div {
filter: hue-rotate(/*insert deg to desired color*/);
}
Here is a great article on filters. You will need to change each image according to where you want it to be. The rotate is in accordance with a color wheel.

How do I scale a group of text tags relative to the size of a page?

I've been messing with this for weeks, so I'm just gonna flat out ask how to do it.
I've been working on a little SVG countdown clock just for the hell of it. But when I try and position things relative to how the page might be sized, I get stuck. I can't seem to size text tags relative to the page, and I can't find a way to put them into a group for sizing.
I've tried putting them into symbol tags, and using viewBox, but I can't seem to get that to work either.
My intended goal is to have this working on any sized screen from monitors to smartphones. So I wanted to do something with a min, and max width/height, or something along those lines, and apply it to a combination of the number and it's associated label. So I would have one group for days with both the number of days(being changed by the script), and then the word "days" underneath it.
Here is what I'm working with if it helps:
http://jsfiddle.net/2P9qV/
Does anyone have any ideas? I've been stumped for weeks.
Ok, so I finally figured it out. Luckily, I was able to use multiple SVGs within an SVG to create content, and then display it using use tags.
So I had each set as part of it's own symbol in defs. From there, I used the viewBox on the symbol tag to limit the draw space to the exact width and height of the content created, which I found by inspecting the element, and using the height and width it had been made as(without the 'px'). This way, I could center the whole set in the SVG that had the use tag, then set the width of the SVG to 100%, and the height to the percentage I wanted to scale it to. Since these are set as percentages, the page will limit the SVG's size to whichever is most limiting, while still maintaining scale, so it won't skew.
So one of the symbols would be:
<symbol id="daysSymbol" viewBox="0 0 110 156">
<g>
<text id="days" y="0" x="50%" font-size="100" class="time big">00</text>
<text id="daysT" y="100" x="50%" font-size="40" class="text bigText">DAYS</text>
</g>
</symbol>
and the SVG for this would be
<svg id="daysSVG" height="50%" width="100%" y="30%">
<use xlink:href="#daysSymbol" height="100%"/>
</svg>
It also let me get creative with how I managed the contents on the page. As something happened, I was able to dynamically remove content, and scale other elements to take up the new space.
For those curious, here is the final product
https://hostr.co/file/PLCY7lodb7Lk/xboxCountdown.svg
It won't last for that long, since it was a countdown clock. But I won't take it down(as long as it doesn't get automatically deleted), and if you wanted to see how it worked, you cold just download it and change the date it counted down to.

SVG text disappears on larger label

I am using Morris.js for making charts. Morris.js uses SVG to draw the graphs. I have set up a JSbin here: JSbin example
Morris.js uses Raphael to draw the svg graphs. The issue is with the labels on the X-Axis. They disappear when the size of labels is too large. I tinkered around with the size of the div element holding the graph and font size of the labels but since the labels are dynamically generated for various users I cannot decide on a fixed value. An ideal solution would be to have the text wrapped up. What can be done to counter the situation?
Morris.js uses the following snippet to make the text svg element.
this.raphael.text(xPos, yPos, text).attr('font-size', '11').attr('font-family', 'calibri').attr('fill', this.options.gridTextColor);
It seems raphael supports multiline strings by putting "\n" in the text. This could be a cheap solution for you, systematically replacing " " by "\n" in your labels.
The other (more tricky) solution would be to replace the "text" element in the SVG generated by raphael by a foreign element that allows word wrapping:
<foreignObject x="20" y="10" width="150" height="200">
<p xmlns="http://www.w3.org/1999/xhtml">Wrapping text using some foreignObject in SVG!</p>
</foreignObject>
or if you need a fallback:
<switch>
<foreignObject x="160" y="10" width="150" height="200"><p xmlns="http://www.w3.org/1999/xhtml">Wrapping text using some foreignObject in SVG!</p></foreignObject>
<text x="20" y="20">Your SVG viewer cannot display html.</text>
</switch>
I don't know the best way to do this replacement of "text" by a foreign object: either after the Morris rendering or by hacking Morris/Raphael.
I think the problem definition need more clarification,
at my end the code you have sent works fine, also I have replicated the same in JSFIDDLE that is also working fine..
I think the label problem is done automatically as I have tested the default size you have kept was carrying width 385px when looking in to CSS and 518px when looking the same, in both case the rendered labels were different, in-fact the label exceeding certain width were not rendered. SO there is no meaning to set style and override.
I think the things are built in library. So its a long way to go, or change the library.
let me know which will be convenient way, I will help you accordingly :)
Edit:
However, you can change property of GridTextSize: that is 16 by default
Fiddle2 here I have updated
'gridTextSize:8' that displays all text in size 8.
Not perfect but will do :)
Download not minimized morris.js from there. In zip-archive you can find a file morris.js. Open it in edit mode and change value of default label angle parameter (line 133):
133 | xLabelAngle: 90,//original value 0
Now, if you really need it minimized, you can compress file by any online js compress tool.

Background color of tspan element

Is it possible to give SVG <tspan> element background color? If not, what would be the best way to simulate it?
My goal is to give text background color, and I figured that filling <tspan> elements would be perfect — they already "outline" text chunks (<tspan> elements) that represent lines in multiline text.
The example I'm working with:
<text x="100" y="100" font-size="30">
<tspan>hello</tspan>
<tspan x="100" dy="1.2em">world</tspan>
</text>
I tried "fill" attribute but it seems to affect fill (color) of text, not the area behind it:
<tspan fill="yellow">hello</tspan>
I also tried setting background-color via CSS:
<style type="text/css">tspan { background-color: yellow }</tspan>
..but that doesn't work (in at least Chrome 17 and Firefox 12).
Wrapping tspan in <g> (or text itself in <g>) with "fill" doesn't work either:
<g fill="yellow"><tspan>hello</tspan></g>
<tspan><g fill="yellow">hello</g></tspan>
Aside from creating a <rect> element positioned at the same location — something I'd like to avoid — is there another way to achieve this?
A rect is probably the best way to do that (assuming you are only dealing with the simplest forms of text).
The tspans have no "background" themselves in SVG 1.1, the background is whatever gets painted before the tspan. There are many cases to consider, such as the case where a tspan is inside a textPath that has the shape of a circle. Also note that it's not as simple as a continous rectangle in all cases, a single tspan can be skewed, rotated and partitioned into several intersecting and non-intersecting shapes due to transforms, glyph positioning, etc.
There's another way I can think of that would do this without scripting, but then you'd need to know the width of the string in advance (e.g by using a monospaced font). If you have that then you can add another tspan element using the Ahem font, placing it before the other tspan in the document and giving it the same x,y position as the tspan you want to give a "background".
Otherwise the way to do this is through scripting, and adding rectangles (or tspans with an Ahem-like font).
SVG does not support directly specifying an image background color...but you can adjust the four values of the viewBox attribute (complicated). I know it's something you want to avoid, but CSS wouldn't help you.
...or you can use getBBox and getCTM...it would give you advantages for rotated text.
EXAMPLE:
http://srufaculty.sru.edu/david.dailey/svg/getCTM.svg

Resources