0
votes

I am making a simple drag to draw script on canvas. The thing is that when I am drawing a line to connect the points of the mouse positions, the canvas draws extra lines from some random points, but always from the initial point.

Here's an example to see the problem: http://sktch.io/988111

I took the data captured in one such mouse move, and manually went through drawing each point, and i can confirm that the issue doesn't get solved. What makes me annoyed is that if I manually do moveTo and lineTo for some arbitrary points, the code works. It's got some issue with mouse drag points. As far as I can tell there isn't an issue with the points collected.

Any pointers are appreciated. I have already tried various modifications such as, beginPath, moveto, lineto, and stroke in each single loop iteration, or beginpath, and in the loop - moveto, lineto, and then stroke outside the loop. Same results.

The problematic code is included at the bottom.

var App = (function() {
    var canvas, currentLayer, paint = false, context, socket, room, currentTool = 'line',
    keysrt = function(key,desc) {
        return function(a,b){
            return desc ? ~~(a[key] < b[key]) : ~~(a[key] > b[key]);
        }
    },
    reDraw = function() {
        if (context) {
            context.clearRect(0, 0, canvas.offsetWidth, canvas.offsetHeight);
            Object.keys(layers).sort().forEach(function(key) {
                let layer = layers[key];
                switch(layer.getTool()) {
                    case 'line':
                        var data = layer.getData().sort(keysrt('time'));
                        if(data.length > 1) {
                            for(var i = 1; i < data.length; i++) {
                                context.beginPath();
                                context.moveTo(data[i-1]['value'][0], data[i-1]['value'][1]);
                                context.lineTo(data[i]['value'][0], data[i]['value'][1]);
                                context.stroke();
                            }
                        }
                        break;
                    case 'eraser':
                        var data = layer.getData().sort(keysrt('time'));
                        if(data.length > 0) {
                            for(var i = 0; i < data.length; i++) {
                                context.clearRect(data[i][0], data[i][1], 4, 4);
                            }
                        }
                        break;
                    case 'clear':
                        context.clearRect(0, 0, canvas.offsetWidth, canvas.offsetHeight);
                        break;
                }
            });
        }
    },
    Layer = function() {
        var data = [], tool, id = new Date().getTime();
        return {
            push: function(time, value) {
                data.push({time: time, value: value})
            },
            toJSON: function() {
                return {
                    data: data,
                    tool: tool,
                    id: id
                }
            },
            setTool: function(t) {
                tool = t;
            },
            getTool: function() {
                return tool;
            },
            getData: function() {
                return data;
            },
            getId: function() {
                return id;
            },
            setId: function(i) {
                id = i;
            },
            setData: function(d) {
                data = d;
            }
        };
    },
    layers = {},
    events = 0;
    press = function (e) {
        var mouseX = (e.changedTouches ? e.changedTouches[0].pageX : e.pageX) - this.offsetLeft, mouseY = (e.changedTouches ? e.changedTouches[0].pageY : e.pageY) - this.offsetTop;
        // add a layer
        paint = true;
        currentLayer = Layer();
        currentLayer.setTool(currentTool);
        layers[currentLayer.getId()] = currentLayer;
        events = 1;
    },
    drag = function (e) {
        var mouseX = (e.changedTouches ? e.changedTouches[0].pageX : e.pageX) - this.offsetLeft, mouseY = (e.changedTouches ? e.changedTouches[0].pageY : e.pageY) - this.offsetTop;
        if (paint) {
            events = events + 1;
            currentLayer.push(new Date().getTime() * 10 + events, [mouseX, mouseY]);
            reDraw()
        }
        e.preventDefault();
    },
    release = function () {
        paint = false;
        if(currentLayer) {
            events = 0;
            reDraw();
        }
        currentLayer = null;
    },
    init = function(id, appendTo, width, height) {
        room = id;
        canvas = document.createElement('canvas');
        canvas.setAttribute('width', width);
        canvas.setAttribute('height', height);
        canvas.setAttribute('id', 'canvas');
        appendTo.appendChild(canvas);

        context = canvas.getContext("2d");

        canvas.addEventListener("mousedown", press, false);
        canvas.addEventListener("mousemove", drag, false);
        canvas.addEventListener("mouseup", release);
        canvas.addEventListener("mouseout", release, false);

        // Add touch event listeners to canvas element
        canvas.addEventListener("touchstart", press, false);
        canvas.addEventListener("touchmove", drag, false);
        canvas.addEventListener("touchend", release, false);
        canvas.addEventListener("touchcancel", release, false);

        document.getElementById('clear').addEventListener("click", function() {
            layer = Layer();
            layer.setTool('clear');
            layers[layer.getId()] = layer;
            reDraw();
        }, false);
    }
    return {
        init: init
    }
})();
1
You are missing context.clearRect(0,0,canvasWidth,canvasHeight);Rayon
@Rayon It is there. in the reDraw method. line number 10.Amit
@Kaiido clicking and dragging in the box on the included link is the stackoverflow.com/help/mcveAmit

1 Answers

0
votes

The bug lies in your sorting function, which only returns 0 or 1.

So changing your keysrt to a more readable

keysrt = function(key,desc) {
    return function(a,b){
        if(desc){
            if(a[key] < b[key]) return 1;
            if(a[key] > b[key]) return -1;
            return 0;
            }
        else{
            if(a[key] < b[key]) return -1;
            if(a[key] > b[key]) return 1;
            return 0;
            }               
    }

fixes the issue :

var App = (function() {
  var canvas, currentLayer, paint = false,
    context, socket, room, currentTool = 'line',
    keysrt = function(key, desc) {
      return function(a, b) {
        if (desc) {
          if (a[key] < b[key]) return 1;
          if (a[key] > b[key]) return -1;
          return 0;
        } else {
          if (a[key] < b[key]) return -1;
          if (a[key] > b[key]) return 1;
          return 0;
        }
      }
    },
    reDraw = function() {
      if (context) {
        context.clearRect(0, 0, canvas.offsetWidth, canvas.offsetHeight);
        Object.keys(layers).sort().forEach(function(key) {
          let layer = layers[key];
          switch (layer.getTool()) {
            case 'line':
              var data = layer.getData().sort(keysrt('time'));
              if (data.length > 1) {
                for (var i = 1; i < data.length; i++) {
                  context.beginPath();
                  context.moveTo(data[i - 1]['value'][0], data[i - 1]['value'][1]);
                  context.lineTo(data[i]['value'][0], data[i]['value'][1]);
                  context.stroke();
                }
              }
              break;
            case 'eraser':
              var data = layer.getData().sort(keysrt('time'));
              if (data.length > 0) {
                for (var i = 0; i < data.length; i++) {
                  context.clearRect(data[i][0], data[i][1], 4, 4);
                }
              }
              break;
            case 'clear':
              context.clearRect(0, 0, canvas.offsetWidth, canvas.offsetHeight);
              break;
          }
        });
      }
    },
    Layer = function() {
      var data = [],
        tool, id = new Date().getTime();
      return {
        push: function(time, value) {
          data.push({
            time: time,
            value: value
          })
        },
        toJSON: function() {
          return {
            data: data,
            tool: tool,
            id: id
          }
        },
        setTool: function(t) {
          tool = t;
        },
        getTool: function() {
          return tool;
        },
        getData: function() {
          return data;
        },
        getId: function() {
          return id;
        },
        setId: function(i) {
          id = i;
        },
        setData: function(d) {
          data = d;
        }
      };
    },
    layers = {},
    events = 0;
  press = function(e) {
      var mouseX = (e.changedTouches ? e.changedTouches[0].pageX : e.pageX) - this.offsetLeft,
        mouseY = (e.changedTouches ? e.changedTouches[0].pageY : e.pageY) - this.offsetTop;
      // add a layer
      paint = true;
      currentLayer = Layer();
      currentLayer.setTool(currentTool);
      layers[currentLayer.getId()] = currentLayer;
      events = 1;
    },
    drag = function(e) {
      var mouseX = (e.changedTouches ? e.changedTouches[0].pageX : e.pageX) - this.offsetLeft,
        mouseY = (e.changedTouches ? e.changedTouches[0].pageY : e.pageY) - this.offsetTop;
      if (paint) {
        events = events + 1;
        currentLayer.push(new Date().getTime() * 10 + events, [mouseX, mouseY]);
        reDraw()
      }
      e.preventDefault();
    },
    release = function() {
      paint = false;
      if (currentLayer) {
        events = 0;
        reDraw();
      }
      currentLayer = null;
    },
    init = function(id, appendTo, width, height) {
      room = id;
      canvas = document.createElement('canvas');
      canvas.setAttribute('width', width);
      canvas.setAttribute('height', height);
      canvas.setAttribute('id', 'canvas');
      appendTo.appendChild(canvas);

      context = canvas.getContext("2d");

      canvas.addEventListener("mousedown", press, false);
      canvas.addEventListener("mousemove", drag, false);
      canvas.addEventListener("mouseup", release);
      canvas.addEventListener("mouseout", release, false);

      // Add touch event listeners to canvas element
      canvas.addEventListener("touchstart", press, false);
      canvas.addEventListener("touchmove", drag, false);
      canvas.addEventListener("touchend", release, false);
      canvas.addEventListener("touchcancel", release, false);

    }
  return {
    init: init
  }

})();
App.init('', document.body, 800, 800)