nickf
nickf

Reputation: 546015

Save a reference to a html canvas path

I'm working on some code which is drawing to a canvas. One part of the code draws some lines onto the canvas. The position and colour of those lines don't change, but they often need to be redrawn because other code may have affected it (eg: drawn over the top of it).

There can be several hundred lines to draw, and in these cases, profiling shows me that it's taking ~200ms to draw, so I'm looking to optimise this somewhat.

One thing I noticed was that when drawing to the canvas, you basically are adding points to a path and then once ready, you can fill or stroke that path. Though the pixels on the canvas are out of date, if I were able to keep a reference to the path, then updating would be as simple as re-stroking the previously constructed path.

My question is: how on earth do you get a Path object?

The fill and stroke methods appear to accept a path object, and the spec defines the methods for Path, but I can't seem to find the actual Path class anywhere...

So, just to recap:

I have something like this:

function update() {
  context.beginPath();
  // lots of lines added to the default path...
  context.moveTo(x1, y1); context.lineTo(somewhere, else);
  context.moveTo(x2, y2); context.lineTo(somewhere, else);

  context.stroke();
}

What I'd like is something like this:

function update() {
  if (!this.path) {
    this.path = new Path(); // <-- here's the magic
    this.path.moveTo(x1, y2); this.path.lineTo(somewhere, else); // etc
  }
  this.path.stroke();
}

Upvotes: 7

Views: 3207

Answers (4)

markE
markE

Reputation: 105015

The canvas spec calls for a Path object that is not implemented in browsers yet.

BTW, when implemented, the Path object will be useful in hit-testing when combined with context.isPointInPath(myPath); Someday...

Here's how you could create your own Path object until the browsers catch up:

  • Create a JS object that contains a canvas where your path strokes are drawn.
  • When you want to do myPath.stroke(), use myVisibleContext.drawImage(myPath.context,0,0) to "blit" the path's canvas onto your drawing canvas.

Demo: http://jsfiddle.net/m1erickson/QLJv8/

Code:

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; }
    #canvas{border:1px solid red;}
</style>

<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");

    function Path(maxWidth,maxHeight,color,linewidth,drawingContext){
        this.width=maxWidth;
        this.height=maxHeight;
        this.drawingCtx=drawingContext;
        this.points=[]
        this.canvas=document.createElement("canvas");
        this.canvas.width=maxWidth;
        this.canvas.height=maxHeight;
        this.ctx=this.canvas.getContext("2d");
        this.ctx.strokeStyle=color;
        this.ctx.lineWidth=linewidth;
        this.lastX;
        this.lastY;
    }
    Path.prototype.moveTo=function(x,y){
        this.lastX=x;
        this.lastY=y;

    }
    Path.prototype.lineTo=function(x,y){
        this.ctx.moveTo(this.lastX,this.lastY);
        this.ctx.lineTo(x,y);
        this.ctx.stroke();
        this.lastX=x;
        this.lastY=y;
    }
    Path.prototype.stroke=function(){
        this.drawingCtx.drawImage(this.canvas,0,0);
    }

    // create a new path object

    var p=new Path(300,300,"blue",2,ctx);

    // set the Path's drawing commands

    p.moveTo(69,91);
    p.lineTo(250,150);
    p.moveTo(69,208);
    p.lineTo(180,54);
    p.lineTo(180,245);
    p.lineTo(69,91);
    p.moveTo(69,208);
    p.lineTo(250,150);

    // draw the Path.canvas to the drawing canvas
    p.stroke();

    // tests...

    $("#stroke").click(function(){
        p.stroke();
    });
    $("#erase").click(function(){
        ctx.clearRect(0,0,canvas.width,canvas.height);
    });

}); // end $(function(){});
</script>

</head>

<body>
    <button id="stroke">Path.stroke</button><br>
    <button id="erase">Erase main canvas</button><br>
    <canvas id="canvas" width=300 height=300></canvas>
</body>
</html>

Upvotes: 1

Patrick Brosset
Patrick Brosset

Reputation: 94

None of the canvas drawing API let you hold references to objects. Canvas lets you draw pixels in a bitmap, not create and manipulate objects like SVG does.

If you're looking to optimize performance and you want to reuse the same path over and over again, you might want to draw it once in a separate canvas oject, and then draw that canvas into your other canvas using drawImage (which can take a canvas as argument).

Upvotes: 0

Anas
Anas

Reputation: 731

Their is no path support in canvas, but why don't use svg line and set its zIndex to be on top of others.

Upvotes: 0

nickf
nickf

Reputation: 546015

Turns out, it's just that no browser supports it yet, according to this blog (dated 24th January 2013) http://www.rgraph.net/blog/2013/january/html5-canvas-path-objects.html

Upvotes: 1

Related Questions