Search code examples
javascriptsnap.svg

Is there a way to contour or outline a svg element with Js?


Is there a way to contour or outline a svg element with SnapSVG ot other JavaScript Lib?


Solution

  • Create a canvas slightly bigger than the SVG size you want.

    Draw a rectangle over it in the colour you want the outLine. Set globalCompositeOperation = "destination-atop" then draw the SVG on top. Now you have a mask of the SVG.

    Then draw that Mask on another canvas in a circle the with the radius you want the outline. Then draw the SVG on top and you have an outlined SVG.

    If you have semi transparent parts you will have to do some extra work. But that's the general principle.

    // the SVG image to outline
    svg = `<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  width="328" height="208" id="testSVG" ><defs></defs><path d="M278.5,30Q317.3,35.8,319,72.5Q288.3,93.3,262.5,65Q242.8,77.3,252,134.5Q261.3,160.3,283.5,149C317.8,121.6,257.5,130.8,271,98.5Q270.9,95.6,319.5,97Q322.4,96.9,321,160.5Q297.8,189.3,261.5,185Q237.3,181.3,226,156.5Q214,135.5,218,86.5Q227.8,44.8,242.5,38Q264.5,26,278.5,30Z
    M42.5,30Q89.3,27.3,93,67.5C63.5,99,51.5,42,38,67.5Q95.5,95,97,126.5Q99.3,175.3,60.5,185Q32,187,21.5,177Q4.8,157.3,5,142.5C34.9,102.2,48.5,175.5,65,145.5C75.9,118.3,8.4,111.6,9,86.5Q4.3,36.8,42.5,30Z
    M102.5,33Q133.5,33,135,33.5L157,136L179,33.5Q179,32.5,211,33.5Q173.8,191.3,173.5,182Q139,182.5,139,181.5Q99.9,33.6,102.5,33Z
    "  fill="rgba(255,134,0,1.000)" fill-rule="evenodd" stroke="transparent" stroke-width="1" stroke-opacity="1" ></path></svg>`
    
    // creates image with 2dContext attached
    var createImage= function(w,h){
        var image = document.createElement("canvas");  
        image.width = w;
        image.height =h; 
        image.ctx = image.getContext("2d");
        return image;
    }  
    // Setting width is in pixels
    var outlineWidth = 8;
    var outlineColour = "Yellow";
    
    // Function to outline SVG image. Or any image 
    function outline(){  // as this is the onload this is the image to outline
        var w = this.width + outlineWidth*2+2;  // get the new width and height
        var h = this.height + outlineWidth*2+2;
        var mask = createImage(w,h); // create the mask image
        mask.ctx.clearRect(0,0,w,h);  // Clear is You must do this for destination-atop to work
        mask.ctx.fillStyle = outlineColour;  // 
        mask.ctx.fillRect(0,0,w,h);  // fill the image with the outline colour
        mask.ctx.globalCompositeOperation = "destination-atop";  // now set so only pixels drawn on will show background colour
        mask.ctx.drawImage(this,outlineWidth+1,outlineWidth+1);
        // we now have a mask of the SVG in the colour we want
      
        var outlined = createImage(w,h);   // create the final image
        // draw an outline by drawing a circle with the mask
        for(var i = 0; i < Math.PI*2; i+= (Math.PI*2)/(outlineWidth*2)){
            var x = Math.cos(i)*outlineWidth+outlineWidth+1;
            var y = Math.sin(i)*outlineWidth+outlineWidth+1;
            outlined.ctx.drawImage(mask,x,y);
        }
        // draw the SVG in the center of the outline
        outlined.ctx.drawImage(this,outlineWidth*2+1,outlineWidth*2+1);    
         // done so show the world
        var output = document.getElementById("resultImage");
        output.src = outlined.toDataURL();
        
    }
    
    var svgImage = new Image();
    svgImage.onload = outline;
    svgImage.src = "data:image/svg+xml;base64,"+btoa(svg);
    var input = document.getElementById("inImg");
    input.src = "data:image/svg+xml;base64,"+btoa(svg);
    SVG outlined<br>Outlined image will appear below the original<br>
    <img id="inImg">
    <img id="resultImage">