SVG <use> ignores Gradient styling - svg

I have a svg file that does not render gradient in the <use> tag. Why is it behaving differently, and how can I fix it?
SVG File:
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100"
style="stroke:#000000;stroke-width:2"
xml:space="preserve"
id="pb_svg_1">
<rect width="50" height="50" style="fill: url(#lg1)"/>
<defs>
<linearGradient id="lg1" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:rgb(88,88,88);stop-opacity:1"></stop>
<stop offset="100%" style="stop-color:rgb(255,255,255);stop-opacity:1"></stop>
</linearGradient>
</defs>
</svg>
These are 3 methods I render the svg. 2 work but the one I need does not:
<!-- Does NOT include gradient style -->
<svg width="100" height="100">
<use href="12.svg#pb_svg_1" width="100" height="100"/>
</svg>
<!-- Gradient style works! -->
<div>
<object data="12.svg" width="100" height="100"></object>
</div>
<!-- Gradient style works! -->
<div style="width: 100px;height: 100px">
<embed src="12.svg"/>
</div>
I expect the use element to render the file as it does when the svg is on the same page.
EDIT: It does work in firefox and does not work in chrome and edge

Workaround: define gadients in an inlined svg
Move the gradient <defs> to an inlined hidden <svg>.
It's important to hide this svg via zero width and height properties like width:0; height:0;position:absolute;.
display:none or visibility:hidden will remove/disable gradients, clip paths etc.
<!-- HTML svg use instance -->
<svg width="100" height="100" viewBox="0 0 100 100">
<use href="#pb_svg_1" style="fill: url(#lg1); stroke:#000000;stroke-width:2 "/>
</svg>
<!-- Inline svg: hidden gradient definition -->
<svg style="width:0; height:0; position:absolute;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">
<defs>
<linearGradient id="lg1" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:rgb(88,88,88);stop-opacity:1"/>
<stop offset="100%" style="stop-color:rgb(255,255,255);stop-opacity:1"/>
</linearGradient>
</defs>
</svg>
<!-- External svg: 12.svg -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<symbol id="pb_svg_1" viewBox="0 0 100 100">
<rect x="0" y="0" width="50" height="50" /></symbol>
</svg>
Workaround 2: inline external use references
If refactoring all svg assets isn't feasible – change your embedding method.
2.1 via fetch
HTML
<!-- HTML svg use instance -->
<svg width="100" height="100" viewBox="0 0 100 100">
<use href="12.svg#pb_svg_1" />
</svg>
Js
inlineExternalUse();
function inlineExternalUse(){
let extSvgs = document.querySelectorAll('use');
if(extSvgs.length){
extSvgs.forEach(function(item, i){
let href = item.getAttribute('href') ? item.getAttribute('href') : item.getAttribute('xlink:href');
// change href to inline reference
let hrefNew = '#'+href.split('#')[1];
item.setAttribute('href', hrefNew);
fetch(href)
.then(response => response.text() )
.then(data => {
//inline ext svg
let parser = new DOMParser();
let svgInline = parser.parseFromString(data, "application/xml").querySelector('svg');
svgInline.setAttribute('aria-hidden', 'true')
svgInline.style.width=0;
svgInline.style.height=0;
svgInline.style.position='absolute';
document.body.appendChild(svgInline);
});
});
}
}
2.2: via native web component
See #Danny '365CSI' Engelman's article "〈load-file〉Web Component, add external content to the DOM" or this answer "How to change the color of an svg element?"

Related

Why image is showing default image, instead of original image?

So I am working on recharts where I am customising dots which can be only done with svg.
<svg className="svg-triangle">
<g>
<defs>
<pattern id="image" x="0%" y="0%" height="100%" width="100%"
viewBox="0 0 512 512">
<image x="0%" y="0%" width="400" height="400" href="../../Assets/Images/Icons/triangle_default.png"></image>
</pattern>
</defs>
<circle cx={cx} cy={cy} r={6} fill="url(#image)" />
</g>
</svg>
But it keeps showing like this and I don't understand why
If the image is stored locally alongside your code, then you need to import or require the URL to load the file.
const triangle = require('../../Assets/Images/Icons/triangle_default.png');
import triangle from '../../Assets/Images/Icons/triangle_default.png';
<image x="0%" y="0%" width="400" height="400" href={triangle}/>
or inline
<image x="0%" y="0%" width="400" height="400" href={require('../../Assets/Images/Icons/triangle_default.png')}/>
See also: How do I reference a local image in React?

Alternative to <use/> to avoid repeating ids when repeating Inline SVG

Plunkr example.
So lets say I have svg that I am including inline in HTML
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100" class="col-sm-3">
<symbol id="circle" viewBox="0 0 50 50">
<circle cx="25" cy="25" r="25" fill="white" stroke="black" stroke-width="2"></circle>
<text x="50%" y="50%" text-anchor="middle" dy=".3em">___THIS_IS_A_VARIABLE__</text>
</symbol>
<use xlink:href="#circle" width="85" height="85"/>
</svg>
I have a slightly more complex use care where the inside of the <symbol/> is a complex path of a different scale than the svg root viewbox so I need to use the <symbol/> to scale it to be full size.
I need to include a bunch of these in my html which I do via a for loop. Everything works find, but I am afraid I might introduce bugs in the future because in my html document I have multiple elements with the same id.
My questions:
Is there a different way to use <use/> like with a css selector?
Am I correct that repeating ids in inline SVG inside a html document is as bad as repeating ids in a html document itself?
Is there a better way to nest viewbox attributes to avoid <use/>?
The best way I can think of is to make the id dynamic. So in ther case of using angular it would be:
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100" class="col-sm-3">
<symbol [id]="circleId" viewBox="0 0 50 50">
<circle cx="25" cy="25" r="25" fill="white" stroke="black" stroke-width="2"></circle>
<text x="50%" y="50%" text-anchor="middle" dy=".3em">___THIS_IS_A_VARIABLE__</text>
</symbol>
<use [attr.xlink:href]="'#' + circleId" width="85" height="85"/>
</svg>
No need for a Framework, native W3C Web Components can do the same:
If you don't/can't specify an id, generate one: let id = "id" + new Date()/1;
svg {
display: inline-block;
background: grey;
}
<svg-shape id=1 text="red"></svg-shape>
<svg-shape id=2></svg-shape>
<svg-shape id=3 text="blue"></svg-shape>
<script>
customElements.define('svg-shape', class extends HTMLElement {
connectedCallback() {
let id = this.getAttribute("id");
let text = this.hasAttribute("text") ? this.getAttribute("text") : "";
this.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100" class="col-sm-3">
<symbol id="id${id}" viewBox="0 0 50 50">
<circle cx="25" cy="25" r="25" fill="white" stroke="${text}" stroke-width="2"></circle>
<text x="50%" y="50%" text-anchor="middle" dy=".3em">${text}</text>
</symbol>
<use href="#id${id}" width="85" height="85"/>
</svg>`;
this.onclick = (evt) => console.log('clicked', id);
}
});
</script>

Image tag in svg pattern definition is not accepting an svg format image [duplicate]

This question already has answers here:
SVG data image not working as a background-image in a pseudo element
(4 answers)
Closed 3 years ago.
The following HTML code renders me a pattern that contains an image of type .svg inside another SVG.
<html>
<body>
<svg width="0px" height="0px">
<defs>
<pattern id="saint1729" patternUnits="userSpaceOnUse" width="8" height="8" fill="#ff7156">
<image xlink:href="http://saint1729.me/tiny-checkered-pattern.svg" x="0" y="0" width="8" height="8" />
</pattern>
</defs>
</svg>
<div>
<svg width="240" height="16">
<path d="M0 0 H240 V16 H0 Z" fill="url(#saint1729)"/>
</svg>
</div>
</body>
</html>
When I changed the image attribute href inside pattern to direct SVG tag (which is coming from the same URL), I don't see anything rendered. Here's the code for this scenario.
<html>
<body>
<svg width="0px" height="0px">
<defs>
<pattern id="saint1729" patternUnits="userSpaceOnUse" width="8" height="8" fill="#ff7156">
<image xlink:href="<svg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'><g fill='#c82c0e' fill-opacity='1'><path fill-rule='evenodd' d='M0 0h4v4H0V0zm4 4h4v4H4V4z'/></g></svg>" x="0" y="0" width="8" height="8" />
</pattern>
</defs>
</svg>
<div>
<svg width="240" height="16">
<path d="M0 0 H240 V16 H0 Z" fill="url(#saint1729)"/>
</svg>
</div>
</body>
</html>
What is the difference between the two? How to make the second approach work?
I am using Google Chrome browser.
However this is working. You need to encode the svg. I use this SVG encoder. Also please read this post: Optimizing SVGs in data URIs
<svg width="0px" height="0px">
<defs>
<pattern id="saint1729" patternUnits="userSpaceOnUse" width="8" height="8" fill="#ff7156">
<image xlink:href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3E%3Cg fill='%23c82c0e' fill-opacity='1'%3E%3Cpath fill-rule='evenodd' d='M0 0h4v4H0V0zm4 4h4v4H4V4z'/%3E%3C/g%3E%3C/svg%3E" x="0" y="0" width="8" height="8" />
</pattern>
</defs>
</svg>
<div>
<svg width="240" height="16">
<path d="M0 0 H240 V16 H0 Z" fill="url(#saint1729)"/>
</svg>
</div>

svg transparent radial gradient in safari not working

I have a SVG radial gradient that works in Chrome, Firefox and even Internet Explorer but does not work in Safari. Any idea how to get this to work in Safari?
Here is the codepen: http://codepen.io/fractorr/pen/OVaYvV
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<radialGradient r="50%" cy="50%" cx="50%" id="myRadialGradient2">
<stop stop-color="transparent" offset="0"></stop>
<stop stop-color="#000000" offset="1"></stop>
</radialGradient>
</defs>
<rect x="10" y="10" width="100" height="100" rx="10" ry="10" style="fill:blue;"/>
<rect x="10" y="10" width="100" height="100" rx="10" ry="10" style="fill:url(#myRadialGradient2); stroke: #005000; stroke-width: 3;"/>
</svg>
In your gradient's definition, alter the opacity for the stop points. So,instead of shifting the color from a given value to transparency, you would alter the transparency itself. The result seems to mimick the firefox behavior accurately.
Leaving the stop-color attributes in the code does not harm the displayed result. However, the duplicate computation is pointless, and given that bitmapping a gradient is relatively costly iirc, better drop it.
See here for a demo: http://codepen.io/anon/pen/aOQreP
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<radialGradient r="50%" cy="50%" cx="50%" id="myRadialGradient2">
<stop stop-opacity="0" offset="0"></stop>
<stop stop-opacity="1" offset="1"></stop>
</radialGradient>
</defs>
<rect x="10" y="10" width="100" height="100" rx="10" ry="10" style="fill:blue;"/>
<rect x="10" y="10" width="100" height="100" rx="10" ry="10" style="fill:url(#myRadialGradient2); stroke: #005000; stroke-width: 3;"/>
</svg>
These modifications shouldn't affect the rendering on other platforms.
Tested on Safari 5.1.7 (7534.57.2) on Windows.

Simple fill pattern in svg : diagonal hatching

How would I fill an SVG shape, not with a single colour, an image or a gradient, but with a hatching pattern, diagonal if possible.
It's been 2 hours and I've found nothing (at least after 2005).
I figure a possible hack would be a hatched PNG that would serve as fill, but that is not ideal.
I did not find anything for diagonal hatching on the internet either, so I'll share my solution here:
<pattern id="diagonalHatch" patternUnits="userSpaceOnUse" width="4" height="4">
<path d="M-1,1 l2,-2
M0,4 l4,-4
M3,5 l2,-2"
style="stroke:black; stroke-width:1" />
</pattern>
(note the lower case "l" in the path expression)
The above creates a hatch with diagonal lines from the lower left to the upper right that are 4 pixels apart. Besides the diagonal line (M0,4 l4,-4) you also have to stroke the upper left and the lower right edges of the pattern area, since the line will otherwise be "constricted" due to clipping where it intersects the edges of the square.
To fill a rectangle with this pattern, do:
<rect x="0" y="0" width="100%" height="100%" fill="url(#diagonalHatch)"/>
Use the patternTransform attribute to rotate a vertical (or horizontal) line segment. This method tiles seamlessly and uses the simplest possible path. The pattern width attribute controls how close parallel hatches are.
<pattern id="diagonalHatch" width="10" height="10" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
<line x1="0" y1="0" x2="0" y2="10" style="stroke:black; stroke-width:1" />
</pattern>
This code from http://bl.ocks.org/jfsiii/7772281 seems very clean and reusable:
svg {
width: 500px;
height: 500px;
}
rect.hbar {
mask: url(#mask-stripe)
}
.thing-1 {
fill: blue;
}
.thing-2 {
fill: green;
}
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>SVG colored patterns via mask</title>
</head>
<body>
<svg>
<defs>
<pattern id="pattern-stripe"
width="4" height="4"
patternUnits="userSpaceOnUse"
patternTransform="rotate(45)">
<rect width="2" height="4" transform="translate(0,0)" fill="white"></rect>
</pattern>
<mask id="mask-stripe">
<rect x="0" y="0" width="100%" height="100%" fill="url(#pattern-stripe)" />
</mask>
</defs>
<!-- bar chart -->
<rect class="hbar thing-2" x="0" y="0" width="50" height="100"></rect>
<rect class="hbar thing-2" x="51" y="50" width="50" height="50"></rect>
<rect class="hbar thing-2" x="102" y="25" width="50" height="75"></rect>
<!-- horizontal bar chart -->
<rect class="hbar thing-1" x="0" y="200" width="10" height="50"></rect>
<rect class="hbar thing-1" x="0" y="251" width="123" height="50"></rect>
<rect class="hbar thing-1" x="0" y="302" width="41" height="50"></rect>
</svg>
</body>
</html>
You may be able to create, what you want using a <pattern> tag.
As a starting point you might take this example of the respective MDN docu:
<?xml version="1.0"?>
<svg width="120" height="120" viewBox="0 0 120 120"
xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<pattern id="Triangle"
width="10" height="10"
patternUnits="userSpaceOnUse">
<polygon points="5,0 10,10 0,10"/>
</pattern>
</defs>
<circle cx="60" cy="60" r="50"
fill="url(#Triangle)"/>
</svg>
One problem with drawing a diagonal line within a pattern is that when the pattern is tiled the lines won't always line up - especially at high zooms. (It depends on the SVG rendering engine you happen to be using).
#Ingo's answer above attempts to resolve this by drawing in the triangles at the top-left and bottom-right corners - but again, using some rendering engines and high zooms, it doesn't always look best - and sometimes the line ends up looking a bit like a string of sausages.
Another approach is to draw a horizontal line in the pattern and rotate the pattern, e.g.
<svg:svg viewBox="0 0 100 100" version="1.1"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<svg:defs>
<svg:pattern id="diagonalHatch" patternUnits="userSpaceOnUse" width="4" height="4" patternTransform="rotate(45 2 2)">
<svg:path d="M -1,2 l 6,0" stroke="#000000" stroke-width="1"/>
</svg:pattern>
</svg:defs>
<svg:rect x="0" y="0" height="100" width="100" fill="url(#diagonalHatch)"/>
These two resources are very helpful:
https://bocoup.com/weblog/using-svg-patterns-as-fills
https://github.com/iros/patternfills/blob/master/public/patterns.css
For example:
<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'>
<rect width='10' height='10' fill='red'/>
<path d='M-1,1 l2,-2
M0,10 l10,-10
M9,11 l2,-2' stroke='orange' stroke-width='2'/>
</svg>
This is a solution for diagonal lines using circle in pattern. You can change angle as per your requirements.
<svg width="500" height="500">
<defs>
<pattern id="transformedPattern"
x="0" y="0" width="2" height="20"
patternUnits="userSpaceOnUse"
patternTransform="rotate(45)">
<circle cx="1" cy="1" r="2" style="stroke: none; fill: #0000ff" />
</pattern>
</defs>
<rect x="10" y="10" width="100" height="100"
style="stroke: #000000; fill: url(#transformedPattern);" />
</svg>
I tried with this sample. Hopefully, It can help you much.
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>SVG colored patterns via mask</title>
</head>
<body>
<svg viewBox="0 0 300 300" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="stripes" viewBox="0,0,8,8" width="16" height="16" patternUnits="userSpaceOnUse">
<polygon points="0,0 4,0 0,4" fill="yellow"></polygon>
<polygon points="0,8 8,0 8,4 4,8" fill="yellow"></polygon>
<polygon points="0,4 0,8 8,0 4,0" fill="green"></polygon>
<polygon points="4,8 8,8 8,4" fill="green"></polygon>
</pattern>
</defs>
<rect fill="url(#stripes)" x="150" y="20" width="100" height="50" />
<circle cx="50" cy="50" r="50" fill="url(#stripes)"/>
</svg>
</body>
</html>
Regards,
Vu Phan
SVG 2 has a hatch entity for specifically this purpose. From that page's example section:
<hatch hatchUnits="userSpaceOnUse" pitch="5" rotate="135">
<hatchpath stroke="#a080ff" stroke-width="2"/>
</hatch>
This is a very easily configurable way to create hatches:
Furthermore the hatch path can also be customised:
<hatchpath stroke-width="1" d="C 0,4 8,6 8,10 8,14 0,16 0,20"/>
For React Native use can use this component, for making background lines pattern.
You should add to your project react-native-svg
import PropTypes from 'prop-types';
import React, { PureComponent } from "react";
import { View } from "react-native";
import Svg, { Defs, Line, Pattern, Rect } from 'react-native-svg';
export default class PatternLineView extends PureComponent {
static propTypes = {
pattern: PropTypes.func.isRequired,
space: PropTypes.number,
backgroundColor: PropTypes.string,
lineColor: PropTypes.string,
lineWidth: PropTypes.number,
rotation: PropTypes.number
}
static defaultProps = {
pattern: () => { },
space: 8,
lineColor: "#D2D9E5",
lineWidth: 3,
rotation: 45
}
render() {
const transform = `rotate(${this.props.rotation})`
return <View style={{
flex: 1,
flexDirection: "row",
height: "100%",
width: "100%",
position: "absolute",
top: 0,
start: 0,
backgroundColor: this.props.backgroundColor
}}>
<Svg width="100%" height="100%">
<Defs>
<Pattern
id="linePattern"
patternUnits="userSpaceOnUse"
patternTransform={transform}
width={this.props.space}
height={this.props.space}>
<Line
x1="0"
y1="0"
x2="0"
y2="100%"
stroke={this.props.lineColor}
strokeWidth={this.props.lineWidth}
/>
</Pattern>
</Defs>
<Rect
fill="url(#linePattern)"
x="0"
y="0"
width="100%"
height="100%"
/>
</Svg>
</View>
}
}
I've adapted Ingo's answer here.
<defs>
<pattern id="diagonalHatch" patternUnits="userSpaceOnUse" width="4" height="4">
<!-- background -->
<path id="background"
d="M-1,3 L3,-1
M1,5 L5,1" style="stroke:pink; stroke-width:10" />
<!-- hatches -->
<path id="hatches"
d="M-2,2 L2,-2
M0,4 L4,0
M2,6 L6,2" style="stroke:red; stroke-width:1" />
</pattern>
</defs>
This pattern includes two paths, one for the background, and other for the hatches. The background color is addressable vs JS such as:
const hatchPath = document.querySelector("path#hatches");
hatchPath.setAttribute('style', "stroke:blue; stroke-width:1")
The background path is overly-wide on purpose, so that there's no part of the pattern not at least covered by the background. Meanwhile, the hatches can have their width tuned to change how thick the lines are.
Some great points got lost in the comments, so I wanted to aggregate that knowledge with some simpler inline examples. As far as I can tell, what method you choose to use is entirely up to preference since the heavy lifting is being done by patternTransform + rotate either way, but personally I think the <rect> method is easier to digest at-a-glance even if it might make more semantic sense to use <line>.
SVG Non-Scaling Pattern with <rect>
Define the spacing between your lines with the <pattern>'s width, and the width of the lines themselves via the nested <rect>'s width.
Codepen example as full-size background.
<svg height="100%" width="100%" xmlns="http://www.w3.org/2000/svg">
<pattern id="diaHatch" width="9" height="1" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
<rect x="0" y="0" width="3" height="1" fill="red" />
</pattern>
<rect x="0" y="0" width="100%" height="100%" fill="url(#diaHatch)" />
</svg>
SVG Non-Scaling Pattern with <line>
Define the spacing between the <line>s with the pattern height, and use stroke-width for the <line> itself.
Codepen example as full-size background.
<svg height="100%" width="100%" xmlns="http://www.w3.org/2000/svg">
<pattern id="diaHatch" width="1" height="9" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
<line x1="0" x2="100%" y1="0" y2="0" stroke-width="9" stroke="black" />
</pattern>
<rect x="0" y="0" width="100%" height="100%" fill="url(#diaHatch)" />
</svg>
On SVG 2 & Hatches [as of October 2022]
While the above examples solve the issue, SVG 2's Hatches (Candidate Recommendation 2016) are a documented method of tackling this exact issue. Inkscape happens to implement them because one of its developers, Tavmjong Bah, was an Invited Expert at the time, but browsers are still tackling them. To note, the latest Editor's Draft (2018) doesn't include hatch, which doesn't necessarily mean anything, but might be why it hasn't yet been prioritized by browser vendors.
Tracking SVG 2 Features for the Browser
Each major engine has an ongoing thread for feature implementations:
SVG 2 in Gecko
SVG 2 in Blink
SVG 2 in Webkit

Resources