简体   繁体   中英

Multi polygon SVG selection (fill color) and mask export to an image

Need to load image+SVGmask, select polygons of SVG (fill color) and export tha SVG mask to and image.

I based my initial HTML code on @Praveen answer to this other question to be able to have a background image and put over it a multi-polygon SVG that I already have, to define each color edges.

The objective is to be able to select the polygons required and then save only the mask. A process, step-by-step would be something to the image below where the user selects, for example red and black, and the SVG mask is saved considering not selected polygons in black (selected in white).

处理

I have done an example on JSFiddle to be more clear on the HTML code where src of image and SVG are embebed but they will be in local files as commented on the code.

<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>TEST SVG</title>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!--
https://github.com/eligrey/Blob.js/
https://github.com/eligrey/FileSaver.js
-->
<script src="Blob.js"></script>
<script src="FileSaver.js"></script>
<script>
$(document).ready(function(e) {
        $('img.svg').each(function(){
                var $img = jQuery(this);
                var imgID = $img.attr('id');
                var imgClass = $img.attr('class');
                var imgURL = $img.attr('src');

                jQuery.get(imgURL, function(data) {
                    // Get the SVG tag, ignore the rest
                    var $svg = jQuery(data).find('svg');

                    // Add replaced image's ID to the new SVG
                    if(typeof imgID !== 'undefined') {
                        $svg = $svg.attr('id', imgID);
                    }
                    // Add replaced image's classes to the new SVG
                    if(typeof imgClass !== 'undefined') {
                        $svg = $svg.attr('class', imgClass+' replaced-svg');
                    }

                    // Remove any invalid XML tags as per http://validator.w3.org
                    $svg = $svg.removeAttr('xmlns:a');

                    // Replace image with new SVG
                    $img.replaceWith($svg);

                    $('path').click(function(){
                        if($(this).attr("class")=="selected"){
                            $(this).attr("class", "");
                        }
                        else{
                            $(this).attr("class","selected");
                        }
                    });

                }, 'xml');
        });
});


function writeDownloadLink(){
    try {
        var isFileSaverSupported = !!new Blob();
    } catch (e) {
        alert("blob not supported");
    }
    /*
    var blob = new Blob([mySVG], {type: "image/svg+xml"});
    saveAs(blob, "mySVG_selection.png");
    */
};

</script>
<style>
    #bg div{
        position:absolute;  
        top:0px;
        left:0px;
    }
    #bg .path{
        z-index:1;  
    }
    #bg .bg{
        z-index:0;  
    }
    path{
        fill:transparent;
    }
    path:hover{
        fill:rgba(255,255,255,0.6);
        cursor:pointer;
    }
    path.selected{
        fill:rgba(255,255,255,0.6); 
    }
    #savebutton {
        position:absolute;  
        top:0px;
        left:400px;
    }
</style>
</head>
<body>
    <div id="bg">
        <div class="path">
            <!--<img src="Sample_svg.svg" class="svg" />-->
            <img src='data:image/svg+xml;utf8,
                <svg width="1000" height="1000" xmlns="http://www.w3.org/2000/svg">
                    <path d="M0 0 0 200 200 200 200 0 "/>
                    <path d="M0 200 0 400 200 400 200 200 "/>
                    <path d="M200 0 200 200 400 200 400 0 "/>
                    <path d="M200 200 200 400 400 400 400 200 "/>
                </svg>
            ' class="svg" />
        </div>
        <div class="bg">
            <!--<img src="Sample_Bg.png" />-->
            <img src="https://imgur.com/olRQfPy.png" />
        </div>
    </div>
    <div id="savebutton">
        <button onclick="writeDownloadLink()">Save PNG</button>
    </div>
</body>
</html>

So the problem to solve is to extract/export the "SVG mask" and save it in a PNG file. I have a guess (I am not good on web services, javascript,...) that Blob JavaScript can help to do it with a code similar to:

var blob = new Blob([mySVG], {type: "image/svg+xml"});
saveAs(blob, "mySVG_selection.png");

that is someway commented on the example, but no idea how to take only "SVG mask" abd convert it to a image.

EDIT

Based on @enxaneta comments, I update a working code but without the possibility to deselect a selected polygon:

<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>TEST SVG</title>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<script>
function myFunction()
{

    let canv = document.createElement("canvas");
    canv.width = svg.getAttribute("width");
    canv.height = svg.getAttribute("height");
    //canv.width = $('svg').attr("width");
    //canv.height = $('svg').attr("height");
    let ctx = canv.getContext("2d");
    ctx.fillRect(0,0,canv.width,canv.height)

    let selectedRy = [];

    svg.addEventListener("click",(e)=>{
      if(e.target.nodeName == "path"){
        e.target.classList.add("selected");
        selectedRy.push(e.target.getAttribute("d"));
      } 
    })

    action.addEventListener("click",()=>{
      for(let i = 0; i < selectedRy.length; i++){
          let thisPath = new Path2D(selectedRy[i]);
          ctx.fillStyle = "white";
          ctx.fill(thisPath); 
      }
      img.setAttribute("src",canv.toDataURL("myImg.png"));
    })
}
</script>
<style>
    #bg div{
        position:absolute;  
        top:30px;
        left:0px;
    }
    #bg .path{
        z-index:1;  
    }
    #bg .bg{
        z-index:0;  
    }
    path{
        fill:transparent;
    }
    path:hover{
        fill:rgba(255,255,255,0.6);
        cursor:pointer;
    }
    img{
        border:0px solid
    }
</style>
</head>
<body onload="myFunction()">
   <button id="action">click</button>
   <div id="bg">
      <div class="path">
         <!--<img src="Sample_svg.svg" class="svg" />-->
         <svg id="svg" xmlns="http://www.w3.org/2000/svg" width="400" height="400" class="svg replaced-svg">
            <path d="M0 0 0 200 200 200 200 0"></path>
            <path d="M0 200 0 400 200 400 200 200"></path>
            <path d="M200 0 200 200 400 200 400 0"></path>
            <path d="M200 200 200 400 400 400 400 200 " ></path>
         </svg>
         <img id="img" width="400" height="400"/> 
      </div>
      <div class="bg">
         <!--<img src="Sample_Bg.png" />-->
         <img src="https://imgur.com/olRQfPy.png" />
      </div>
   </div>
</body>
</html>

I understand that the user will click on some svg rects, and then you need to save it to an image where the selected rects are white on a black background. I this is the case this is my solution:

  1. user click the rects.
  2. you select those rects and save the d attribute in the selectedRy
  3. when the user clicks the button (or some other event) you copy those rects to a canvas (not attached to the DOM in this case) and then you use the toDataURL() to save it as a data uri image.

 let canv = document.createElement("canvas"); canv.width = svg.getAttribute("width"); canv.height = svg.getAttribute("height"); let ctx = canv.getContext("2d"); ctx.fillRect(0,0,canv.width,canv.height) let selectedRy = []; svg.addEventListener("click",(e)=>{ if(e.target.nodeName == "path"){ e.target.classList.add("selected"); selectedRy.push(e.target.getAttribute("d")); } }) action.addEventListener("click",()=>{ for(let i = 0; i < selectedRy.length; i++){ let thisPath = new Path2D(selectedRy[i]); ctx.fillStyle = "white"; ctx.fill(thisPath); } img.setAttribute("src",canv.toDataURL()); }) 
 path:hover{opacity:.5} img{border:1px solid} 
 <button id="action">click</button> <div class="path"> <svg id="svg" xmlns="http://www.w3.org/2000/svg" width="400" height="400" class="svg replaced-svg"> <path d="M0 0 0 200 200 200 200 0" fill="red"></path> <path d="M0 200 0 400 200 400 200 200" fill="blue"></path> <path d="M200 0 200 200 400 200 400 0" fill="lightgreen"></path> <path d="M200 200 200 400 400 400 400 200 " ></path> </svg> <img id="img" width="400" height="400"/> </div> 

I hope this is what you need.

UPDATE

the OP is commenting:

your code does not allow to "delete a selection" as it makes a push. Is there any way to deselect a previous selected polygon?

This update is answering this part of their comment.

Instead of adding a class on click you can toggle that class. The selected rects are all the rects with a .selected class: sel = svg.querySelectorAll(".selected") . Then, when you click the button for every selected path you draw a path on the canvas.

In order to know what svg rects are selected, in the css I've added .selected{opacity:.25}

 let canv = document.createElement("canvas"); canv.width = svg.getAttribute("width"); canv.height = svg.getAttribute("height"); let ctx = canv.getContext("2d"); let sel = []; svg.addEventListener("click",(e)=>{ if(e.target.nodeName == "path"){ e.target.classList.toggle("selected"); sel = svg.querySelectorAll(".selected") } }) action.addEventListener("click",()=>{ //painting a black rect ctx.fillStyle = "black"; ctx.fillRect(0,0,canv.width,canv.height); //for every selected element in the SVG is painting a white rect on the canvas for(let i = 0; i < sel.length; i++){ let thisPath = new Path2D(sel[i].getAttribute("d")); ctx.fillStyle = "white"; ctx.fill(thisPath); } //paint the image img.setAttribute("src",canv.toDataURL()); }) 
 path:hover{opacity:.75} img{border:1px solid} .selected{opacity:.25} 
 <button id="action">click</button> <div class="path"> <svg id="svg" xmlns="http://www.w3.org/2000/svg" width="400" height="400" class="svg replaced-svg"> <path d="M0 0 0 200 200 200 200 0" fill="red"></path> <path d="M0 200 0 400 200 400 200 200" fill="blue"></path> <path d="M200 0 200 200 400 200 400 0" fill="lightgreen"></path> <path d="M200 200 200 400 400 400 400 200 " ></path> </svg> <img id="img" width="400" height="400"/> </div> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM