Transform SVG path to canvas html5 - javascript

Here I have some code that make svg path:
http://jsbin.com/gazecasagi/1/edit?html,output
<html>
<head>
<script>
function draw() {
var polygons = [[{"X":22,"Y":59.45},{"X":136,"Y":66},{"X":170,"Y":99},{"X":171,"Y":114},{"X":183,"Y":125},{"X":218,"Y":144},{"X":218,"Y":165},{"X":226,"Y":193},{"X":254,"Y":195},{"X":283,"Y":195},{"X":292,"Y":202},{"X":325,"Y":213},{"X":341,"Y":134},{"X":397,"Y":245},{"X":417,"Y":548}]];
var scale = 1000;
reverse_copy(polygons);
polygons = scaleup(polygons, scale);
var cpr = new ClipperLib.Clipper();
var delta = 20;
var joinType = ClipperLib.JoinType.jtRound;
var miterLimit = 2;
var AutoFix = true;
var svg, offsetted_polygon,
cont = document.getElementById('svgcontainer');
offsetted_polygon = cpr.OffsetPolygons(polygons, delta * scale, joinType, miterLimit, AutoFix);
//console.log(JSON.stringify(offsetted_polygon));
// Draw red offset polygon
svg = '<svg style="margin-top:10px;margin-right:10px;margin-bottom:10px;background-color:#dddddd" width="800" height="600">';
svg += '<path stroke="red" fill="red" stroke-width="2" stroke-opacity="0.6" fill-opacity="0.2" d="' + polys2path(offsetted_polygon, scale) + '"/>';
//Draw blue polyline
svg += '<path stroke="blue" stroke-width="1" d="' + polys2path(polygons, scale) + '"/>';
svg += '</svg>';
cont.innerHTML += svg;
}
// helper function to scale up polygon coordinates
function scaleup(poly, scale) {
var i, j;
if (!scale) scale = 1;
for(i = 0; i < poly.length; i++) {
for(j = 0; j < poly[i].length; j++) {
poly[i][j].X *= scale;
poly[i][j].Y *= scale;
}
}
return poly;
}
// converts polygons to SVG path string
function polys2path (poly, scale) {
var path = "", i, j;
if (!scale) scale = 1;
for(i = 0; i < poly.length; i++) {
for(j = 0; j < poly[i].length; j++){
if (!j) path += "M";
else path += "L";
path += (poly[i][j].X / scale) + ", " + (poly[i][j].Y / scale);
}
path += "Z";
}
return path;
}
function reverse_copy(poly) {
// Make reverse copy of polygons = convert polyline to a 'flat' polygon ...
var k, klen = poly.length, len, j;
for (k = 0; k < klen; k++) {
len = poly[k].length;
poly[k].length = len * 2 - 2;
for (j = 1; j <= len - 2; j++) {
poly[k][len - 1 + j] = {
X: poly[k][len - 1 - j].X,
Y: poly[k][len - 1 - j].Y
}
}
}
}
</script>
</head>
<body onload="draw()">
<div id="svgcontainer"></div>
</body>
</html>
Is there a simple way to tranform SVG path to Canvas. I need this becouse I need to show this example on mobile devices and Canvas have a better performance than canvas on mobile devices.
This code I need to transform to CANVAS:
// Draw red offset polygon
svg = '<svg style="margin-top:10px;margin-right:10px;margin-bottom:10px;background-color:#dddddd" width="800" height="600">';
svg += '<path stroke="red" fill="red" stroke-width="2" stroke-opacity="0.6" fill-opacity="0.2" d="' + polys2path(offsetted_polygon, scale) + '"/>';
//Draw blue polyline
svg += '<path stroke="blue" stroke-width="1" d="' + polys2path(polygons, scale) + '"/>';
svg += '</svg>';
How I can transform SVG path to simple CANVAS path?

You can use canvg library to convert svg to canvas.
You should include all its necessary js files to your page and then use it something like this:
canvg(document.getElementById('canvasElement'), '<svg>...</svg>')

Of course, the fastest way to present your complex polyline is to convert it into an image.
A fully optimized canvas version of your complex polyline would involve a canvas path:
Create a closed path of your red outline using lines with Bezier curves for joins. You can use context.lineTo and context.quadraticCurveTo + context.bezierCurveTo to define the path. The resulting path is commonly called a spline.
Stroke the path with red.
Fill the path with pink.
Draw the blue line.
This is not hard to do but does involve some trigonometry (mainly finding points tangent to the vectors of your polyline).
Here's an alternative that uses shadowing to mimic your complex SVG polyline:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var pts = [{x:22,y:59.45},{x:136,y:66},{x:170,y:99},{x:171,y:114},{x:183,y:125},{x:218,y:144},{x:218,y:165},{x:226,y:193},{x:254,y:195},{x:283,y:195},{x:292,y:202},{x:325,y:213},{x:341,y:134},{x:397,y:245},{x:417,y:548}];
mimicSvg(pts);
function mimicSvg(pts){
// make caps & joins round
ctx.lineCap='round';
ctx.lineJoin='round';
// draw the outside line with red shadow
ctx.shadowColor='red';
ctx.shadowBlur='2';
ctx.lineWidth=25;
// draw multiple times to darken shadow
drawPolyline(pts);
drawPolyline(pts);
drawPolyline(pts);
// stop shadowing
ctx.shadowColor='transparent';
// refill the outside line with pink
ctx.strokeStyle='pink';
drawPolyline(pts);
// draw the inside line
ctx.lineWidth=2;
ctx.strokeStyle='blue';
drawPolyline(pts);
}
function drawPolyline(pts){
ctx.beginPath();
ctx.moveTo(pts[0].x,pts[0].y);
for(var i=1;i<pts.length;i++){
ctx.lineTo(pts[i].x,pts[i].y);
}
ctx.stroke();
}
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=500 height=600></canvas>

Related

Detect intersection between two svg shape [duplicate]

I have designed a Duval's Triangle ( a diagnostic tool ), on SVG,
cosisting of Segments (closed Paths) of different colors.
The diagnostic result will be a coordinate.
Need to detect the resultant coordinate is in which Closed Path ?
Like in the following case the diagnostic result is a RED DOT.
Need to detect the close path i.e in this case : D2
You have several options:
SVG 2 has the isPointInFill() method. Which you can call on each shape to see if a point is within the fill of a path. However I believe only Chrome and Opera have implemented this new feature of SVG2.
https://developer.mozilla.org/en-US/docs/Web/API/SVGGeometryElement/isPointInFill
You can draw your SVG to a Canvas and use the isPointInPath() method.
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/isPointInPath
You could draw the SVG to a Canvas and use the getImageData() method to get the colour of that particular pixel.
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/getImageData
And of course you can also do it numerically by calculating it yourself in some way.
This is using Paul LeBeau's answer.
1.
First a demo where you check if the point is in a certain path, the #c path in this case.
Please read the comments in my code.
// creating a new SVG point
let point = svg.createSVGPoint()
point.x = 300;
point.y = 300;
//checking if the point is in the path c
console.log(c.isPointInFill(point));
svg{border:1px solid}
<svg id="svg" viewBox="0 0 606.731 526.504" width="200" >
<polygon id="a" fill="#D9D1E0" stroke="#020202" stroke-miterlimit="10" points="300.862,1.001 0.862,526.001 605.862,526.001 "/>
<polygon id="b" fill="#926BB5" stroke="#020202" stroke-miterlimit="10" points="289.576,19.681 442.34,283.546 411.092,343.437
515.705,526.001 428.453,525.716 337.314,365.138 385.054,280.945 262.668,66.555 "/>
<polygon id="c" fill="#8ED5ED" stroke="#020202" stroke-miterlimit="10" points="334.4,193.005 384.186,280.946 337.315,364.272
428.453,525.716 142.019,525.716 "/>
<circle cx="300" cy="300" r="5" fill="red" />
</svg>
2.
A second demo where I'm drawing the polygons on a canvas and using the method isPointInPath() of the context:
Please read the comments in my code.
let ctx = canv.getContext("2d");
canv.width = 606.731;
canv.height = 526.504;
// the main triangle
let duval = [300.862, 1.001, 0.862, 526.001, 605.862, 526.001];
// the arrays of points for the 2 polygons inside the main triangle
let rys = [
[
289.576,
19.681,
442.34,
283.546,
411.092,
343.437,
515.705,
526.001,
428.453,
525.716,
337.314,
365.138,
385.054,
280.945,
262.668,
66.555
],
[
334.4,
193.005,
384.186,
280.946,
337.315,
364.272,
428.453,
525.716,
142.019,
525.716
]
];
// drawing the polygons
drawPoly(duval, "#D9D1E0");
drawPoly(rys[0], "#926BB5");
drawPoly(rys[1], "#8ED5ED");
// the point to check
let p = { x: 300, y: 300 };
drawPoint(p);
// looping through the array of shapes to check if the point is in path
for (let i = 0; i < rys.length; i++) {
// draw again the polygon without stroking or filling
drawPoly(rys[i]);
//chect if the point is in path
if (ctx.isPointInPath(p.x, p.y)) {
// do something
console.log(i);
// if found break the loop
break;
}
}
// a function to draw a polygon from an array
function drawPoly(ry, color) {
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(ry[0], ry[1]);
for (let i = 2; i < ry.length; i += 2) {
ctx.lineTo(ry[i], ry[i + 1]);
}
ctx.closePath();
if (color) {
ctx.fill();
ctx.stroke();
}
}
function drawPoint(p) {
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(p.x, p.y, 5, 0, 2 * Math.PI);
ctx.fill();
}
canvas{border:1px solid}
<canvas id="canv"></canvas>
3
This time using the getImageData() method to get the colour of the pixel at the point.
The code is similar to the one in the previous example
let ctx = canv.getContext("2d");
canv.width = 606.731;
canv.height = 526.504;
let duval = [300.862, 1.001, 0.862, 526.001, 605.862, 526.001];
let rys = [
[
289.576,
19.681,
442.34,
283.546,
411.092,
343.437,
515.705,
526.001,
428.453,
525.716,
337.314,
365.138,
385.054,
280.945,
262.668,
66.555
],
[
334.4,
193.005,
384.186,
280.946,
337.315,
364.272,
428.453,
525.716,
142.019,
525.716
]
];
drawPoly(duval, "#D9D1E0");
drawPoly(rys[0], "#926BB5");
drawPoly(rys[1], "#8ED5ED");
function drawPoly(ry, color) {
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(ry[0], ry[1]);
for (let i = 2; i < ry.length; i += 2) {
ctx.lineTo(ry[i], ry[i + 1]);
}
ctx.closePath();
if (color) {
ctx.fill();
ctx.stroke();
}
}
// HERE BEGINS THE IMPORTANT PART
let imgData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
let p = { x: 300, y: 300 };
// mark the point with an empty circle
ctx.beginPath();
ctx.arc(p.x,p.y,5,0,2*Math.PI);
ctx.stroke();
// the index of the point p in the imgData.data array
let index = (p.y*imgData.width + p.x)*4;
//the red,green and blue components of the color of the pixel at the index
let r = imgData.data[index];
let g = imgData.data[index + 1];
let b = imgData.data[index + 2];
//test the color
test.style.background = `rgb(${r},${g},${b})`;
canvas{border:1px solid}
#test{width:50px; height:50px; border:1px solid;}
<canvas id="canv"></canvas>
<div id="test"></div>

How to determine if a coordinate is inside SVG Close Path?

I have designed a Duval's Triangle ( a diagnostic tool ), on SVG,
cosisting of Segments (closed Paths) of different colors.
The diagnostic result will be a coordinate.
Need to detect the resultant coordinate is in which Closed Path ?
Like in the following case the diagnostic result is a RED DOT.
Need to detect the close path i.e in this case : D2
You have several options:
SVG 2 has the isPointInFill() method. Which you can call on each shape to see if a point is within the fill of a path. However I believe only Chrome and Opera have implemented this new feature of SVG2.
https://developer.mozilla.org/en-US/docs/Web/API/SVGGeometryElement/isPointInFill
You can draw your SVG to a Canvas and use the isPointInPath() method.
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/isPointInPath
You could draw the SVG to a Canvas and use the getImageData() method to get the colour of that particular pixel.
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/getImageData
And of course you can also do it numerically by calculating it yourself in some way.
This is using Paul LeBeau's answer.
1.
First a demo where you check if the point is in a certain path, the #c path in this case.
Please read the comments in my code.
// creating a new SVG point
let point = svg.createSVGPoint()
point.x = 300;
point.y = 300;
//checking if the point is in the path c
console.log(c.isPointInFill(point));
svg{border:1px solid}
<svg id="svg" viewBox="0 0 606.731 526.504" width="200" >
<polygon id="a" fill="#D9D1E0" stroke="#020202" stroke-miterlimit="10" points="300.862,1.001 0.862,526.001 605.862,526.001 "/>
<polygon id="b" fill="#926BB5" stroke="#020202" stroke-miterlimit="10" points="289.576,19.681 442.34,283.546 411.092,343.437
515.705,526.001 428.453,525.716 337.314,365.138 385.054,280.945 262.668,66.555 "/>
<polygon id="c" fill="#8ED5ED" stroke="#020202" stroke-miterlimit="10" points="334.4,193.005 384.186,280.946 337.315,364.272
428.453,525.716 142.019,525.716 "/>
<circle cx="300" cy="300" r="5" fill="red" />
</svg>
2.
A second demo where I'm drawing the polygons on a canvas and using the method isPointInPath() of the context:
Please read the comments in my code.
let ctx = canv.getContext("2d");
canv.width = 606.731;
canv.height = 526.504;
// the main triangle
let duval = [300.862, 1.001, 0.862, 526.001, 605.862, 526.001];
// the arrays of points for the 2 polygons inside the main triangle
let rys = [
[
289.576,
19.681,
442.34,
283.546,
411.092,
343.437,
515.705,
526.001,
428.453,
525.716,
337.314,
365.138,
385.054,
280.945,
262.668,
66.555
],
[
334.4,
193.005,
384.186,
280.946,
337.315,
364.272,
428.453,
525.716,
142.019,
525.716
]
];
// drawing the polygons
drawPoly(duval, "#D9D1E0");
drawPoly(rys[0], "#926BB5");
drawPoly(rys[1], "#8ED5ED");
// the point to check
let p = { x: 300, y: 300 };
drawPoint(p);
// looping through the array of shapes to check if the point is in path
for (let i = 0; i < rys.length; i++) {
// draw again the polygon without stroking or filling
drawPoly(rys[i]);
//chect if the point is in path
if (ctx.isPointInPath(p.x, p.y)) {
// do something
console.log(i);
// if found break the loop
break;
}
}
// a function to draw a polygon from an array
function drawPoly(ry, color) {
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(ry[0], ry[1]);
for (let i = 2; i < ry.length; i += 2) {
ctx.lineTo(ry[i], ry[i + 1]);
}
ctx.closePath();
if (color) {
ctx.fill();
ctx.stroke();
}
}
function drawPoint(p) {
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(p.x, p.y, 5, 0, 2 * Math.PI);
ctx.fill();
}
canvas{border:1px solid}
<canvas id="canv"></canvas>
3
This time using the getImageData() method to get the colour of the pixel at the point.
The code is similar to the one in the previous example
let ctx = canv.getContext("2d");
canv.width = 606.731;
canv.height = 526.504;
let duval = [300.862, 1.001, 0.862, 526.001, 605.862, 526.001];
let rys = [
[
289.576,
19.681,
442.34,
283.546,
411.092,
343.437,
515.705,
526.001,
428.453,
525.716,
337.314,
365.138,
385.054,
280.945,
262.668,
66.555
],
[
334.4,
193.005,
384.186,
280.946,
337.315,
364.272,
428.453,
525.716,
142.019,
525.716
]
];
drawPoly(duval, "#D9D1E0");
drawPoly(rys[0], "#926BB5");
drawPoly(rys[1], "#8ED5ED");
function drawPoly(ry, color) {
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(ry[0], ry[1]);
for (let i = 2; i < ry.length; i += 2) {
ctx.lineTo(ry[i], ry[i + 1]);
}
ctx.closePath();
if (color) {
ctx.fill();
ctx.stroke();
}
}
// HERE BEGINS THE IMPORTANT PART
let imgData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
let p = { x: 300, y: 300 };
// mark the point with an empty circle
ctx.beginPath();
ctx.arc(p.x,p.y,5,0,2*Math.PI);
ctx.stroke();
// the index of the point p in the imgData.data array
let index = (p.y*imgData.width + p.x)*4;
//the red,green and blue components of the color of the pixel at the index
let r = imgData.data[index];
let g = imgData.data[index + 1];
let b = imgData.data[index + 2];
//test the color
test.style.background = `rgb(${r},${g},${b})`;
canvas{border:1px solid}
#test{width:50px; height:50px; border:1px solid;}
<canvas id="canv"></canvas>
<div id="test"></div>

Calc/Place horizontal labels around SVG Donut Graph (NO LIBRARIES)

I am looking to add 'floating' labels around an SVG Donut Graph, as seen below, using only SVG elements and vanilla JS.
The corresponding lines are a plus, but not necessary.
Building of the dynamic donut/sectors is complete and I have it ported/working correctly within an Angular application. I am using vanilla JS w/Jquery here for ease.
I have scoured StackOverflow and Google to their depths but cannot locate a Question & Answer which provides a compatible solution using an algorithm/code to calculate desired placement. 90% of similar questions reference d3.js while the others reference a different charting library.
Not sure where to begin as I don't think the information I have on the segments is enough to calc placement within the viewBox.
I do know the circumference of the drawn circles are each 100 with a radius of 15.91549430918954. stroke-dasharray and stroke-dashoffset are calculated and set to each segment to build out the colored segments appropriately.
Is it possible to use what data I have to figure this out? Do I need a little more? How would I translate the calculation to x/y coordinates for the viewBox?
Thanks.
$(document).ready(function() {
let data = [500,100,350,600];
let dataTotal = 0;
for (let i = 0; i < data.length; i++) {
dataTotal += data[i];
}
let colors = ['#ce4b99', '#4286f4', '#62f441', '#952ec1'];
let labels = ['A', 'B', 'C', 'D'];
let total = 0;
for (let i = 0; i < data.length; i++) {
let dataPercent = data[i] / dataTotal;
let dataVal = 100 * dataPercent;
var chart = document.getElementById("chart");
const group = document.createElementNS('http://www.w3.org/2000/svg', 'g');
const node = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
chart.appendChild(group);
group.appendChild(node);
group.appendChild(label);
label.textContent = labels[i];
label.setAttribute('class', 'data-label');
label.setAttribute('x', '20%');
label.setAttribute('y', '20%');
node.setAttribute('stroke-dasharray',`${dataVal} ${100 - dataVal}`);
node.setAttribute('class','donut-segment');
node.setAttribute('fill','transparent');
node.setAttribute('stroke-dashoffset', getOffset(total, i))
total += dataVal;
node.setAttribute('stroke', colors[i]);
node.setAttribute('stroke-width','3');
node.setAttribute('r','15.91549430918954');
node.setAttribute('cx','42');
node.setAttribute('cy','42');
}
});
function getOffset(total, i) {
if (i === 0) return 25;
return ((100 - total) + 25);
}
.chart-contain {
width: 50%;
margin 0 auto;
}
.data-label {
font-size: 4px;
}
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script>
<div class='chart-contain'>
<svg id='chart' width="100%" height="100%" viewBox="0 0 84 84" class="donut">
<circle class="donut-hole" cx="42" cy="42" r="15.91549430918954" fill="transparent"></circle>
<circle class="donut-ring" cx="42" cy="42" r="15.91549430918954" fill="transparent" stroke="#d2d3d4" stroke-width="3">
</circle>
</svg>
</div>
You may not like my answer because I couldn't use your code. I needed to organise the data differently. However I keep the general idea: I'm using stroke-dasharray and stroke-dashoffset. I would have used paths instead of strokes.
I'm using a radius of 20, but you may change it back. You can also change it to anything else.
In order to draw the text I calculated the starting angle and the ending angle of your arcs. Then I calculated the middle angle. Once I know the middle angle I can draw the text and the lines.
I'm putting the arcs in the #chart group, the text in the #text group and the lines in the #lines group.
const SVG_NS = 'http://www.w3.org/2000/svg';
const XLINK = 'http://www.w3.org/1999/xlink';
let r = 20,cx=42,cy=42;// the attributes for the circle
let text_r = r + 10; // the radius for the text
let perimeter = 2*r*Math.PI;//the perimeter of the circle
donut.setAttributeNS(null, "r", r);
donut.setAttributeNS(null, "cx", cx);
donut.setAttributeNS(null, "cy", cy);
let data = [
{
value:500,
stroke:'#ce4b99',
label: 'A'
},
{
value:100,
stroke:'#4286f4',
label: 'B'
},
{
value:350,
stroke:'#62f441',
label: 'C'
},
{
value:600,
stroke:'#952ec1',
label: 'D'
}
]
let total = 0;
data.map(d =>{
d.temp = total;
total += d.value;
})
data.map(d =>{
d.offset = map(d.temp,0,total,0, perimeter)
d.real_value = map(d.value, 0, total, 0, perimeter);
d.dashArray = `${d.real_value},${perimeter}`;
/// angles
let angleStart = -d.offset/r;
let angleEnd = (d.offset + d.real_value)/r;
d.angleMiddle = (angleEnd - angleStart)/2;
// text
let t = {}
t.props ={
x : cx + text_r*Math.cos(d.angleMiddle),
y : cy + text_r*Math.sin(d.angleMiddle),
}
t.textContent = d.label;
d.text_point = t;
//line
let l = {
x1 : cx + r*Math.cos(d.angleMiddle),
y1 : cy + r*Math.sin(d.angleMiddle),
x2 : cx + .9*text_r*Math.cos(d.angleMiddle),
y2 : cy + .9*text_r*Math.sin(d.angleMiddle),
}
d.line = l;
})
data.map(d=>{// create a new use element
d.use = document.createElementNS(SVG_NS, 'use');
d.use.setAttributeNS(XLINK, 'xlink:href', '#donut');
d.use.setAttributeNS(null, 'stroke', d.stroke);
d.use.setAttributeNS(null, 'stroke-dasharray', d.dashArray);
d.use.setAttributeNS(null, 'stroke-dashoffset', -d.offset);
chart.appendChild(d.use);
drawLine(d.line, lines);
drawText(d.text_point, text);
})
// helpers
function drawLine(o, parent) {
var line = document.createElementNS(SVG_NS, 'line');
for (var name in o) {
if (o.hasOwnProperty(name)) {
line.setAttributeNS(null, name, o[name]);
}
}
parent.appendChild(line);
return line;
}
function drawText(o, parent) {
var text = document.createElementNS(SVG_NS, 'text');
for (var name in o.props) {
if (o.props.hasOwnProperty(name)) {
text.setAttributeNS(null, name, o.props[name]);
}
text.textContent = o.textContent;
}
parent.appendChild(text);
return text;
}
function map(n, a, b, _a, _b) {
let d = b - a;
let _d = _b - _a;
let u = _d / d;
return _a + n * u;
}
svg{border:1px solid;}
#donut{fill:none; stroke-width:5px;}
text{dominant-baseline:central;text-anchor:middle;font-size: 4px;}
line{stroke:black;stroke-width:.1px}
<div class='chart-contain'>
<svg viewBox="0 0 84 84" width="250" class="donut">
<defs>
<circle id="donut" cx="42" cy="42" r="20" ></circle>
</defs>
<g id='text'></g>
<g id='lines'></g>
<g id='chart'></g>
</svg>
</div>
In order to get it exactly like yours you may want to rotate the whole chart -Math.PI/2 and then rotate the text back.

how to know if any x or y point is on svg path

i have a svg path and i want to know if my mouse is on svg path or not, if it is i want to change the cursor to mouse to pointer.
This could have been easily done by adding mouse hover property on path and also with Recognize point(x,y) is inside svg path or outside
with this solution.
but there is a twist, I have another transparent layer over it, because of which I cannot have those two solutions.
right now I am making top layer display none and it works fine. but because of this my mouse pointer and the action I do such as moving a certain element on mouse move is slow,
hence i want to find out if there is any other better way without making display equal to none.
please find the fiddle example, I want to change the cursor to pointer when its on mypath element and also want myline should be moving as i move mouse over the layer, i can do display to none on layer for time being, but i noticed on firefox, line movement is not that smooth,
https://jsfiddle.net/shyam_bhiogade/9a7zuou2/6/
<svg width="400" height="400">
<g>
<path id="mypath" d="M10 200 L200 90 L200 200" fill="transparent" stroke="black" stroke-width="5" />
<rect class="interactiveArea" width="500" height="500" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0);opacity:0.2" />
<line id="myline" x1="20" y1="0" x2="20" y2="400" stroke-width="2" stroke="black" />
</g>
</svg>
I have used the solution given at https://bl.ocks.org/mbostock/8027637 , it returns the distance of x and y point from the path, if the distance is less than 1px or width of the stroke, I consider that x and y point is on the path.
function closestPoint(pathNode, point) {
var pathLength = pathNode.getTotalLength(),
precision = 8,
best,
bestLength,
bestDistance = Infinity;
// linear scan for coarse approximation
for (var scan, scanLength = 0, scanDistance; scanLength <= pathLength; scanLength += precision) {
if ((scanDistance = distance2(scan = pathNode.getPointAtLength(scanLength))) < bestDistance) {
best = scan, bestLength = scanLength, bestDistance = scanDistance;
}
}
// binary search for precise estimate
precision /= 2;
while (precision > 0.5) {
var before,
after,
beforeLength,
afterLength,
beforeDistance,
afterDistance;
if ((beforeLength = bestLength - precision) >= 0 && (beforeDistance = distance2(before = pathNode.getPointAtLength(beforeLength))) < bestDistance) {
best = before, bestLength = beforeLength, bestDistance = beforeDistance;
} else if ((afterLength = bestLength + precision) <= pathLength && (afterDistance = distance2(after = pathNode.getPointAtLength(afterLength))) < bestDistance) {
best = after, bestLength = afterLength, bestDistance = afterDistance;
} else {
precision /= 2;
}
}
best = [best.x, best.y];
best.distance = Math.sqrt(bestDistance);
return best;
function distance2(p) {
var dx = p.x - point[0],
dy = p.y - point[1];
return dx * dx + dy * dy;
}
}

Drawing and animating a triangle in SVG and HTML with user input

I have a college project that I've chose to present in HTML, the user would input the three sides of a triangle and the shape would be rendered on the screen. I've made a JavaScript that get these values and create the x and y coordinates drawing the triangle inside the <canvas> tag:
<script type="application/javascript">
function init() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
var a = *user input*;
var b = *user input*;
var c = *user input*;
var ox = 450-(a/2); // 450px since the canvas size is 900px,
var oy = 450+(y3/2); // this aligns the figure to the center
var x3 = ((b*b)+(a*a)-(c*c))/(2*a);
var y3 = Math.ceil(Math.sqrt((b*b)-(x3*2)));
var img = new Image();
img.src = 'grad.png';
ctx.strokeStyle = '#fff';
ctx.lineWidth = 3;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = 10;
ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';
var ptrn = ctx.createPattern(img,'repeat');
ctx.fillStyle = ptrn;
ctx.beginPath();
ctx.moveTo(ox,oy);
ctx.lineTo(a+ox,oy);
ctx.lineTo(ox+x3,oy-y3);
ctx.lineTo(ox,oy);
ctx.fill();
ctx.stroke();
ctx.closePath();
}
}
</script>
<body onLoad="init();">
<canvas id="canvas" width="900" height="900"></canvas><br>
</body>
I'm trying to compose a simple scale animation once the page is loaded making the triangle and other shapes to "grow" on the screen. If I use CSS, the entire canvas will scale. Also, I don't know how to make this animation possible since the values are not fixed and using canvas, I would have to animate this frame-by-frame.
Now if I use CSS and SVG, I could use a simple ease-in and scale effect for each element, the problem is that I would have to generate the triangle in a SVG using the values inputted by user. How can I do this?
A triangle is a polygon with 3 points. Look at SVG Polygon documentation.
In JavaScript you can create a polygon like so:
var svgns = "http://www.w3.org/2000/svg";
function makeTriangle() {
shape = svgDocument.createElementNS(svgns, "polygon");
shape.setAttributeNS(null, "points", "5,5 45,45 5,45");
shape.setAttributeNS(null, "fill", "none");
shape.setAttributeNS(null, "stroke", "green");
svgDocument.documentElement.appendChild(shape);
}
If you're always going to have a triangle (or polygon) on the screen, I would create the basic framework with SVG/CSS and set the attribute wuth CSS:
<svg xmlns="http://www.w3.org/2000/svg" width="900" height="900">
<defs>
<filter id="dropshadow" height="130%">
<feGaussianBlur in="SourceAlpha" stdDeviation="10"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<polygon id="triangle" filter="url(#dropshadow)" />
</svg>
<style>
#triangle {
fill: url(grad.png);
stroke-width: 3px;
stroke: white;
}
</style>
You could then use much of the same code to set the polygon points:
var points = [
[ox, oy].join(','),
[a + ox, oy].join(','),
[ox + x3, oy - y3].join(',')
].join(' ');
document.getElementById('triangle').setAttribute('points', points);
You can see an example here: http://fiddle.jshell.net/fTPdy/

Categories

Resources