Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drawing SVG into Canvas and manipulating drawn elements using their IDs

I have an SVG file loaded into an <object> tag. I am using javascript to manipulate some elements within this svg. For example:

var theSvgXml = document.getElementById('theObject').contentDocument;
theSvgXml.getElementById('theElementId').style.display = 'inline';
theSvgXml.getElementById('theElementId').style.fill = 'red';
theSvgXml.getElementById('anotherElement').style.display = 'none';

This works perfectly and all was well. But i am wondering if the same thing can be done with canvas. I have read about kinetic js, fabric js, canvg js as well, and i saw the various methods of loading the svg file into the canvas either by the file directory, the xml or through an image.

But after drawing this svg file into the canvas, can i manipulate the elements by their IDs?

The SVG is created using Adove Illustrator, and each layer or group is given an id with can be accessed using css selectors. Again, can this be done in canvas after drawing the SVG onto it. (Please note that the SVG is HUGE and the only reason i am thinking about the canvas solution is because of the svg not showing the illustrator multiply effect to create a shadow)

Any help will be highly appreciated. Thank you.

Code Snippet:

<g id="Pockets">
<g id="Pen__x26__Radio_Arm_Pocket" class="st791"> … </g>
<g id="Pen_Pocket_Arm" class="st791"> … </g>
<g id="Card_Zipper_Arm_Pocket" class="st791"> … </g>
<g id="Radio_Pocket_Arm_Pocket" class="st791"> … </g>
<g id="Angled_Chest_Pocket_Right" class="st791"> … </g>
<g id="Angled_Chest_Pocket_Left" class="st791"> … </g>
<g id="Angled_Chest_Pocket_left_and_Right" class="st791"> … </g>
<g id="Chest_Pocket_Right" class="st791"> … </g>
<g id="Chest_Pocket_Left" class="st791"> … </g>
<g id="Chest_Pocket_left_and_Right" class="st791"> … </g>
<g id="Tool_Pocket" class="st791"> … </g>
<g id="Cargo_x2F_Mobile_Pocket_Velcro" class="st791"> … </g>
<g id="Cargo_x2F_Mobile_Pocket_Zip" class="st791"> … </g>
<g id="Cargo_x2F_Mobile_Pocket_Button" class="st791"> … </g>
<g id="Cargo_Pocket_Velcro" class="st791"> … </g>
<g id="Cargo_Pocket_Button" class="st791"> … </g>
<g id="Cargo_Pocket_Zip" class="st791"> … </g>
<g id="Back_Pocket_Right_Velcro" class="st791"> … </g>
<g id="Back_Pocket_left_Velcro" class="st791"> … </g>
<g id="Back_Pocket_left_and_Right_Velcro" class="st791"> … </g>
<g id="Back_Pocket_Right_Velcro_Button" class="st791"> … </g>
<g id="Back_Pocket_left_Velcro_Button" class="st791"> … </g>
<g id="Back_Pocket_left_and_Right_Button" class="st791"> … </g>
<g id="Back_Pocket_Right_Zip" class="st791"> … </g>
<g id="Back_Pocket_left_Zip" class="st791"> … </g>
<g id="Back_Pocket_left_and_Right_Zip" class="st791"> … </g>
</g>

As you can see form the xml snippet after exporting the file from adobe illustrator, each group is set with an ID. How can preserve these in canvas as objects (as Fabrics.js suggests using getObjects() method)? Is there a way to achieve this? If yes, how can i reference these groups? Also, the shadow is a key issue and i do not want ot use flash. Thanks

like image 353
M O H Avatar asked Sep 11 '13 11:09

M O H


People also ask

Can you upload SVG to canvas?

Once you have your SVG files downloaded, you can import them in the Canva editor by selecting the Uploads panel. Once the panel is open, you can drag the SVG files right into the panel or click Upload media to find the SVG files on your computer.

What is the difference between SVG and canvas?

Differences Between SVG and CanvasSVG is a language for describing 2D graphics in XML. Canvas draws 2D graphics, on the fly (with a JavaScript). SVG is XML based, which means that every element is available within the SVG DOM. You can attach JavaScript event handlers for an element.

Which is better SVG or canvas?

SVG gives better performance with smaller number of objects or larger surface. Canvas gives better performance with smaller surface or larger number of objects. SVG is vector based and composed of shapes. Canvas is raster based and composed of pixel.


2 Answers

Here's how to do this with Fabric:

fabric.loadSVGFromURL('/assets/72.svg', function(objects, options){

  var group = fabric.util.groupSVGElements(objects, options);

  group
    .set({ left: 300, top: 200 })
    .scaleToWidth(500)
    .setCoords();

  canvas.add(group);
}, reviver);

function reviver(element, object) {
  object.id = element.getAttribute('id');
}

The code should be pretty self-explanatory. We load SVG; Fabric parses it internally, spitting out set of objects representing each element. We then group those elements and add them onto canvas in one chunk. Reviver is responsible for reading id off of each SVG element and assigning it to a corresponding fabric instance.

Run this snippet in http://fabricjs.com/kitchensink/ and you get:

loaded svg

Let's inspect this grouped object:

canvas.item(0) + ''; "#<fabric.PathGroup (29303): { top: 200, left: 300 }>"

And its children:

canvas.item(0).getObjects(); // Array[2287]

Let's retrieve one by id:

var greenland = canvas.item(0).getObjects().filter(function(obj) {
  return obj.id === 'path4206';
})[0];

This is all plain old Javascript, as you can see. Let's change color of that particular object/shape now:

greenland.fill = 'red';

greenland in red

like image 71
kangax Avatar answered Oct 15 '22 02:10

kangax


<canvas> operates on pixels, it doesn't have the concept of objects/elements like SVG. So to maniupulate the image you'd need to manipulate the underlying SVG and re-render it to canvas after every change.

You can put SVG image on <canvas> using .drawImage method.

var cvs = document.createElement('canvas'), ctx = cvs.getContext('2d');
cvs.width = 600;
cvs.height = 300;
document.body.appendChild(cvs);

var img = new Image();
img.width = '600';
img.height = '300';

img.onload = function(){
    ctx.drawImage(img,0,0);
}
img.src = 'file.svg';

But you can't manipulate SVG created this way. To do this you should try to convert an <svg> element DOM do base46 encoded string, set it an a source of an image, then render to the canvas.

So in the example above instead of img.src = 'file.svg' you'd need

img.src = 'data:image/svg+xml;base64,' + btoa( document.getElementById('mySvgElement').outerHTML );
like image 1
pawel Avatar answered Oct 15 '22 02:10

pawel