This page displays a SVG having three strokes. Clicking on Save as PNG button will convert the SVG to PNG. This works well.
http://www.holisticedu.us/wechat/three.htm
Clicking on the strokes of the SVG will change the stroke color from back to red. Then clicking on Save as PNG button , the stoke color of the PNG does not change. I have no idea about this. I want that the stroke color of the PNG can also be changed when the stroke color of the SGV was changed.
The following is my code, thank you for any suggestions.
<!DOCTYPE html><html xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><head><meta charset="utf-8"/>
<title>three</title>
<script type="text/javascript">
function changeRectColor(evt) {
evt.target.setAttributeNS(null,"fill","red");
}
</script>
</head><body>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="250" height="250" viewBox="0 0 2052 2052">
<def>
<g id="g1" onclick="changeRectColor(evt)" name="三" transform="translate(514 718) matrix(1 0 0 -1 0 616)">
<path name="1.1" onclick="changeRectColor(evt)" d="M677 619Q699 619 733 602Q756 592 756 577Q756 566 726 559Q691 548 572 530Q390 505 335 505Q284 505 264 517Q244 531 244 541Q244 550 288 552Q390 561 513 583Q599 597 652 613Q671 619 677 619Z"/>
<path name="1.2" onclick="changeRectColor(evt)" d="M645 343Q667 343 701 326Q724 316 724 301Q724 293 700 288Q672 280 575 267Q428 249 383 249Q332 249 312 261Q292 275 292 285Q292 292 328 293Q411 300 512 316Q582 327 625 338Q640 343 645 343Z"/>
<path name="1.3" onclick="changeRectColor(evt)" d="M852 104Q860 104 914 84Q960 59 960 39Q960 21 897 21Q816 23 722 23Q623 23 465 12Q351 7 222 -19Q185 -27 177 -27Q156 -27 115 -9Q72 10 72 21Q72 36 100 38Q244 39 311 51Q438 65 591 73Q735 85 811 99Q844 104 852 104Z"/>
</g>
</def>
<g id="bg" transform="translate(514 718)">
<rect id="sq" style="fill:none;stroke:lightgray;stroke-width:4" x="-512" y="-716" width="0" height="0"/>
<g id="box"><rect style="fill:none;stroke:black;stroke-width:4" x="0" y="-204" width="1024" height="1024"/>
<line style="stroke:black;stroke-width:2;stroke-dasharray:60 60;" x1="0" y1="308" x2="1024" y2="308"/>
<line style="stroke:black;stroke-width:2;stroke-dasharray:60 60;" x1="512" y1="-204" x2="512" y2="820"/>
</g></g>
<use id="u1" xlink:href="#g1" x="0" y="0"/>
</svg>
<button><font size = 4>Save as PNG</font></button>
<canvas id="he_canvas" width="250" height="250"></canvas>
<script type="text/javascript">
var btn = document.querySelector('button');
var svg = document.querySelector('svg');
var canvas = document.querySelector('he_canvas');
function triggerDownload (imgURI) {
var evt = new MouseEvent('click', {
view: window,
bubbles: false,
cancelable: true
});
var a = document.createElement('a');
a.setAttribute('download', 'mypng.png');
a.setAttribute('href', imgURI);
a.setAttribute('target', '_blank');
a.dispatchEvent(evt);
}
btn.addEventListener('click', function () {
var canvas_browser = document.getElementById('he_canvas');
var ctx = canvas_browser.getContext('2d');
var data = (new XMLSerializer()).serializeToString(svg);
var DOMURL = window.URL || window.webkitURL || window;
var img = new Image();
var svgBlob = new Blob([data], {type: 'image/svg+xml;charset=utf-8'});
var url = DOMURL.createObjectURL(svgBlob);
img.onload = function () {
ctx.drawImage(img, 0, 0);
DOMURL.revokeObjectURL(url);
var imgURI = canvas_browser
.toDataURL('image/png')
.replace('image/png', 'image/octet-stream');
triggerDownload(imgURI);
};
img.src = url;
});
</script>
</body></html>
Important note :
This question is raised by a behavior which is only implemented in Firefox, so the first part of this answer will assume to be seen from Firefox.
This one is an interesting case...
So what you are doing is to handle click events on cloned nodes through an <use> element.
evt.target.setAttributeNS(null,"fill","red");
Here, evt.target will be the cloned node, not the original one, you can check it by cloning twice your elements :
<script>
function changeRectColor(evt) {
evt.target.setAttributeNS(null,"fill","red");
}
</script>
<svg width="500" height="250" viewBox="0 0 4104 2052">
<def>
<g id="g1" onclick="changeRectColor(evt)" name="三" transform="translate(514 718) matrix(1 0 0 -1 0 616)">
<path name="1.1" onclick="changeRectColor(evt)" d="M677 619Q699 619 733 602Q756 592 756 577Q756 566 726 559Q691 548 572 530Q390 505 335 505Q284 505 264 517Q244 531 244 541Q244 550 288 552Q390 561 513 583Q599 597 652 613Q671 619 677 619Z"></path>
<path name="1.2" onclick="changeRectColor(evt)" d="M645 343Q667 343 701 326Q724 316 724 301Q724 293 700 288Q672 280 575 267Q428 249 383 249Q332 249 312 261Q292 275 292 285Q292 292 328 293Q411 300 512 316Q582 327 625 338Q640 343 645 343Z"></path>
<path name="1.3" onclick="changeRectColor(evt)" d="M852 104Q860 104 914 84Q960 59 960 39Q960 21 897 21Q816 23 722 23Q623 23 465 12Q351 7 222 -19Q185 -27 177 -27Q156 -27 115 -9Q72 10 72 21Q72 36 100 38Q244 39 311 51Q438 65 591 73Q735 85 811 99Q844 104 852 104Z"></path>
</g>
</def>
<g id="bg" transform="translate(514 718)">
<rect id="sq" style="fill:none;stroke:lightgray;stroke-width:4" x="-512" y="-716" width="0" height="0"></rect>
<g id="box"><rect style="fill:none;stroke:black;stroke-width:4" x="0" y="-204" width="1024" height="1024"></rect>
<line style="stroke:black;stroke-width:2;stroke-dasharray:60 60;" x1="0" y1="308" x2="1024" y2="308"></line>
<line style="stroke:black;stroke-width:2;stroke-dasharray:60 60;" x1="512" y1="-204" x2="512" y2="820"></line>
</g></g>
<use id="u1" xlink:href="#g1" x="0" y="0"></use>
<use id="u2" xlink:href="#g1" x="1026" y="0"></use>
</svg>
As you can see, only the cloned one gets modified, but this cloned one is not accessible from the DOM (it is only in some shadow-DOM), hence, when you parse your SVG element to create a new image from it, you won't be able to parse the changes made to these cloned nodes.
Solution
Don't use a <use> element... Instead, display directly the original nodes.
Anyway, as said previously, only Firefox does handle click events on cloned nodes inside a <use> element.
var btn = document.querySelector('button');
var svg = document.querySelector('svg');
btn.addEventListener('click', function () {
var canvas_browser = document.createElement('canvas');
var ctx = canvas_browser.getContext('2d');
var data = (new XMLSerializer()).serializeToString(svg);
var DOMURL = window.URL || window.webkitURL || window;
var img = new Image();
var svgBlob = new Blob([data], {type: 'image/svg+xml;charset=utf-8'});
var url = DOMURL.createObjectURL(svgBlob);
img.onload = function () {
canvas_browser.width = img.width;
canvas_browser.height = img.height;
ctx.drawImage(img, 0, 0);
DOMURL.revokeObjectURL(url);
document.body.appendChild(canvas_browser)
};
img.src = url;
});
<button>download</button><br>
<script>
function changeRectColor(evt) {
evt.target.setAttributeNS(null,"fill","red");
}
</script>
<svg width="250" height="250" viewBox="0 0 2052 2052">
<g id="bg" transform="translate(514 718)">
<rect id="sq" style="fill:none;stroke:lightgray;stroke-width:4" x="-512" y="-716" width="0" height="0"></rect>
<g id="box"><rect style="fill:none;stroke:black;stroke-width:4" x="0" y="-204" width="1024" height="1024"></rect>
<line style="stroke:black;stroke-width:2;stroke-dasharray:60 60;" x1="0" y1="308" x2="1024" y2="308"></line>
<line style="stroke:black;stroke-width:2;stroke-dasharray:60 60;" x1="512" y1="-204" x2="512" y2="820"></line>
</g></g>
<g id="g1" onclick="changeRectColor(evt)" name="三" transform="translate(514 718) matrix(1 0 0 -1 0 616)">
<path name="1.1" onclick="changeRectColor(evt)" d="M677 619Q699 619 733 602Q756 592 756 577Q756 566 726 559Q691 548 572 530Q390 505 335 505Q284 505 264 517Q244 531 244 541Q244 550 288 552Q390 561 513 583Q599 597 652 613Q671 619 677 619Z"></path>
<path name="1.2" onclick="changeRectColor(evt)" d="M645 343Q667 343 701 326Q724 316 724 301Q724 293 700 288Q672 280 575 267Q428 249 383 249Q332 249 312 261Q292 275 292 285Q292 292 328 293Q411 300 512 316Q582 327 625 338Q640 343 645 343Z"></path>
<path name="1.3" onclick="changeRectColor(evt)" d="M852 104Q860 104 914 84Q960 59 960 39Q960 21 897 21Q816 23 722 23Q623 23 465 12Q351 7 222 -19Q185 -27 177 -27Q156 -27 115 -9Q72 10 72 21Q72 36 100 38Q244 39 311 51Q438 65 591 73Q735 85 811 99Q844 104 852 104Z"></path>
</g>
</svg>
Related
I'm trying to add an SVG inside an SVG so that I can manipulate the fill colors of both of them.
For example, if I have an SVG of Square with path fill of Red, and I want to add SVG of a circle with color blue I can do it by writing 2 SVG with position absolute, but I want the circle SVG inside the Square SVG multiplying itself to the fill the Square SVG space limiting itself to Square SVG's area only.
Like this.
Which I did achieve by adding an image as a pattern inside that Square SVG. Like:
<svg class="blowse" xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 46.000000 66.000000" preserveAspectRatio="xMidYMid meet" >
<g transform="translate(0.000000,66.000000) scale(0.100000,-0.100000)" fill="#000000" stroke="none">
<pattern id="blowse_pattern_img" patternUnits="userSpaceOnUse" width="100%" height="100%">
<image id="blowse_img" href="https://harryatwork.com/vinay/images/club.png" x="0" y="0" width="50px" height="50px"></image>
</pattern>
<path style="fill:red;" class="path_blowse" d="M343 619 c-6 -6 -26 -12 -44 -13 -127 -11 -279 -102 -266 -158 7 -29 67 -196 94 -263 11 -27 26 -69 32 -92 7 -24 20 -47 30 -53 17 -9 19 -4 23 63 3 55 11 88 36 138 28 58 43 75 110 125 l79 59 -18 29 c-16 26 -26 60 -45 154 -5 24 -14 28 -31 11z" />
<path style="fill:url(#blowse_pattern_img)" class="path_blowse_img" d="M343 619 c-6 -6 -26 -12 -44 -13 -127 -11 -279 -102 -266 -158 7 -29 67 -196 94 -263 11 -27 26 -69 32 -92 7 -24 20 -47 30 -53 17 -9 19 -4 23 63 3 55 11 88 36 138 28 58 43 75 110 125 l79 59 -18 29 c-16 26 -26 60 -45 154 -5 24 -14 28 -31 11z" />
</g>
</svg>
However, using JQuery I can manipulate the Parent SVG's fill but I cannot do anything about the image that I did just added.
So to Manipulate the color of the child, I need it to be an SVG instead of an Image.
So, I need to add another path of that shape along with the parent path. like :
<svg class="blowse" xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 46.000000 66.000000" preserveAspectRatio="xMidYMid meet" >
<g transform="translate(0.000000,66.000000) scale(0.100000,-0.100000)" fill="#000000" stroke="none">
<path class="path_blowse" d="M343 619 c-6 -6 -26 -12 -44 -13 -127 -11 -279 -102 -266 -158 7 -29 67 -196 94 -263 11 -27 26 -69 32 -92 7 -24 20 -47 30 -53 17 -9 19 -4 23 63 3 55 11 88 36 138 28 58 43 75 110 125 l79 59 -18 29 c-16 26 -26 60 -45 154 -5 24 -14 28 -31 11z" />
<path xmlns="http://www.w3.org/2000/svg" d="M5656 11870 c-498 -45 -890 -217 -1275 -559 -403 -360 -677 -1053 -698 -1766 -11 -366 36 -549 248 -980 161 -328 202 -435 165 -435 -27 0 -153 43 -311 105 -327 130 -593 184 -1015 204 -340 17 -465 -6 -714 -131 -207 -104 -385 -237 -592 -443 -242 -241 -393 -448 -524 -720 -169 -349 -207 -506 -217 -886 -9 -328 17 -544 87 -744 38 -108 183 -418 247 -530 358 -624 949 -1041 1653 -1165 159 -28 479 -38 642 -20 531 58 1102 312 1543 684 156 132 362 348 474 498 24 32 144 207 267 390 149 221 230 333 242 333 26 0 33 -60 26 -250 -12 -385 -35 -617 -85 -870 -143 -725 -468 -1336 -988 -1855 -181 -181 -280 -265 -440 -371 -268 -178 -520 -276 -1206 -468 -471 -132 -622 -190 -709 -274 -43 -41 -75 -112 -58 -129 14 -14 479 -27 1312 -39 1056 -14 5327 -7 5545 10 308 23 350 44 296 149 -45 86 -120 127 -316 172 -414 94 -887 235 -1170 348 -376 151 -662 347 -970 663 -436 449 -744 1015 -879 1619 -52 236 -112 621 -146 938 -20 197 -27 422 -12 422 13 0 136 -168 282 -386 263 -392 453 -616 720 -848 515 -447 1168 -722 1765 -743 363 -12 718 68 1070 242 267 132 454 266 657 472 204 206 327 383 473 678 109 219 146 325 177 497 21 113 23 154 23 488 0 306 -3 383 -18 479 -33 205 -86 358 -212 613 -128 259 -262 444 -461 638 -185 180 -312 270 -539 380 -313 153 -572 207 -933 197 -314 -9 -487 -53 -857 -217 -210 -93 -349 -150 -365 -150 -32 0 17 130 174 463 169 356 219 499 251 712 47 316 -20 800 -167 1195 -119 320 -280 574 -499 785 -361 349 -729 524 -1240 591 -145 18 -575 27 -723 14z"/>
</g>
</svg>
Which did nothing.
Hope I was clear in explaining my query.
Working FIDDLE
I need an SVG instead of an image there.
Any help is greatly appreciated.
The club path is far away outside the svg canvas. Besides is much bigger than the path you need to fill with the pattern.
In order to see the club I'm putting it in a symbol with a viewBox and use the symbol with the position (x,y) and the size (width, height) I want. (the red club in the next example)
I also can use the symbol to build a pattern and use the pattern to fill the shape.
<svg class="blowse" xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 46.000000 66.000000" preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,66.000000) scale(0.100000,-0.100000)" fill="#000000" stroke="none">
<path fill="url(#blowse_pattern_img)" class="path_blowse" d="M343 619 c-6 -6 -26 -12 -44 -13 -127 -11 -279 -102 -266 -158 7 -29 67 -196 94 -263 11 -27 26 -69 32 -92 7 -24 20 -47 30 -53 17 -9 19 -4 23 63 3 55 11 88 36 138 28 58 43 75 110 125 l79 59 -18 29 c-16 26 -26 60 -45 154 -5 24 -14 28 -31 11z" />
<use href="#club" x="100" y="400" width="30" height="30" fill="red" />
</g>
<defs>
<pattern id="blowse_pattern_img" patternUnits="userSpaceOnUse" width="30" height="30">
<use href="#club" width="20" height="20" fill="gold" />
</pattern>
<symbol viewBox="720 1440 10530 10530" id="club">
<path xmlns="http://www.w3.org/2000/svg" d="M5656 11870 c-498 -45 -890 -217 -1275 -559 -403 -360 -677 -1053 -698 -1766 -11 -366 36 -549 248 -980 161 -328 202 -435 165 -435 -27 0 -153 43 -311 105 -327 130 -593 184 -1015 204 -340 17 -465 -6 -714 -131 -207 -104 -385 -237 -592 -443 -242 -241 -393 -448 -524 -720 -169 -349 -207 -506 -217 -886 -9 -328 17 -544 87 -744 38 -108 183 -418 247 -530 358 -624 949 -1041 1653 -1165 159 -28 479 -38 642 -20 531 58 1102 312 1543 684 156 132 362 348 474 498 24 32 144 207 267 390 149 221 230 333 242 333 26 0 33 -60 26 -250 -12 -385 -35 -617 -85 -870 -143 -725 -468 -1336 -988 -1855 -181 -181 -280 -265 -440 -371 -268 -178 -520 -276 -1206 -468 -471 -132 -622 -190 -709 -274 -43 -41 -75 -112 -58 -129 14 -14 479 -27 1312 -39 1056 -14 5327 -7 5545 10 308 23 350 44 296 149 -45 86 -120 127 -316 172 -414 94 -887 235 -1170 348 -376 151 -662 347 -970 663 -436 449 -744 1015 -879 1619 -52 236 -112 621 -146 938 -20 197 -27 422 -12 422 13 0 136 -168 282 -386 263 -392 453 -616 720 -848 515 -447 1168 -722 1765 -743 363 -12 718 68 1070 242 267 132 454 266 657 472 204 206 327 383 473 678 109 219 146 325 177 497 21 113 23 154 23 488 0 306 -3 383 -18 479 -33 205 -86 358 -212 613 -128 259 -262 444 -461 638 -185 180 -312 270 -539 380 -313 153 -572 207 -933 197 -314 -9 -487 -53 -857 -217 -210 -93 -349 -150 -365 -150 -32 0 17 130 174 463 169 356 219 499 251 712 47 316 -20 800 -167 1195 -119 320 -280 574 -499 785 -361 349 -729 524 -1240 591 -145 18 -575 27 -723 14z" />
</symbol>
</defs>
</svg>
Embed the inner SVG directly:
<svg class="blowse" xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 46.000000 66.000000" preserveAspectRatio="xMidYMid meet" >
<g transform="translate(0.000000,66.000000) scale(0.100000,-0.100000)" fill="#000000" stroke="none">
<pattern id="blowse_pattern_img" patternUnits="userSpaceOnUse" width="100%" height="100%">
<svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" viewBox="0 0 500 500">
<circle cx="250" cy="250" r="210" fill="#fff" stroke="#000" stroke-width="8"/>
</svg>
</pattern>
<path style="fill:red;" class="path_blowse" d="M343 619 c-6 -6 -26 -12 -44 -13 -127 -11 -279 -102 -266 -158 7 -29 67 -196 94 -263 11 -27 26 -69 32 -92 7 -24 20 -47 30 -53 17 -9 19 -4 23 63 3 55 11 88 36 138 28 58 43 75 110 125 l79 59 -18 29 c-16 26 -26 60 -45 154 -5 24 -14 28 -31 11z" />
<path style="fill:url(#blowse_pattern_img)" class="path_blowse_img" d="M343 619 c-6 -6 -26 -12 -44 -13 -127 -11 -279 -102 -266 -158 7 -29 67 -196 94 -263 11 -27 26 -69 32 -92 7 -24 20 -47 30 -53 17 -9 19 -4 23 63 3 55 11 88 36 138 28 58 43 75 110 125 l79 59 -18 29 c-16 26 -26 60 -45 154 -5 24 -14 28 -31 11z" />
</g>
</svg>
In this way, it can be controlled as a separate item with its own context.
I'm trying to animate SVG on scroll, i've created the SVG path my self using math, and it's showing as what i need exactly, but the problem started when i tried to add effect on path using CSS stroke-dasharray
my problem is : the animation starts from two points, one from the start, and the other after the q command, as long as i used move command twice, one at the start and one after q curve command.
i tried to fix this all the way, but it changes the drawn SVG totally.
here is a snippet to explain more how it works with me,
const shape = document.querySelector('#shape');
window.addEventListener('scroll', slide);
function slide() {
let value = 2000 - scrollY*4;
shape.style.strokeDashoffset= value;
}
body {
background:black;
-webkit-user-select:none;
}
#svgdiv {
position:fixed;
margin:auto;
top:1rem;
width:95%;
}
#div1 {
height:30rem;
}
#shape {
stroke-dashoffset:2000;
stroke-dasharray: 2000;
}
#shapebg {
stroke-dashoffset:2000;
stroke-dasharray: 0;
}
#div2 {
height:19rem;
}
<div id="div1"></div>
<div id="svgdiv">
<svg viewBox="0 0 1080 500">
<path id="shape" d="M 0 0 10 0 10 0 10 50 10 50 40 50 40 50 40 120 40 120 235 120 235 120 235 50 235 50 q 450 -20 290 130 M 525 180 595 220 595 220 605 300 605 300 675 250 675 250 725 320 725 320 765 255 765 255 835 260 835 260 810 200 810 200 890 70 890 70 1050 130 1050 130 1040 260 1040 260 880 270 880 270 1079 300" stroke="purple" stroke-width="1.5" fill="none" />
<path id="shapebg" d="M 0 0 10 0 10 0 10 50 10 50 40 50 40 50 40 120 40 120 235 120 235 120 235 50 235 50 q 450 -20 290 130 M 525 180 595 220 595 220 605 300 605 300 675 250 675 250 725 320 725 320 765 255 765 255 835 260 835 260 810 200 810 200 890 70 890 70 1050 130 1050 130 1040 260 1040 260 880 270 880 270 1079 300" stroke="purple" stroke-width="0.6" fill="none" />
</svg>
</div>
<div id="div2">
</div>
as you can see it starts from two points, but i need the animation to start from scratch and go to end directly,
any hints please ?
I`ve made a few changes to your code;
I've removed the move to command in the middle to get a continous path: M 525 180 595 220 becomes L595 220. Since you don't have any command letter after M, by default everything that comes next are lines L
The total length of your path is 2223 not 2000. I've changed it everywere. To know the length of the path you need to use the getTotalLength() method.
You don't scroll enough. In your code let value = 2000 - scrollY*4; but the #div1 { height:30rem;} Since in this case 1rem = 16px, 30rem = 16*30 = 480. Since you need to scroll 2223 you need to use let value = 2000 - scrollY*4.631; (2223/480 = 4.631)
Observation: I've rounded numbers, you can use unrounded numbers
const shape = document.querySelector('#shape');
window.addEventListener('scroll', slide);
function slide() {
let value = 2223 - scrollY*4.631;
shape.style.strokeDashoffset= value;
}
//console.log(shape.getTotalLength())
body {
background:black;
-webkit-user-select:none;
}
#svgdiv {
position:fixed;
margin:auto;
top:1rem;
width:95%;
}
#div1 {
height:30rem;
}
#shape {
stroke-dashoffset:2223;
stroke-dasharray: 2223;
}
#div2 {
height:19rem;
}
<div id="div1"></div>
<div id="svgdiv">
<svg viewBox="0 0 1080 500">
<path id="shape" d="M0,0L10,0L10,0L10,50L10,50L40,50L40,50L40,120L40,120L235,120L235,120L235,50L235,50Q685,30,525,180L 595 220 595 220 605 300 605 300 675 250 675 250 725 320 725 320 765 255 765 255 835 260 835 260 810 200 810 200 890 70 890 70 1050 130 1050 130 1040 260 1040 260 880 270 880 270 1079 300" stroke="purple" stroke-width="2.5" fill="none" />
<path id="shapebg" d="M0,0L10,0L10,0L10,50L10,50L40,50L40,50L40,120L40,120L235,120L235,120L235,50L235,50Q685,30,525,180L 595 220 595 220 605 300 605 300 675 250 675 250 725 320 725 320 765 255 765 255 835 260 835 260 810 200 810 200 890 70 890 70 1050 130 1050 130 1040 260 1040 260 880 270 880 270 1079 300" stroke="purple" stroke-width="1.5" fill="none" />
</svg>
</div>
<div id="div2">
</div>
I want to auto generate an imagemap type of result for a raster image. I was able to supply this image as a PNG:
The original SVG for this looks like this:
<svg width="580" height="400" xmlns="http://www.w3.org/2000/svg">
<g>
<rect fill="#fff" id="canvas_background" height="402" width="582" y="-1" x="-1"/>
<g display="none" overflow="visible" y="0" x="0" height="100%" width="100%" id="canvasGrid">
<rect fill="url(#gridpattern)" stroke-width="0" y="0" x="0" height="100%" width="100%"/>
</g>
</g>
<g>
<rect id="svg_1" height="67" width="54" y="119.5" x="125.5" stroke-width="1.5" stroke="#000" fill="#fff"/>
<rect id="svg_3" height="67" width="54" y="119.5" x="180.5" stroke-width="1.5" stroke="#000" fill="#fff"/>
</g>
</svg>
Once I traced it using the library: https://github.com/jankovicsandras/imagetracerjs I get back path data like this:
<svg width="156" height="114" version="1.1" xmlns="http://www.w3.org/2000/svg" desc="Created with imagetracer.js version 1.2.3" >
<path fill="rgb(60,60,60)" stroke="rgb(60,60,60)" stroke-width="1" opacity="1" d="M 20 20 L 131 20 L 131 89 L 20 89 L 20 20 Z M 22 22 L 22 87 L 74 87 L 74 22 L 22 22 Z M 77 22 L 77 87 L 129 87 L 129 22 L 77 22 Z " />
<path fill="rgb(255,255,255)" stroke="rgb(255,255,255)" stroke-width="1" opacity="1" d="M 0 0 L 156 0 L 156 114 L 0 114 L 0 0 Z M 20 20 L 20 89 L 131 89 L 131 20 L 20 20 Z " />
<path fill="rgb(255,255,255)" stroke="rgb(255,255,255)" stroke-width="1" opacity="1" d="M 22 22 L 74 22 L 74 87 L 22 87 L 22 22 Z " />
<path fill="rgb(255,255,255)" stroke="rgb(255,255,255)" stroke-width="1" opacity="1" d="M 77 22 L 129 22 L 129 87 L 77 87 L 77 22 Z " />
</svg>
I would like to go back to the rect or polygon method so I can measure the area of each object so that if there were traced text I could exclude it / flatten it by saying it's total area is lower than allowed as a polygon / rect object.
Is there a way to convert the path data back to where I have 2 separate objects? I want to be able to overlay the results over the original image and allow targeting each square
I try to answer your question as best as I can, but there are multiple solutions here.
If you force imagetracerjs to use only straight line edges (with qtres = 0.00001) , then SVG path elements are polygons, their coordinates are defined in the d attribute: d="M 20 20 L 131 20 ... " in the first example. (More info: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d)
But 1 d attribute is often not just 1 polygon. The shape can include holes, and they are represented as smaller polygons. (More info: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill-rule)
You can extract the d attributes with e.g. Regex, split them by the Z tags (which mean roughly: this shape ends, a hole shape begins) , then use the Gauss's area formula (https://en.wikipedia.org/wiki/Shoelace_formula) on the coordinates to get the areas. You might need to subtract the areas of the holes from the area of the parent shape.
You have 4 separate objects on your image, but some are not trivial. I try to "translate" your SVG result so you can decide which path to process.
ImageTracer output is sorted by colors first. The 1. path is the blackish frame around the two smaller rectangles, technically this is a blackish rectangle with two rectangle holes. The 2. path is white frame around the blackish frame, technically a white rectangle with a big rectangle hole. The 3. and 4. paths are the smaller white rectangles, which might be relevant for you.
There's an alternative to splitting and parsing the SVG string, but it's a bit undocumented, sadly. You can get a tracedata object first, process it, and optionally render it to SVG string later. (More info: https://github.com/jankovicsandras/imagetracerjs#examples )
var options = {qtres:0.0001};
ImageTracer.imageToTracedata(
'example.png',
function(tracedata){
// color layers loop
for(var i=0; i<tracedata.layers.length; i++){
// paths loop
for(var j=0; j<tracedata.layers[i].length; j++){
var thispath = tracedata.layers[i][j];
// processing this path if it's not a hole
if( !thispath.isholepath ){
// accumulating coordinates in [ [x,y] , [x,y] , ... ] polygon
var thispolygon = [];
thispolygon.push( [ thispath.segments[0].x1 , thispath.segments[0].y1 ] );
for(var k=0; k<thispath.segments.length; k++){ thispolygon.push( [ thispath.segments[k].x2 , thispath.segments[k].y2 ] ); }
// TODO: calculate thispolygon area with https://en.wikipedia.org/wiki/Shoelace_formula here
}
}// End of paths loop
}// End of color layers loop
},// End of imageToTracedata callback()
options
);// End of ImageTracer.imageToTracedata()
I hope these help. :)
acknowledgement: reference
[SVG] How to close the path (between the 1st and the last point) and fill the area accordingly:
It's not a single path, so i can't close it with 'Z' at the end
It's not a single path, so I can't close it with 'Z' at the end
Join the paths first.
E.g. the sample from your link is originally
<path xmlns="http://www.w3.org/2000/svg" fill="none" stroke="blue" stroke-width="8" d="M 60 60 C 111.55555555555557 160 163.11111111111114 260 220 300"/>
<path xmlns="http://www.w3.org/2000/svg" fill="none" stroke="red" stroke-width="8" d="M 220 300 C 276.88888888888886 340 339.11111111111114 320 420 300"/>
But if you append the dstring of the second to the first and replace the second M (Move) with L (LineTo), you get this:
<path xmlns="http://www.w3.org/2000/svg" fill="cyan" stroke="red" stroke-width="8" d="M 60 60 C 111.55555555555557 160 163.11111111111114 260 220 300 L 220 300 C 276.88888888888886 340 339.11111111111114 320 420 300 Z"/>
I have also set fill=cyan.
Some of these drawn things are from the linked site and not in the code in my answer.
I'm trying to add a point into a SVG-Path at a certain position on the path.
<svg width="190" height="160" xmlns="http://www.w3.org/2000/svg">
<path d="M 48 241 C 50 13 129 26 130 238 C 138 521 214 431 220 228 C 237 65 288 -18 316 240" stroke="black" fill="transparent"/>
</svg>
Furthermore, I would like to add a point in the Path (curve), so that the path (curve) doesn't deform.
The point is e.g. "C x1 y1 x2 y2 210 331".
My question:.
How can I determine the controls x1 y1 x2 y2?
E.g. "C 100 100 100 500 210 331" is wrong, because the curve will be deformed.
<svg width="190" height="160" xmlns="http://www.w3.org/2000/svg">
<path d= "M 48 241 C 50 13 129 26 130 238 C 100 100 100 500 210 331 C 210 331 210 331 220 228 C 237 65 288 -18 316 240" stroke="black" fill="transparent"/>
</svg>
In other words the point (x = 210, y = 331) is given. Wanted are controls "x1 y1 x2 y2".
See here for more explanation
(In order to add a graphic in “stackoverflow.com”, it is needed some points. But I haven’t enough points, therefore I inserted a link)
Thanks in advance.