SVG Polyline Manipulation in TypeScript


#1

I am working with TypeScript 3.1.1 in VS Code. I need to manipulate an SVG Polyline in TypeScript code. I am having trouble creating a new SVGPoint object. My initial HTML looks like this:

<svg class="distance-polyline-svg" width="100%" height="100%" ref="distancePolylineSvg">
    <polyline points="100,100 300,100 200,300 500,300" style="stroke: rgba(6, 28, 129, 0.3); stroke-width: 5; fill: transparent" ref="distancePolyline" />
</svg>

distancePolylineSvg is declared as SVGElement
distancePolyline is declared as SVGPolylineElement

I can access a point using:

this.distancePolyline.points.getItem(i);

But when I try to create a new point to use in this.distancePolyline.points.replaceItem or this.distancePolyline.points.appendItem I am having no success. I’ve tried new SVGPoint() and get a bad constructor error. I’ve tried new DOMPoint() , which works but when using it in replaceItem I get an error stating it is expecting a parameter of type SVGPoint. Casting doesn’t work. Neither SVGElement nor SVGPolygonElement have a createSVGPoint method and document.rootElement.createSVGPoint doesn’t work because rootElement is null.

How do I create a new SVGPoint to pass to the SVGPointList methods?


#2

Hey Tony. I saw your question on stack overflow and asked for a repro there so I can try to get it working.

My guess is there is a bigger issue here. Chances are that you’re going to want to leverage the Aurelia templating engine rather than manually constructing the SVG DOM nodes. It’s impossible to say without more information about the overall problem you’re trying to solve. If you wanna post a bit more about your problem and the best way to approach it here, I might be able to give some advice on the overall architecture.


#3

Hi Matthew,

Architecturally, I think it would be too slow to use the templating engine for this. I think the SVG DOM is a better approach than manipulating the polygon element directly.

As part of my larger project, I have created a custom element for viewing a map. The map uses a canvas to display a seemless grid of map tiles that are downloaded as needed from a mapping provider (Mapbox, HERE Maps, Bing Maps, Google Maps, etc.). I can pan and zoom the map and draw shapes on top of it. The only thing I am struggling with is the polyline.

I could use another canvas and overlay the map, drawing the polyline there, but as with other versions (WPF, Silverlight, etc.) that I’ve developed, the SVG polyline works well when overlayed on the map. The user draws the polyline (which I use to measure distance), starting with a mouse click, with each subsequent mouse click adding a point in the polyline. A double-click finishes the operation.

My issue may be a TypeScript typings issue, as I can’t seem to create an SVGPoint object. Perhaps I am approaching it incorrectly. I have been able to work around the issue by using an existing point created in the initial HTML, appending it to the SVGPointList, getting its reference, and modifying the x and y properties of the new point. This is pretty ugly. There must be a way to create an SVGPoint using TypeScript in my Aurelia custom element.

I can put together a repro if you still want one, though it is really as simple as creating an SVGPoint object in code. If you still feel there is a better architectural way to approach this, that will provide equal performance while the user is drawing the polyline (the last point moves as the mouse moves, drawing the last polyline segment), do let me know.


#4

Use svg.createSVGPoint()

let point = svg.createSVGPoint();
point.x = 0; 
point.y = 0;
polyline.points.appendItem(point);

gist.run here: https://gist.run/?id=040775f06aba5e955afd362ee60863aa


#5

Right, that is something I tried. As I mentioned in my original post, I declared the svg element (distancePolylineSvg) as SVGElement. But there is no createSVGPoint() method on that type in TypeScript.

Have you tried it in TypeScript?


#6

Maybe the type definition is missing for that, probably because it’s not heavily use. You could extend the interface inside your app to fix that if that was the case.


#7

I’m sure that is the case. I thought maybe there were some typings I could load. If not, I’ll have to stick with my workaround or figure out how to extend the interface as you suggest.


#8

I’ve updated the gist.run with typings. https://gist.run/?id=040775f06aba5e955afd362ee60863aa

let svg: SVGSVGElement = <any>document.getElementById('svg');
let polyline: SVGPolylineElement = <any>svg.getElementById('polyline');
let point: SVGPoint = svg.createSVGPoint();
point.x = 0;
point.y = 0;
polyline.points.appendItem(point);

The SVG DOM API spec currently lacks a method for getting accurately typed SVG elements from an HTML DOM, and so there is no method for doing this in TypeScript. However, the actual objects returned from the DOM API are in fact of the right type. Therefore, you need to use an cast on your HTML DOM queries and then manually type them as SVG DOM elements.

Using Aurelia’s element.ref makes this look a little bit cleaner, though it’s basically doing the same thing for you.

Also shared this answer in StackOverflow here: https://stackoverflow.com/questions/52743698/svg-polyline-manipulation-in-typescript/52771302#52771302


#9

OK, I just needed to declare the SVG element as SVGSVGElement instead of SVGElement. So close. :slightly_frowning_face:.

Thanks for taking the time to help with this.

FYI. The link to your gist is incorrect in the last post.


#10

Accidentally overwrote it. Source here: https://gist.github.com/davismj/040775f06aba5e955afd362ee60863aa/6de4a43c06b7af29a171bc36f70793c3ea81c5a5