tags:

views:

319

answers:

4

Hi,

I'm trying to work with an SVG polygon and javascript. I create a polygon and set its initial point list like this:

var polygon = document.createElementNS('http://www.w3.org/2000/svg','polygon');
polygon.setAttribute("points", "0,0  100,100 200,200");

now what do I do if I want to modify the 2nd point (100,100)? Right now I'm basically reconstructing the whole string again. But can we address "polygon.points" as an array somehow, or is it really just a plain simple string? This can work ok for very simple polygons, but if my polygon eventually has hundreds of point pairs, I'd hate to reconstruct the entire "points" attribute as a string every time I want to modify a single element.

Thanks

A: 

You need to use setAttributeNS. You'll probably want to cache that namespace in a variable somewhere so you don't have to keep typing it.

Kyle Butt
Do we have to use setAttributeNS / getAttributeNS any time we're setting an attribute with an SVG element? For example, even if we want to modify the 'x' parameter of a rect? (rect.setAttributeNS(ns, 'x', 22)? Thanks
@unknown-google: no, using setAttribute() is fine.
heycam
A: 

You need to set all points at once, the performance is pretty solid, what you may want to do is manage the array outside and merge it on the setAttribute calls

Don Albrecht
Ok so, if I want a generic solution, in order to modify the Nth pair, I need to parse the string myself, find it, then modify it. That kind of stinks, but I guess I can keep the point pairs as a separate structure, kind of stinks they made it like that! Thanks
+1  A: 

No way around it I'm afraid. You have to reconstruct the string again. But it's not difficult to wrap the whole thing in an object, something like:

function Polygon () {
    var pointList = [];
    this.node = document.createElementNS('http://www.w3.org/2000/svg','polygon');
    function build (arg) {
        var res = [];
        for (var i=0,l=arg.length;i<l;i++) {
            res.push(arg[i].join(','));
        }
        return res.join(' ');
    }
    this.attribute = function (key,val) {
        if (val === undefined) return node.getAttribute(key);
        node.setAttribute(key,val);
    }
    this.getPoint = function (i) {return pointList[i]}
    this.setPoint = function (i,x,y) {
        pointList[i] = [x,y];
        this.attribute('points',build(pointList));
    }
    this.points = function () {
      for (var i=0,l=arguments.length;i<l;i+=2) {
          pointList.push([arguments[i],arguments[i+1]]);
      }
      this.attribute('points',build(pointList));
    }
    // initialize 'points':
    this.points.apply(this,arguments);
}

var polygon = new Polygon(0,0, 100,100, 200,200);
polygon.setPoint(0, 50,10); // set point and automatically re-build points
polygon.points(50,50, 50,100, 200,100); // set everything
polygon.node; // refer to the actual SVG element

* not the best implementation but you get the idea.

slebetman
Thanks looks great I see what you mean. I'll try to implement an optimized one then on top of a 'vanilla' representation.
+1  A: 

You can access the individual point values using the SVG DOM:

var p = polygon.points.getItem(1);
p.x = 150;
p.y = 300;

(Assuming that your UA implements this interface.) See SVGPolygonElement, SVGAnimatedPoints, SVGPointList and SVGPoint.

I find though that using these SVG DOM interfaces (at least for me in Batik, in which I do most of my SVG stuff) is often not faster than just updating the attribute with string manipulation.

heycam