PeterParameter
PeterParameter

Reputation: 524

SVG element inserted into DOM is ignored (its type is changed)

I am using the VivaGraph.js library to render a graph in SVG. I am trying to display an image cropped to a circle, for which I am using a clipPath element - as recommended in this post. However, when I create a new SVG element of type that has a capital letter in it, e.g. clipPath in my case, the element that is inserted into the DOM is lowercase, i.e. clippath, even though the string I pass in to the constructor is camelCase. Since SVG is case sensitive, this element is ignored. Everything else seems to be okay.

I also tried to change the order in which I append the child elements, in hopes of changing the 'z-index', but it didn't have an impact on this.

I am using the following code inside of the function that creates the visual representation of the node in the graph (the 'addNode' callback) to create the node:

var clipPhotoId = 'clipPhoto';
var clipPath = Viva.Graph.svg('clipPath').attr('id', clipPhotoId);
var ui = Viva.Graph.svg('g');
var photo = Viva.Graph.svg('image').attr('width', 20).attr('height', 20).link(url).attr('clip-path', 'url(#' + clipPhotoId + ')');
var photoShape = Viva.Graph.svg('circle').attr('r', 10).attr('cx', 10).attr('cy', 10);

clipPath.append(photoShape);

ui.append(clipPath);
ui.append(photo);

return ui;

Thank you!

Upvotes: 0

Views: 327

Answers (1)

ptommasi
ptommasi

Reputation: 1252

There is a bit of tweaking needed on top of the post you provided.

General idea to solve your issue is this one:

  1. We create a VivaGraph svg graphics (which will create an svg element in the dom)
  2. Into this svg graphic we create only once a clip path with relative coordinates
  3. When we create a node we refer to the clip path

Code is:

        var graph = Viva.Graph.graph();

        graph.addNode('a', { img : 'a.jpg' });
        graph.addNode('b', { img : 'b.jpg' });

        graph.addLink('a', 'b');

        var graphics = Viva.Graph.View.svgGraphics();

        // Create the clipPath node
        var clipPath = Viva.Graph.svg('clipPath').attr('id', 'clipCircle').attr('clipPathUnits', 'objectBoundingBox');
        var circle = Viva.Graph.svg('circle').attr('r', .5).attr('cx', .5).attr('cy', .5);
        clipPath.appendChild(circle);

        // Add the clipPath to the svg root
        graphics.getSvgRoot().appendChild(clipPath);

        graphics.node(function(node) {
               return Viva.Graph.svg('image')
                     .attr('width', 30)
                     .attr('height', 30)
                     // I refer to the same clip path for each node
                     .attr('clip-path', 'url(#clipCircle)')
                     .link(node.data.img);
            })
            .placeNode(function(nodeUI, pos){
                nodeUI.attr('x', pos.x - 15).attr('y', pos.y - 15);
            });

        var renderer = Viva.Graph.View.renderer(graph, { graphics : graphics });
        renderer.run();

The result in the dom will be like this:

<svg>
    <g buffered-rendering="dynamic" transform="matrix(1, 0, 0,1,720,230.5)">
        <line stroke="#999" x1="-77.49251279562495" y1="-44.795726056131116" x2="6.447213894549255" y2="-56.29464520347651"></line>
        <image width="30" height="30" clip-path="url(#clipCircle)" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="a.jpg" x="-92.49251279562495" y="-59.795726056131116"></image>
        <image width="30" height="30" clip-path="url(#clipCircle)" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="b.jpg" x="-8.552786105450746" y="-71.2946452034765"></image>
    </g>
    <clipPath id="clipCircle" clipPathUnits="objectBoundingBox">
        <circle r="0.5" cx="0.5" cy="0.5"></circle>
    </clipPath>
</svg>

Notice the clipPathUnits="objectBoundingBox", since it's the main trick for this solution.

Upvotes: 1

Related Questions