I'm drawing a simple US map with state borders drawn. Eventually there will be hover and click events. However, anywhere the states touch, the borders appear double thickness. On the assumption that there isn't something wrong with my shapes, how can I keep these lines from getting double thickness? Is there a CSS setting?
It's written in React, but I hope that's not relevant. In the end it's just path objects in a group in an svg.
export const USMap = (props: Props) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 959 593">
<title>Map of US States</title>
<g id="states">
{Object.entries(states).map(([abbreviation, state]) => (
<path key={abbreviation} d={state.dimensions} fill="#fff" stroke="#000" strokeWidth="1" />
))}
</g>
</svg>
);
};
In my React app, I want to be able to crop an image and embed it into a hexagonal SVG shape to use as an avatar. I've done the cropping part and made the SVG border, just one problem that I have is that the embedded picture overflows the border approximately to its half and I want that border to be fully visible.
Here is my code:
export const HexagonFrame = ({ logo }) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="63.191"
height="69.06"
viewBox="0 0 63.191 69.06"
>
<defs>
<clipPath id="shape">
<path
id="hex"
d="M4.2,34.351c.161-3.452.08-6.825.482-10.277A13.043,13.043,0,0,1,11.025,14.2c4.416-2.81,8.993-5.379,13.489-8.029,4.978-2.971,9.956-2.81,14.854.08C43.382,8.659,47.4,11.068,51.412,13.4c4.737,2.81,7.547,6.9,7.708,12.445.161,6.182.08,12.445-.241,18.627a12.05,12.05,0,0,1-5.54,9.474c-5.058,3.292-10.277,6.423-15.577,9.394a12.22,12.22,0,0,1-12.285-.08c-4.9-2.73-9.635-5.62-14.372-8.591-4.577-2.89-6.664-7.226-6.825-12.525-.08-2.569,0-5.138,0-7.788Z"
transform="translate(-0.01 0.042)"
fill="none"
stroke="#fff"
strokeMiterlimit="10"
strokeWidth="8"
strokeOpacity="1"
/>
</clipPath>
</defs>
<use xlinkHref="#hex" />
{logo && (
<image
width="63.191"
height="69.06"
clipPath="url(#shape)"
xlinkHref={logo}
id="logo"
/>
)}
</svg>
);
};
And screenshots of the hexagonal shape I'm working with. As you can see the border is overflown by the picture inside
You must position the stroke in front of the image.
SVG strokes are positioned onto the middle of the path they are defined with. So if you just change the order, part of the image will be obscured by the stroke. To avoid that, you need to reposition (by 4px, half the width of the stroke) and resize (by 8px, the full width of the stroke) the image. Like this:
export const HexagonFrame = ({ logo }) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="63.191"
height="69.06"
viewBox="0 0 63.191 69.06"
>
<defs>
<clipPath id="shape">
<path
id="hex"
d="M4.2,34.351c.161-3.452.08-6.825.482-10.277A13.043,13.043,0,0,1,11.025,14.2c4.416-2.81,8.993-5.379,13.489-8.029,4.978-2.971,9.956-2.81,14.854.08C43.382,8.659,47.4,11.068,51.412,13.4c4.737,2.81,7.547,6.9,7.708,12.445.161,6.182.08,12.445-.241,18.627a12.05,12.05,0,0,1-5.54,9.474c-5.058,3.292-10.277,6.423-15.577,9.394a12.22,12.22,0,0,1-12.285-.08c-4.9-2.73-9.635-5.62-14.372-8.591-4.577-2.89-6.664-7.226-6.825-12.525-.08-2.569,0-5.138,0-7.788Z"
transform="translate(-0.01 0.042)"
fill="none"
stroke="#fff"
strokeMiterlimit="10"
strokeWidth="8"
strokeOpacity="1"
/>
</clipPath>
</defs>
{logo && (
<image
x="4"
y="4"
width="55.191"
height="61.06"
clipPath="url(#shape)"
xlinkHref={logo}
id="logo"
/>
)}
<use xlinkHref="#hex" />
</svg>
);
};
I am trying to create a kind of "spotlight" effect over a large background image where the user can use the cursor to view parts of an image.
I'm currently applying a gaussian blur filter to a mask element which is positioned at the cursor and reveals parts of the large image.
I would like to have a higher blur amount in order to get a softer edge, but when I increase the value of the stdDeviation attribute inside the filter, the border of the SVG is revealing – I have attached two images to illustrate, also you can see it in this pen https://codepen.io/moevbiz/pen/YbwErx
Here is my code:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%">
<defs>
<filter id="filter">
<feGaussianBlur stdDeviation="50"/>
</filter>
<mask id="mask">
<ellipse id="ellipse" cx="50%" cy="50%" rx="100" ry="100" fill="white" filter="url(#filter)"></ellipse>
</mask>
</defs>
<image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/Harry-Potter-1-.jpg" width="100%" height="100%" mask="url(#mask)"></image>
</svg>
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/Harry-Potter-1-.jpg" alt="" />
body {
margin: 0;
padding: 0;
height: 100vh;
width: 100vw;
overflow:hidden;
}
img {
width: 100vw;
height: 100vh;
mask: url(#mask);
}
document.onmousemove=function(e) {
var mousecoords = getMousePos(e);
var mouseX = mousecoords.x;
var mouseY = mousecoords.y;
var ellipse = document.getElementById('ellipse');
ellipse.setAttribute('cx', mouseX);
ellipse.setAttribute('cy', mouseY)
};
function getMousePos(e) {
return {x:e.clientX,y:e.clientY};
}
current(i want to have a larger blur than this, not just increase the ellipse radius)
increased blur value with visible borders
Thankful for any hints!
You need to increase your Filter Region.
By default it is <filter x="-10%" y="-10%" width="120%" height="120%">. But if you want to use a large blur, you will need to increase it, to cater for the way that the blur spreads the pixels out further.
Something like <filter id="filter" x="-75%" y="-75%" width="250%" Height="250%"> works for your example.
https://codepen.io/PaulLeBeau/pen/qGboxR
Let's say I have the following html context, which I don't have access to its creation :
<div id="outer">
<div id="chart-div">
<svg id="chart"></svg>
</div>
<div id="legend-div">
<svg id="legend"></svg>
</div>
</div>
What I'm trying to do is export this SVG to an image using canvg library, the problem here is that they are separated and so I get two canvas, and canvg library accepts a SVG definition string as input.
How can I modify the html elements above so that they are only one SVG? Using Javascript as this is a browser extension.
I have tried just switching the DIVs tags to SVG but it just broke everything and the SVG became blank
This is my solution to your problem: I make the outer div display:none, I'm creating another svg element (you may do it dynamicaly) end inside the #new svg I'm using the #chart and the #legend. I hope it helps.
svg{border:1px solid;}
#outer {display:none}
#outer div{position:absolute; width:500px;}
<div id="outer">
<div id="chart-div">
<svg id="chart" viewBox="0 0 300 150">
<circle stroke="gold" fill="none" cx="100" cy="75" stroke-width="40" stroke-dasharray="124.85" stroke-dashoffset="20" r="20" />
</svg>
</div>
<div id="legend-div">
<svg id="legend" viewBox="0 0 300 150">
<rect fill="skyBlue" x="200" y="100" width="80" height ="30" />
</svg>
</div>
</div>
<svg id="new" viewBox="0 0 300 150" width="500">
<use xlink:href="#chart" />
<use xlink:href="#legend" />
</svg>
This is a Javascript solution for merging two svg accessibles in the document through DOM manipulation.
var svgNS = "http://www.w3.org/2000/svg";
var outer = document.getElementById('outer');
// get chart content
var chart = document.getElementById('chart-div');
var chartSvg = chart.getElementsByTagName('svg')[0];
var chartContent = Array.from(chartSvg.childNodes);
// get legend content
var legend = document.getElementById('legend-div');
var legendSvg = legend.getElementsByTagName('svg')[0];
var legendContent = Array.from(legendSvg.childNodes);
// create a merged-div where we are going to merge the svgs
var merged = document.createElement('div');
merged.setAttribute('id', 'merged-div');
outer.appendChild(merged);
// createElementNS for svg
var mergedSvg = document.createElementNS(svgNS, 'svg');
mergedSvg.setAttribute('id', 'merged');
// keep the viewBox of the chart
mergedSvg.setAttribute('viewBox', chartSvg.getAttribute('viewBox'));
merged.appendChild(mergedSvg);
// adding the content of both svgs
for (let i = 0; i < chartContent.length; i++) {
mergedSvg.appendChild(chartContent[i]);
}
for (let i = 0; i < legendContent.length; i++) {
mergedSvg.appendChild(legendContent[i]);
}
// the unmerged svgs can be removed
chart.remove();
legend.remove();
<div id="outer">
<div id="chart-div">
<svg id="chart" viewBox="0 0 300 150">
<circle stroke="gold" fill="none" cx="100" cy="75" stroke-width="40" stroke-dasharray="124.85" stroke-dashoffset="20" r="20" />
</svg>
</div>
<div id="legend-div">
<svg id="legend" viewBox="0 0 300 150">
<rect fill="skyBlue" x="200" y="100" width="80" height ="30" />
</svg>
</div>
</div>
Resulting markup:
<div id="outer">
<div id="merged-div">
<svg id="merged" viewBox="0 0 300 150">
<circle stroke="gold" fill="none" cx="100" cy="75" stroke-width="40" stroke-dasharray="124.85" stroke-dashoffset="20" r="20"></circle>
<rect fill="skyBlue" x="200" y="100" width="80" height="30"></rect>
</svg>
</div>
</div>
I want to make a fairly large diagram using SVG (I have been using Snap.svg in JavaScript). I'd like to display a zoomable portion of the diagram in an element, and also a smaller version of the whole thing in a different element, in which the user can navigate.
One strategy is this:
Make two identical SVGs, except that they have different viewBoxes, and every time I change one of the svg elements, make an identical change to the other copy. The viewBox attributes cause each view to show the right part of the diagram.
Is that a good strategy? Seems fragile and wasteful to me. Is there some other, smarter approach? Do I really have to draw everything twice?
Hoping for "D'oh!"
Yes it's possible to have a master SVG and then "thumbnail" and/or "zoomed" versions of the same image that update automatically.
document.getElementById("but").addEventListener("click", function() {
var svg = document.getElementById("mainsvg");
var c = document.createElementNS("http://www.w3.org/2000/svg", "circle");
c.setAttribute("cx", 1000*Math.random());
c.setAttribute("cy", 1000*Math.random());
c.setAttribute("r", 50);
c.setAttribute("fill", "red");
svg.appendChild(c);
});
#main {
width: 400px;
height: 400px;
}
#thumb,
#zoom {
display: inline-block;
width: 80px;
height: 80px;
}
svg {
border: solid 1px grey;
}
<div id="main">
<svg id="mainsvg" viewBox="0 0 1000 1000">
<rect x="100" y="100" width="500" height="500" fill="green"
transform="rotate(10,350,350)"/>
<rect x="400" y="400" width="500" height="500" fill="orange"
transform="rotate(-10,650,650)"/>
</svg>
</div>
<div id="thumb">
<svg xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1000 1000">
<use xlink:href="#mainsvg" />
</svg>
</div>
<div id="zoom">
<svg xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1000 1000">
<use xlink:href="#mainsvg"
x="-500" y="-1200"
width="3000" height="3000" />
<!-- control the zoom and position with x, y, width and height -->
</svg>
</div>
<div>
<button id="but">Click me</button>
</div>