How to get intersection point(x,y) on SVG path - svg

I have the SVG path, and I want to get point's y coordinate.
SVG line:
<svg xmlns="http://www.w3.org/2000/svg" width="1920" height="340" viewBox="0 0 1920 340">
<path clip-rule="evenodd" fill="none" stroke="#B3B3B3" stroke-miterlimit="10" d="M-.5 60.3c88.8-42 232.2-89.1 355.2-34.4C549.3 112.5 640.3 163 720.3 192.4c80 29.4 278.9 116.9 451.3 61.4 172.3-55.4 328-112.5 568.3-23.2 100.1 43 151.4 65.8 179.6 79.3"/>
</svg>
I try this answer code, but it doesn't fit my need. I think path.getTotalLength() is not great for my question.
How can I do?
Update:
What my project want to do: JSFiddle
I have no idea how red points can match the line for each device size.

I suggest that you try paperjs. It’s a JS graphics library that uses HTML <canvas> elements as its graphics context(s). Here’s an example using your curve and a vertical line at mouse.x, which intersections shown.
let wave_path
let bounds
window.onload = function() {
//paper is a library for working with canvases; it's like a graphics library that works with a canvas
//as its GUI window. Each canvas context is its own PaperScope, and the global paper variable is a reference
//to the currently active PaperScope.
paper.setup(document.getElementById('paper-canvas'))
//import wave svg. however, if you integrate paperjs into your page, you might as well draw the curve directly onto the
//canvas with paper, rather than creating an invisible svg element that you then import.
let wave_svg = paper.project.importSVG(document.getElementById('svg-wave'))
wave_svg.visible = true // Turn off the effect of display:none
//fit wave into paper canvas
bounds = paper.view.bounds
wave_svg.fitBounds(bounds)
wave_path = wave_svg.children[0] //get contained path
wave_path.strokeColor = 'black'
wave_path.fillColor = null
//set event handlers on paper canvas
paper.view.onMouseMove = mouse_move
}
function mouse_move(event) {
let mouse_location = event.point
//clear canvas before redrawing
paper.project.clear()
//when creating a graphical object with paper, it automatically gets drawn to the canvas at the end of an event handler
//draw vertical line to intersect with
let line = new paper.Path(new paper.Point(mouse_location.x, 0), new paper.Point(mouse_location.x, bounds.height))
line.strokeColor = 'black'
//redraw wave path
new paper.Layer(wave_path)
//draw intersections
let intersections = line.getIntersections(wave_path)
for (intersection of intersections) {
let circle = new paper.Path.Circle(intersection.point, 5)
circle.strokeColor = 'red'
circle.fillColor = 'white'
}
}
<!DOCTYPE html>
<html>
<head>
<title>SVG Intersection Demo</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.2/paper-core.min.js"></script>
</head>
<body>
<svg id="svg-wave" style="display:none;" xmlns="http://www.w3.org/2000/svg" width="1920" height="340">
<path clip-rule="evenodd" fill="none" stroke="#B3B3B3" stroke-miterlimit="10" d="M-.5 60.3c88.8-42 232.2-89.1 355.2-34.4C549.3 112.5 640.3 163 720.3 192.4c80 29.4 278.9 116.9 451.3 61.4 172.3-55.4 328-112.5 568.3-23.2 100.1 43 151.4 65.8 179.6 79.3"/>
</svg>
<div style="text-align:center;">
<canvas id="paper-canvas" style="width:80%;"></canvas>
</div>
</body>
</html>

Related

Wrap text inside SVG rectangle. I want to resize the svg rectangle when user tries to enter the text continuously

I want something like below. Initially there will be a single word when user enters multiple words the size of the box increases. How can I achieve this? Anyone have any idea as to how to proceed on this ?
You can compute the length of the text using http://www.w3.org/TR/SVG/text.html#__svg__SVGTextContentElement__getComputedTextLength
and then you can resize the rect that depends on the textLength. You can call resize function when onkeydown event fires.
Here is an example of resizing the rect when the text length is changed by interval.
<svg id="svg" xmlns="http://www.w3.org/2000/svg" version="1.1">
<rect width="100" height="100" style="fill:rgb(255,255,255);stroke-width:3;stroke:rgb(0,0,0)" ></rect>
<text x="20" y="40">123</text>
</svg>
<script>
var textElement = document.getElementsByTagName('text')[0];
var rectElement = document.getElementsByTagName('rect')[0];
resizeRect();
setInterval(resizeRect, 1000);
function resizeRect(){
textElement.textContent += 0
var textLength = textElement.getComputedTextLength();
rectElement.setAttribute("width", 50 + textLength)
}
</script>
You can find a fiddle here: https://jsfiddle.net/0dvu604g/

SVG line in y=mx+c format

I want to draw an SVG line given the slope and the constant term or the y = mx + c format.
Is there a direct way to do this in SVG or an indirect way?
Thanks in advance.
The <line> tag only supports start and end attributes (x1, y1, x2, y2). As such, you'll need to pick x coordinates outside your canvas manually and use y2 = y1 + m(x2 - x1).
Edit
Looking through the spec, it's possible to transform individual elements as required:
<line ... transform="translate(x, y) rotate(theta)" />
Where theta is the angle of clockwise rotation in degrees.
So you could draw a long horizontal line from (-10000, 0) to (10000, 0), say, and then apply the appropriate rotation and translation to position it:
<line x1="-10000" y1="0" x2="10000" y2="0" transform="translate(150, 200) rotate(-30)" />
Will draw a (seemingly endless) line through (150, 200) of slope π/6 radians.
SVG is a good means of showing geometry. However, you must know some Javascript to build the dynamic display.
Below is an example that you can copy into your HTML file and view it in the browser.
It shows drawing a line from two points, then extending it to the end of the viewable svg viewing area (400x400), using the equation y=mx+c. Note, that the y-axis in svg begins at the upper-left, where y coordinates extends downward as the positive direction.
Drawing the line and extending it uses a bit of javascript.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Draw Line y=mx+b</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body style='padding:10px;font-family:arial'>
<center>
<h4>Draw Line y=mx+b</h4>
Red line is original points. Dashed extends to start(0)/end(400) of x axis
<div id="svgDiv" style='background-color:lightgreen;width:400px;height:400px;'>
<svg id="mySVG" width="400" height="400">
<line id="myLine2" stroke="black" stroke-width="3" stroke-dasharray="10 10" />
<line id="myLine1" stroke="red" stroke-width="5" />
</svg>
</div>
<br />SVG Source:<br />
<textarea id=svgSourceValue style='font-size:110%;font-family:lucida console;width:90%;height:200px'></textarea>
<br />Javascript:<br />
<textarea id=jsValue style='border-radius:26px;font-size:110%;font-weight:bold;color:midnightblue;padding:16px;background-color:beige;border-width:0px;font-size:100%;font-family:lucida console;width:90%;height:400px'></textarea>
</center>
<div id='browserDiv' style='padding:5px;position:absolute;top:5px;left:5px;background-color:gainsboro;'>OK in:IE11/CH32/FF23<br /></div>
<script id=myScript>
function DrawLineToAxis()
{
var x1=120
var y1=150
var x2=360
var y2=290
var m=(y2-y1)/(x2-x1)
myLine1.setAttribute("x1",x1)
myLine1.setAttribute("y1",y1)
myLine1.setAttribute("x2",x2)
myLine1.setAttribute("y2",y2)
/*
y=m*x-m*120+150
*/
//---extend to x=0 x=400
x1=0
y1=-m*120+150
x2=400
y2=m*x2-m*120+150
myLine2.setAttribute("x1",x1)
myLine2.setAttribute("y1",y1)
myLine2.setAttribute("x2",x2)
myLine2.setAttribute("y2",y2)
}
</script>
<script>
document.addEventListener("onload",init(),false)
function init()
{
DrawLineToAxis()
svgSourceValue.value=svgDiv.innerHTML
jsValue.value=myScript.text
}
</script>
</body>
</html>
The Following is my code :
function linemc(m,c){
var x,y,xm=[],ym=[];
x=-1;y=m*x+c;if(y>=0&&y<ysize) {xm.push(x);ym.push(y);}
x=xsize+1;y=m*x+c;if(y>=0&&y<ysize) {xm.push(x);ym.push(y);}
y=-1;x=(y-c)/m;if(x>=0&&x<xsize+1) {xm.push(x);ym.push(y);}
y=ysize+1;x=(y-c)/m;if(x>=0&&x<xsize+1) {xm.push(x);ym.push(y);}
return new line(xm[0],ym[0],xm[1],ym[1]);
}
where new line just creates a DOM node for line by accepting four arguments
and xsize and ysize is size of canvas

SVG (1.1) : how to 'link-to' or 'centre' on a shape in a browser?

Is there is browser-independant way getting the browser to centre on a particular shape (by 'id' attribute) ?
I have tried using xlinks wrapped around shapes like this:
<a xlink:href="#node24"> .... </a>
I have reasonably busy (100+ shapes) directed graph diagrams (generated from dot): and when I load them up in Chrome , more often than not, the intial screen is just blank - forcing the user to use scrollbars to find the diagram at all.
I'm afraid I don't have any good news for you.
For stand-alone SVG documents, you can manipulate the part of an SVG displayed when following a link by linking to a <view> element (distinct from, but making use of, the SVG "viewBox" attribute). The view element specifies the viewBox to use and possibly some other parameters, and the graphic will be displayed with those parameters instead of the default ones.
Example code:
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 100 100"
preserveAspectRatio="xMidYMin meet" >
<circle cx ="50" r="40"/>
<view id="panUp" viewBox="0 -50 100 100" />
<view id="zoomIn" viewBox="25 25 50 50" />
</svg>
If you linked to the file as a whole it would show you an image with half a circle centered at the top of the screen.
If, however, you linked to it like http://example.com/sample.svg#panUp, the circle would be the same size but centered on screen. If you linked to http://example.com/sample.svg#zoomIn, you'd only see the bottom edge of a circle that is twice as big.
(I don't have anywhere to host the file that can serve up raw SVG files, but this CodePen uses data URI to show the effects, although the data URI fragment identifiers doesn't seem to work in Firefox.)
You are supposed to be able to even specify the desired viewBox, transforms, or other attributes as part of the URL fragment (like http://example.com/sample.svg#myView(viewBox(0,0,200,200))), but I don't think that's widely implemented -- it had no effect on either Firefox or Chrome.
And even <view> fragments don't seem to work when the SVG is embedded within an HTML document. So unless your SVG is stand-alone, creating a view for each element (or one view that your dynamically change to match the clicked element), isn't going to be worth the trouble.
So what does work?
The default behaviour, when linking to a fragment (element id) that is not a <view> is to display the nearest ancestor <svg> element that contains that element ("nearest ancestor" because an SVG can contain nested <svg> tags). So if your document has a natural structure to it, you could replace some <g> elements with <svg> with a specified x,y,height and width parameter, and then linking to an element within that sub-graphic would show that view. That should work even when the SVG is embedded within a larger HTML document. But if you've got hundreds of elements moving around, it's probably not a practical solution.
Which leaves #Ian's solution of programmatically manipulating the main SVG viewBox. If you don't want to zoom in, just pan, leave the width and height as the full size of your visualization, and just change the x and y offsets. Something like:
function centerViewOnElement( el ) {
var bbox = el.getBBox()
var elCenterX = bbox.x + bbox.width/2,
elCenterY = bbox.y + bbox.height/2;
svg.setAttribute("viewBox", [(elCenterX - width/2),
(elCenterY - height/2),
width,
height
].join(" ") );
//assuming you've got the svg, width and height already saved in variables...
}
Thought I would do a simpler example, as this feels quite useful in general...with a jsfiddle here
<svg id="mySvg">
<circle id="myCirc" cx="20" cy="20" r="20"/>
<rect id="myRect" x="50" y="50" width="50" height="50"/>
</svg>
var mySvg = document.getElementById("mySvg");
function getNewViewbox( el ) {
var bbox = el.getBBox();
return newViewbox = bbox.x + " " + bbox.y + " " + bbox.width + " " + bbox.height;
}
function focusElement( ev ) {
ev.stopPropagation();
mySvg.setAttribute("viewBox", getNewViewbox( ev.target ) );
}
//click on any element, or even the svg paper
document.getElementById("mySvg").addEventListener("click", focusElement);

Half filled circle with d3.js

I am trying to create a half filled circle with d3.js to be like this.
I didn't find any example of how to do it.
How can this be done with d3.js?
Yes, you can do that with an SVG gradient. All you have to do is define it and then use it as fill for the circle.
var grad = svg.append("defs").append("linearGradient").attr("id", "grad")
.attr("x1", "0%").attr("x2", "0%").attr("y1", "100%").attr("y2", "0%");
grad.append("stop").attr("offset", "50%").style("stop-color", "lightblue");
grad.append("stop").attr("offset", "50%").style("stop-color", "white");
svg.append("circle")
.attr("fill", "url(#grad)");
JSfiddle here.
You may not even require d3 for this simple task. You may use this simple technique, Using Clippath on a circle, I have written it in details in my blog http://anilmaharjan.com.np/blog/2013/11/create-filled-circle-to-visualize-data-using-svg
Use Two circles one above another in a tag.
Fill one with the color you wish and another with white or may be your background color just to make it look like its empty in there.
Then clip the later one using with rectangle in it, assign radius few pixel less than the earlier circle.
Place clip path at the top left .. assign width equal to the diameter of the circle and height will be defined by your data.
The data will act reversible to the filling so you may subtract the actual data from your max. EG: if data is 20/100 do 100-20 so u ll get 80 in this way the empty part will be 80 and filled will be 20.
You may switch between height or width to switch between vertical or horizontal filling axis.
The HTML should look like this.
<svg height="200"> <a transform="translate(100,100)">
<g>
<circle fill="#f60" r="50"></circle>
</g>
<g>
<clippath id="g-clip">
<rect height="50" id="g-clip-rect" width="100" x="-50" y="-50">
</rect>
</clippath>
<circle clip-path="url(#g-clip)" fill="#fff" r="47"></circle>
</g>
</a>
</svg>
I have created a jsfiddle to illustrate this at: http://jsfiddle.net/neqeT/2/
create a div having id name id_cirlce and paste this code inside script tag
<div id="id_circle"></div>
<script>
var svg = d3.select("#id_circle")
.append("svg")
.attr("width",250)
.attr("height",250);
var grad = svg.append("defs")
.append("linearGradient").attr("id", "grad")
.attr("x1", "0%").attr("x2", "0%").attr("y1", "100%").attr("y2", "0%");
grad.append("stop").attr("offset", "50%").style("stop-color", "lightblue");
grad.append("stop").attr("offset", "50%").style("stop-color", "white");
svg.append("circle")
.attr("r",50)
.attr("cx",60)
.attr("cy",60)
.style("stroke","black")
.style("fill","url(#grad)");
</script>

How do I scale an SVG that has some complex path definitions?

I have an SVG whose code is like what follows:
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="150px" height="150px" preserveAspectRatio="xMinYMin meet" viewBox="0 0 150 155" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g id="g11" transform="matrix(1.25,0,0,-1.25,-100.0791,954.14501)">
<g id="g3186" transform="translate(6.3490095,-13.703287)">
<path d="..."></path>
<path d="..."></path>
<path d="..."></path>
<path d="..."></path>
</g>
</g>
</svg>
The "original" dimensions of the SVG generate an image is that is 300px by 310px. I want to scale the image as a whole, let's say by 50%. I've tried a variety of ways to scale the image, but the best I've accomplished is to simply clip or "crop" the original image to a region that is 50% of the original dimensions.
Using preserveAspectRatio, setting the viewbox, trying to use transform="scale(0.5"), etc. have not worked. All I want to do is scale the original dimensions by 50%.
transform="scale(0.5)" should definitely work. You're probably doing something else wrong.
Try wrapping g11 in another g with transform="scale(0.5)" and remove the preserveAspectRatio and viewBox
Here is an example of how I'm doing it
<!DOCTYPE html>
<meta charset="utf-8">
<html>
<head>
<script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<!-- this came from Open Clip Art http://openclipart.org/tags/svg
I just took the bits inside the g tag and gave them an id -->
<svg xmlns:xlink="http://www.w3.org/1999/xlink" >
<defs>
<g id="factory2">
<g id="layer1" transform="translate(-190.12 -497.04)">
<path id="path3019" d="m260.18 638.49c-12.375-0.3028-33.202-0.51166- 46.283-0.46413l-23.783 0.0864 0.54057-2.5363c0.29731-1.3949 0.72336-10.785 0.94678-20.867 0.22343-10.082 0.61582-18.541 0.87198-18.797 0.6619-0.66189 5.5186 2.3677 12.539 7.8218l6.0422 4.6941 2.6179-4.6941c1.4398-2.5818 3.2323-5.8073 3.9833-7.1678 0.75098-1.3605 1.8059-2.6205 2.3442-2.7999 0.53833-0.17944 3.4031 1.6087 6.3662 3.9737 14.183 11.32 13.44 10.876 15.062 9.0051 0.81257-0.93784 2.5493-4.5177 3.8593-7.9552 1.3101-3.4375 2.6092-6.2494 2.887-6.2486 0.2778 0.00077 4.2211 3.6008 8.7629 8s8.6861 7.9986 9.2095 7.9986c1.661 0 5.5112-4.8335 7.5569-9.4868 1.0867-2.4718 2.2704-4.4985 2.6305-4.5037 0.36009-0.005 4.9112 4.2194 10.114 9.3881l9.4588 9.3976-0.36231 12.268c-0.19928 6.7473-0.57659 12.482-0.83847 12.744-0.71531 0.7153-9.6348 0.75231-34.526 0.14325zm-53.812-3.6334c-0.72187-0.28887-1.5844-0.25335-1.9167 0.0789-0.33229 0.33229 0.25834 0.56864 1.3125 0.52522 1.165-0.048 1.4019-0.28494 0.60417-0.60416zm52.618-13.034c0.27612-3.8066 0.0559-4.2797-2.2628-4.8616-1.4128-0.35458-4.5879-0.34189-7.0559 0.0282-4.4272 0.66389-4.4872 0.72466-4.4872 4.5421 0 4.3193 0.69024 4.6694 9 4.5649l4.5-0.0566 0.30588-4.2169zm-34.556 2.4532c-0.1375-0.40496-0.25-2.3113-0.25-4.2363v-3.5h-13v7.8954l5.25 0.37628c6.7165 0.48138 8.3091 0.37478 8-0.53546zm17.25-3.2363v-4l-6.75-0.29569-6.75-0.29569v3.879c0 2.1335 0.31903 4.1981 0.70896 4.588 0.38993 0.38992 3.4274 0.57742 6.75 0.41666l6.041-0.29229v-4zm32.5 0.5v-4h-12v8h12v-4zm16.267 0.75c0.27194-1.7875 0.29645-3.5875 0.0545-4-0.24197-0.4125-3.3384-0.75-6.8809-0.75h-6.441v8h12.773l0.49443-3.25zm-82.457-1.5-0.31037-3.75h-5.7837c-4.0517 0-6.0685-0.44918-6.7347-1.5-0.67184-1.0597-0.95549 0.0411-0.96631 3.75l-0.0153 5.25h14.121l-0.31036-3.75zm13.69-19.25c-0.33992-0.55-1.068-1-1.618-1s-0.72189 0.45-0.38197 1 1.068 1 1.618 1 0.72189-0.45 0.38197-1zm-13.665-0.75c-1.26-1.4949-1.4649-9.1853-1.405-52.75l0.0701-51 3.3219 0.15527c1.8271 0.0854 3.5772 0.4105 3.8891 0.72245 0.31195 0.31195 0.73146 10.142 0.93225 21.845 0.2008 11.703 0.79871 30.278 1.3287 41.278 1.2527 26.001 0.96626 31.906-1.8072 37.25-2.3841 4.5942-4.0201 5.2403-6.33 2.5zm27.665-1.9051c-1.1986-2.8686-1.388-5.8273-0.88023-13.75 0.35583-5.5522 0.69953-20.895 0.76378-34.095 0.12983-26.675 0.48916-28.507 4.9502-25.245 1.5369 1.1238 1.7803 4.6464 2.3358 33.807 0.55628 29.202 0.44258 33.086-1.1049 37.745-2.0404 6.1432-3.9393 6.6248-6.0646 1.5383z"/>
</g>
</g>
</defs>
</svg>
<script type="text/javascript">
var svg = d3.select("svg") .append("g");
var nodes = [{ name: "1", type: 'factory2' }];
var node = svg.selectAll("node")
.data(nodes)
.enter()
.append("g")
.attr({
dx:50, dy:50, width:50, height:50,
// here's the magic
transform:"scale(0.5)"
});
node.append("use")
.attr({
"xlink:href": "#factory2"
});
</script>
</body>
</html>

Resources