Question

I am trying to write a simple freehand drawing app using Kinetic/Paper/Fabric JS. However I've noticed that all examples of freehand drawing with these libraries are not responsive at all.

By responsive I mean that KineticJS/PaperJS does not update the line being drawn as I drag the mouse, the way it is with jSignature.

Is there any way around this problem?

Was it helpful?

Solution

Here is an example using KineticJS: http://jsfiddle.net/m1erickson/EsFSg/

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Prototype</title>
    <script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
    <script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.7.2.min.js"></script>

<style>
#container{
  border:solid 1px #ccc;
  margin-top: 10px;
}
</style>        
<script>
$(function(){

        // create a stage and a layer
        var stage = new Kinetic.Stage({
            container: 'container',
            width: 350,
            height: 350
        });
        var layer = new Kinetic.Layer();
        stage.add(layer);

        // an empty stage does not emit mouse-events
        // so fill the stage with a background rectangle
        // that can emit mouse-events
        var background = new Kinetic.Rect({
            x: 0,
            y: 0,
            width: stage.getWidth(),
            height: stage.getHeight(),
            fill: 'white',
            stroke: 'black',
            strokeWidth: 1,
        })        
        layer.add(background);
        layer.draw();

        // a flag we use to see if we're dragging the mouse
        var isMouseDown=false;
        // a reference to the line we are currently drawing
        var newline;
        // a reference to the array of points making newline
        var points=[];

        // on the background
        // listen for mousedown, mouseup and mousemove events
        background.on('mousedown', function(){onMousedown();});
        background.on('mouseup', function(){onMouseup();});
        background.on('mousemove', function(){onMousemove();});

        // On mousedown
        // Set the isMouseDown flag to true
        // Create a new line,
        // Clear the points array for new points
        // set newline reference to the newly created line
        function onMousedown(event) {
            isMouseDown = true;
            points=[];
            points.push(stage.getMousePosition());
            var line = new Kinetic.Line({
                points: points,
                stroke: "green",
                strokeWidth: 5,
                lineCap: 'round',
                lineJoin: 'round'
            });
            layer.add(line);
            newline=line;
        }

        // on mouseup end the line by clearing the isMouseDown flag
        function onMouseup(event) {
            isMouseDown=false;
        }

        // on mousemove
        // Add the current mouse position to the points[] array
        // Update newline to include all points in points[]
        // and redraw the layer
        function onMousemove(event) {
            if(!isMouseDown){return;};
            points.push(stage.getMousePosition());
            newline.setPoints(points);
            // use layer.drawScene
            // This avoids unnecessarily updating the hit canva
            layer.drawScene();
        }


}); // end $(function(){});

</script>       
</head>

<body>
    <div id="container"></div>
</body>
</html>

[ Addition to answer: further optimization ]

Further performance involves de-linking mousepoint capture from drawing.

In mousemove: Only capture the mouse position--don't try to draw anything in mousemove.

  • accumulatedPointsArray.push({x:mouseX,y:mouseY});

You might even consider ignoring some mousepoints because fewer points still draws a fairly good polyline. Maybe just save every third incoming mousepoint.

To draw: set up an animation loop that:

  • Adds the accumulated points to the Kinetic.Line--myLine.setPoints(accumulatedPointsArray)

  • Does one single myLine.draw().

To squeeze out every bit of performance, consider using Kinetic.Shape instead of a Kinetic.Line to display the polyline as it’s being created by the user. Kinetic.Shape gives you a canvas context to work with so it’s “closer to the metal” and gives better drawing performance versus the “managed” Kinetic.Line. When the user is done defining their line, you could move those accumulated points into a Kinetic.Line and hide the Kinetic.Shape—best of both worlds.

Anyway, good luck with your project!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top