Search code examples
javascripthtml5-canvasmouseeventtouch-event

Need to convert Mouse events to Touch Events for mobile using HTML Canvas


Basically I want the mouseevents above to work on mobile through the use of touchevents. There is some code in there to change the color on each click as well. I 'm hoping that it's as easy as binding the touchevents to the mouseevents, but in my trial and error I still can't get it to work.

Any help with this would be amazing!

Here is the code that i'm working with:

  var canvas = document.createElement('canvas');
document.body.appendChild(canvas);

// some hotfixes... ( ≖_≖)
document.body.style.margin = 0;
canvas.style.position = 'relative';

// get canvas 2D context and set him correct size
var ctx = canvas.getContext('2d');
resize();

// last known position
var pos = { x: 0, y: 0 };

const cvs = document.getElementById("canvas");
cvs.addEventListener('mousedown', getRandomInt);

window.addEventListener('resize', resize);
document.addEventListener('mousemove',  draw);
document.addEventListener('mousedown',  setPosition);
document.addEventListener('mouseenter',  setPosition);


function getRandomInt() {
  window.randInt = Math.floor(Math.random() * Math.floor(3));
}

// new position from mouse event
function setPosition(e) {
  pos.x = e.clientX;
  pos.y = e.clientY;
}

// resize canvas
function resize() {
  ctx.canvas.width = window.innerWidth;
  ctx.canvas.height = window.innerHeight;
}

function draw(e) {
  // mouse left button must be pressed
  if (e.buttons !== 1) return;

  var color = '';

  switch (window.randInt) {
    case 1: 
      color = 'green';
      break;
    case 2: 
      color = 'red';
      break;
    case 0:
      color = 'blue';
      break;
  }

  ctx.beginPath(); // begin

  ctx.lineWidth = 3;
  ctx.lineCap = 'round';
  ctx.strokeStyle = color;

  ctx.moveTo(pos.x, pos.y); // from
  setPosition(e);
  ctx.lineTo(pos.x, pos.y); // to

  ctx.stroke(); // draw it!
}


Solution

  • Actually it is as easy as listening for both - touch and mouse - events. The touchstart event is the mousedown, the touchmove the mousemove and lastly touchend is the mouseup equivalent.

    Theoretically you could simply add the same callback functions to all of these listeners. The only problem is you can't directly query the 'mouse' position using the event.clientX property inside the callback function. That's because there can be multiple touches while there is always just a single mouse event. To keep track of the touches, those are stored in a TouchList - more or less an array - which is the .touches property of the event.

    So we need to make a distinction between a mouse and a touch event inside the callback function:

    • If it's a touch use event.touches[0].clientX
    • If it's a mouse event use event.clientX

    Here's an example based on your code:

    var canvas = document.createElement('canvas');
    canvas.id = "canvas";
    document.body.appendChild(canvas);
    
    document.body.style.margin = 0;
    canvas.style.position = 'relative';
    
    var ctx = canvas.getContext('2d');
    resize();
    
    var pos = {
      x: 0,
      y: 0
    };
    var buttonDown = false;
    
    const cvs = document.getElementById("canvas");
    cvs.addEventListener('mousedown', getRandomInt);
    cvs.addEventListener('touchstart', getRandomInt);
    
    window.addEventListener('resize', resize);
    
    document.addEventListener('mousemove', draw);
    document.addEventListener('mousedown', setPosition);
    document.addEventListener('mouseup', released);
    
    document.addEventListener('touchstart', setPosition);
    document.addEventListener('touchmove', draw);
    document.addEventListener('touchend', released);
    
    function getRandomInt() {
      window.randInt = Math.floor(Math.random() * Math.floor(3));
    }
    
    function released(e) {
      buttonDown = false;
    }
    
    function setPosition(e) {
      if (e.type == "touchstart" || e.type == "mousedown") {
        buttonDown = true;
      }
      if (e.type == "touchstart" || e.type == "touchmove") {
        pos.x = e.touches[0].clientX;
        pos.y = e.touches[0].clientY;
      } else {
        pos.x = e.clientX;
        pos.y = e.clientY;
      }
    }
    
    function resize() {
      ctx.canvas.width = window.innerWidth;
      ctx.canvas.height = window.innerHeight;
    }
    
    function draw(e) {
      if (!buttonDown) return;
    
      var color = '';
    
      switch (window.randInt) {
        case 1:
          color = 'green';
          break;
        case 2:
          color = 'red';
          break;
        case 0:
          color = 'blue';
          break;
      }
    
      ctx.beginPath();
    
      ctx.lineWidth = 3;
      ctx.lineCap = 'round';
      ctx.strokeStyle = color;
    
      ctx.moveTo(pos.x, pos.y);
      setPosition(e);
      ctx.lineTo(pos.x, pos.y);
    
      ctx.stroke();
    }