views:

41

answers:

1

Hi!

I'm trying to make a simple drawing tool using JS and Canvas element. My problem is that I would like to have several canvases and user should be able to draw one line through all of them. Here's a little page I did:

<!DOCTYPE html>
<html>
<head>

    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"&gt;&lt;/script&gt;

    <script type="text/javascript">

        var act = null;
        var context = null;
        var draw = false;
        var c = false;

        function boot() {
            $('.can')
                .mouseenter(function(){
                    act = this;
                    context = act.getContext('2d');
                   // console.log(this);

                })
                .mouseleave(function(){
                    act = null;
                    context = null;
               //     console.log('out');
                })
                .mousedown(function(){
                    draw = true;
                })
                .mouseup(function(){
                    draw = false;
                })
                .mousemove(function(ev){

                //    console.log(act);
                      if (ev.layerX || ev.layerX == 0) { // Firefox
                        x = ev.layerX;
                        y = ev.layerY;
                      } else if (ev.offsetX || ev.offsetX == 0) { // Opera
                        x = ev.offsetX;
                        y = ev.offsetY;
                      }

                    if(draw && context != null)
                      if (!c) {
                        context.beginPath();
                        context.moveTo(x, y);
                        c = true;
                      } else {
                        context.lineTo(x, y);
                        context.stroke();
                      }
                });
        }

        $(document).ready(boot); 
    </script>

    <style>
        .can {border: 1px solid blue; display:block; float:left; margin:0;}
    </style>

</head>

<body>
    <canvas class="can" id="c2" width="200" height="200"></canvas>
    <canvas class="can" id="c1" width="200" height="200"></canvas>
    <canvas class="can" id="c3" width="200" height="200"></canvas>
</body>
</html>

And it partially works: I can draw only in the first canvas. I debugged it and i got really confused, because the context changes as expected and drawing is enabled only in the first canvas.

Any ideas what's the cause of such behavior?

+1  A: 

OK, I've found the source of the problem. The original is here:

http://jsfiddle.net/CVFv5/4/

The fixed version is here:

http://jsfiddle.net/CVFv5/3/

Basically, the issue was that you weren't calculating your X and Y vars correctly. .offsetX and .offsetY calculate the x and y position relative to the element's direct ancestor (which in this case was the body of the page). You can see this by alerting the x and y values on mouseover. Anyway, what you have to do instead is this:

var o = $(this).offset(),
    x = (ev.pageX - o.left),
    y = (ev.pageY - o.top);

There were also a few other problems with your code that I've changed. First of all, you weren't beginning new paths for each canvas, so when you re-entered the canvas, it would lineTo(x, y) from where the line had left off. In order to solve this, I made your mouseout event look like this:

.mouseout(function() {
    c=false;
})

Now it will begin a new path whenever it enters a new canvas.

The third thing I changed was to make it so that it only creates the contexts once on document ready. I imagine this saves a little bit of processing. So I added this line in the global scope:

var contexts = [];

And this method in your $('.can') method chain:

.each(function(el) {
    id = this.id;
    contexts[id] = this.getContext('2d');
})

So everywhere else in your code, you can reference it like this:

contexts[this.id].beginPath();

And now it works.

treeface
Thanks! Clever solution :)
nooga