简体   繁体   中英

Multi-touch for HTML5 Canvas

I am creating a musical instrument with HTML5 canvas and I have problem integrating multi-touch functionality into my code. I want to make it so that users will be able to play more than one key at a time. I am new to the whole HTML5 concept and will appreciate any help along the way.

I came across some JS files that would help me, like hammer.js and magictouch.js. However, I do not know how to use them into my own code.

In this case, I am trying to implement multi-touch into my piano which is drawn by a function on a canvas. How do I go about starting to do multi-touch? How do I use it with a canvas?

        var button = document.getElementById("Draw");
        button.onclick = function(){
            drawOnCanvas();
        }
        function drawOnCanvas(){        
        var canvas = document.getElementById("main");

        if (canvas.getContext){
            var cxt = canvas.getContext("2d");
            <!-- alert("2D context supported"); -->

                cxt.beginPath();
                cxt.moveTo(20, 20);
                cxt.lineTo(60, 20);
                cxt.moveTo(60, 20);
                cxt.lineTo(60, 160);
                cxt.moveTo(60, 160);
                cxt.lineTo(75, 160);
                cxt.moveTo(75, 160);
                cxt.lineTo(75, 230);
                cxt.moveTo(75, 230);
                cxt.lineTo(20, 230);
                cxt.moveTo(20, 20);
                cxt.lineTo(20, 230);
                cxt.stroke();

                <!-- Key note C -->

                cxt.rect(60, 20, 30, 140);
                cxt.fillStyle = 'black';
                cxt.shadowColor = '#999';
                cxt.shadowBlur = 20;
                cxt.shadowOffsetX = 15;
                cxt.shadowOffsetY = 15;
                cxt.fill();
                cxt.stroke();

                <!-- Key note C# -->

                cxt.beginPath();
                cxt.moveTo(90, 20);
                cxt.lineTo(120, 20);
                cxt.moveTo(90, 20);
                cxt.lineTo(90, 160);
                cxt.moveTo(90, 160);
                cxt.lineTo(75, 160);
                cxt.moveTo(75, 160);
                cxt.lineTo(75, 230);
                cxt.moveTo(60, 230);
                cxt.lineTo(60, 230);
                cxt.moveTo(75, 230);
                cxt.lineTo(135, 230);
                cxt.moveTo(135, 230);
                cxt.lineTo(135, 160);
                cxt.moveTo(135, 160);
                cxt.lineTo(120, 160);
                cxt.moveTo(120, 160);
                cxt.lineTo(120, 20);
                cxt.stroke();

                <!-- Key note D -->

                cxt.beginPath();
                cxt.rect(120, 20, 30, 140);
                cxt.fillStyle = 'black';
                cxt.fill();
                cxt.stroke();

                <!-- Key note D# -->

                cxt.beginPath();
                cxt.moveTo(150, 20);
                cxt.lineTo(190, 20);
                cxt.moveTo(150, 20);
                cxt.lineTo(150, 160);
                cxt.moveTo(150, 160);
                cxt.lineTo(135, 160);
                cxt.moveTo(135, 160);
                cxt.lineTo(135, 230);
                cxt.moveTo(135, 230);
                cxt.lineTo(190, 230);
                cxt.moveTo(190, 230);
                cxt.lineTo(190, 20);
                cxt.moveTo(125, 230);
                cxt.stroke(); 

                <!-- Key note E -->

                cxt.beginPath();
                cxt.moveTo(190, 20);
                cxt.lineTo(230, 20);
                cxt.moveTo(190, 20);
                cxt.lineTo(190, 230);
                cxt.moveTo(230, 20);
                cxt.lineTo(230, 160);
                cxt.moveTo(230, 160);
                cxt.lineTo(245, 160);
                cxt.moveTo(245, 160);
                cxt.lineTo(245, 230);
                cxt.moveTo(245, 230);
                cxt.lineTo(190, 230);
                cxt.stroke();

                <!-- Key note F -->

                cxt.beginPath();
                cxt.rect(230, 20, 30, 140);
                cxt.fillStyle = 'black';
                cxt.fill();
                cxt.stroke();

                <!-- Key note F# --> 

                cxt.beginPath();
                cxt.moveTo(260, 20);
                cxt.lineTo(290, 20);
                cxt.moveTo(290, 20);
                cxt.lineTo(290, 160);
                cxt.moveTo(290, 160);
                cxt.lineTo(305, 160);
                cxt.moveTo(305, 160);
                cxt.lineTo(305, 230);
                cxt.moveTo(305, 230);
                cxt.lineTo(245, 230);
                cxt.moveTo(245, 230);
                cxt.lineTo(245, 160);
                cxt.moveTo(245, 160);
                cxt.lineTo(260, 160);
                cxt.moveTo(260, 160);
                cxt.lineTo(260, 20);
                cxt.stroke();

                <!-- Key note G -->

                cxt.beginPath();
                cxt.rect(290, 20, 30, 140);
                cxt.fillStyle = 'black';
                cxt.fill();
                cxt.stroke();

                <!-- Key note G# -->

                cxt.beginPath();
                cxt.moveTo(320, 20);
                cxt.lineTo(350, 20);
                cxt.moveTo(350, 20);
                cxt.lineTo(350, 160);
                cxt.moveTo(350, 160);
                cxt.lineTo(365, 160);
                cxt.moveTo(365, 160);
                cxt.lineTo(365, 230);
                cxt.moveTo(365, 230);
                cxt.lineTo(305, 230);
                cxt.moveTo(305, 230);
                cxt.lineTo(305, 160);
                cxt.moveTo(305, 160);
                cxt.lineTo(320, 160);
                cxt.moveTo(320, 160);
                cxt.lineTo(320, 20);
                cxt.stroke();

                <!-- Key note A -->

                cxt.beginPath();
                cxt.rect(350, 20, 30, 140);
                cxt.fillStyle = 'black';
                cxt.fill();
                cxt.stroke();

                <!-- Key note A# -->

                cxt.beginPath();
                cxt.moveTo(380, 20);
                cxt.lineTo(420, 20);
                cxt.moveTo(420, 20);
                cxt.lineTo(420, 160);
                cxt.moveTo(420, 160);
                cxt.lineTo(420, 160);
                cxt.moveTo(420, 160);
                cxt.lineTo(420, 230);
                cxt.moveTo(420, 230);
                cxt.lineTo(365, 230);
                cxt.moveTo(365, 230);
                cxt.lineTo(365, 160);
                cxt.moveTo(365, 160);
                cxt.lineTo(380, 160);
                cxt.moveTo(380, 160);
                cxt.lineTo(380, 20);
                cxt.stroke();

                <!-- Key note B --> 

                cxt.beginPath();
                cxt.moveTo(420, 20);
                cxt.lineTo(460, 20);
                cxt.moveTo(460, 20);
                cxt.lineTo(460, 160);
                cxt.moveTo(460, 160);
                cxt.lineTo(475, 160);
                cxt.moveTo(475, 160);
                cxt.lineTo(475, 230);
                cxt.moveTo(475, 230);
                cxt.lineTo(420, 230);
                cxt.moveTo(420, 230);
                cxt.lineTo(420, 20);
                cxt.stroke();

                <!-- Key note C' -->

                cxt.beginPath();
                cxt.rect(460, 20, 30, 140);
                cxt.fillStyle = 'black';
                cxt.fill();
                cxt.stroke();

                <!-- Key note C#' -->

                cxt.beginPath();
                cxt.moveTo(490, 20);
                cxt.lineTo(520, 20);
                cxt.moveTo(520, 20);
                cxt.lineTo(520, 160);
                cxt.moveTo(520, 160);
                cxt.lineTo(535, 160);
                cxt.moveTo(535, 160);
                cxt.lineTo(535, 230);
                cxt.moveTo(535, 230);
                cxt.lineTo(475, 230);
                cxt.moveTo(475, 230);
                cxt.lineTo(475, 160);
                cxt.moveTo(475, 160);
                cxt.lineTo(490, 160);
                cxt.moveTo(490, 160);
                cxt.lineTo(490, 20);
                cxt.stroke();

                <!-- Key note D' -->

                cxt.beginPath();
                cxt.rect(520, 20, 30, 140);
                cxt.fillStyle = 'black';
                cxt.fill();
                cxt.stroke();

                <!-- Key note D#' -->

                cxt.beginPath();
                cxt.moveTo(550, 20);
                cxt.lineTo(595, 20);
                cxt.moveTo(595, 20);
                cxt.lineTo(595, 160);
                cxt.moveTo(595, 160);
                cxt.lineTo(595, 160);
                cxt.moveTo(595, 160);
                cxt.lineTo(595, 230);
                cxt.moveTo(595, 230);
                cxt.lineTo(535, 230);
                cxt.moveTo(535, 230);
                cxt.lineTo(535, 160);
                cxt.moveTo(535, 160);
                cxt.lineTo(550, 160);
                cxt.moveTo(550, 160);
                cxt.lineTo(550, 20);
                cxt.stroke();
            }

        canvas.addEventListener("mousedown", function(evt) {
                    var mousePos = getMousePos(canvas, evt);
                    var message = "Mouse Position :  " + mousePos.x + ", " + mousePos.y;
                    writeMessage(canvas, message);
                    playSong(mousePos.x, mousePos.y);
                }, false); 

        function playSong(xpos, ypos) {

            if ((xpos > '40' && xpos < '79' && ypos > '40' && ypos < '180') || (xpos > '40' && xpos < '97' && ypos > '180' && ypos < '250'))
                {
                document.getElementById("c_note").play();
                }
                else if (xpos > '80' && xpos < '110' && ypos > '40' && ypos < '180') 
                {
                document.getElementById("csharp_note").play();
                }
                else if ((xpos > '111' && xpos < '140' && ypos > '40' && ypos < '180') || (xpos > '97' && xpos < '155' && ypos > '180' && ypos < '250'))
                {
                document.getElementById("d_note").play();           
                }
                else if (xpos > '141' && xpos < '171' && ypos > '40' && ypos < '180') 
                {
                document.getElementById("dsharp_note").play();
                }
                else if ((xpos > '172' && xpos < '210' && ypos > '40' && ypos < '180') || (xpos > '156' && xpos < '210' && ypos > '180' && ypos < '250'))
                {
                document.getElementById("e_note").play();           
                }
                else if ((xpos > '211' && xpos < '250' && ypos > '40' && ypos < '180') || (xpos > '210' && xpos < '265' && ypos > '180' && ypos < '250'))
                {
                document.getElementById("f_note").play();           
                }
                else if (xpos > '251' && xpos < '281' && ypos > '40' && ypos < '180') 
                {
                document.getElementById("fsharp_note").play();
                }
                else if ((xpos > '282' && xpos < '310' && ypos > '40' && ypos < '180') || (xpos > '266' && xpos < '325' && ypos > '180' && ypos < '250'))
                {
                document.getElementById("g_note").play();           
                }
                else if (xpos > '311' && xpos < '341' && ypos > '40' && ypos < '180')
                {
                document.getElementById("gsharp_note").play();          
                }
                else if ((xpos > '342' && xpos < '370' && ypos > '40' && ypos < '180') || (xpos > '326' && xpos < '385' && ypos > '180' && ypos < '250'))
                {
                document.getElementById("a_note").play();           
                }
                else if (xpos > '371' && xpos < '399' && ypos > '40' && ypos < '180')
                {
                document.getElementById("asharp_note").play();          
                }
                else if ((xpos > '400' && xpos < '440' && ypos > '40' && ypos < '180') || (xpos > '385' && xpos < '440' && ypos > '180' && ypos < '250'))
                {
                document.getElementById("b_note").play();           
                }
                else if ((xpos > '441' && xpos < '480' && ypos > '40' && ypos < '180') || (xpos > '441' && xpos < '495' && ypos > '180' && ypos < '250'))
                {
                document.getElementById("c2_note").play();          
                }
                else if (xpos > '481' && xpos < '510' && ypos > '40' && ypos < '180')
                {
                document.getElementById("csharp2_note").play();         
                }
                else if ((xpos > '511' && xpos < '540' && ypos > '40' && ypos < '180') || (xpos > '496' && xpos < '555' && ypos > '180' && ypos < '250'))
                {
                document.getElementById("d2_note").play();          
                }
                else if (xpos > '541' && xpos < '571' && ypos > '40' && ypos < '180')
                {
                document.getElementById("dsharp2_note").play();         
                }
                else if ((xpos > '572' && xpos < '615' && ypos > '40' && ypos < '180') || (xpos > '556' && xpos < '615' && ypos > '180' && ypos < '250'))
                {
                document.getElementById("e2_note").play();          
                }
                else if ((xpos > '616' && xpos < '654' && ypos > '40' && ypos < '180') || (xpos > '616' && xpos < '675' && ypos > '180' && ypos < '250'))
                {
                document.getElementById("f2_note").play();          
                }
                else if (xpos > '656' && xpos < '686' && ypos > '40' && ypos < '180')
                {
                document.getElementById("fsharp2_note").play();         
                }
                else if ((xpos > '687' && xpos < '714' && ypos > '40' && ypos < '180') || (xpos > '676' && xpos < '730' && ypos > '180' && ypos < '250'))
                {
                document.getElementById("g2_note").play();          
                }
                else if (xpos > '715' && xpos < '745' && ypos > '40' && ypos < '180')
                {
                document.getElementById("gsharp2_note").play();         
                }
                else if ((xpos > '746' && xpos < '775' && ypos > '40' && ypos < '180') || (xpos > '731' && xpos < '790' && ypos > '180' && ypos < '250'))
                {
                document.getElementById("a2_note").play();          
                }
                else if (xpos > '776' && xpos < '805' && ypos > '40' && ypos < '180')
                {
                document.getElementById("asharp2_note").play();         
                }
                else if ((xpos > '806' && xpos < '845' && ypos > '40' && ypos < '180') || (xpos > '791' && xpos < '845' && ypos > '180' && ypos < '250'))
                {
                document.getElementById("b2_note").play();          
                }

                }

        function writeMessage(canvas, message) {
                cxt.font = "18pt Calibri";
                cxt.fillStyle = "black";
                cxt.clearRect(0, 0, 300, 50);
                cxt.fillText(message, 10, 20);
            }

        function getMousePos(canvas, evt) {
                var rect = canvas.getBoundingClientRect();
                return {x: (evt.pageX + rect.left), y: (evt.pageY + rect.top)};
            }               
    }  

To support some kind of muli-touch feature, you have to handle the touch events touchstart touchmove touchend touchcancel (and pointer events if you want to support IE). The touch events have a changedTouches property. Read added or removed touches from this property and store that information.

function touchStart(evt) {
  var changedTouches = evt.changedTouches;   
  for (var i = 0; i < changedTouches.length; i++) {
    // remember new touches and its key
    var key = changedTouches[i].target;
    touches.push({ id : changedTouches[i].identifier, key: key });
  }
  updateKeys();
}

function touchEnd(evt) {
  var changedTouches = evt.changedTouches;
  for (var i = 0; i < changedTouches.length; i++) {
    // Remove this touch
    var index = getTouchIndex(changedTouches[i].identifier);
    if (index >= 0) {
        touches.splice(index, 1);
    }  
  }
  updateKeys();
}

The tricky event is touchmove , since the event target is still the target of the corresponding touchstart event. In your case, you have to calculate the next piano key, when a finger moves from one key to another.

function touchMove(evt) {
  var changedTouches = evt.changedTouches;
  for (var i = 0; i < changedTouches.length; i++) {
    var touch = changedTouches[i];
    var index = getTouchIndex(touch.identifier);
    if (index >= 0) {
      // Update stored key to the new key
      var key = document.elementFromPoint(touch.pageX, touch.pageY);
      if (isKey(key))
        touches[index].key = key;
    }      
  }
  updateKeys();
}

The piano example:

 var keys = [].slice.call(document.querySelectorAll('.key'), 0); var keyboard = document.getElementById('keyboard'); var touches = []; keyboard.addEventListener("touchstart", touchStart, false); keyboard.addEventListener("touchmove", touchMove, false); keyboard.addEventListener("touchend", touchEnd, false); function isKey(key) { return keys.indexOf(key) >= 0; } function updateKeys() { keys.forEach(function(key) { key.classList.remove("down"); }); touches.forEach(function(touch) { if (isKey(touch.key)) touch.key.classList.add("down"); }); } function touchStart(evt) { evt.preventDefault(); var changedTouches = evt.changedTouches; for (var i = 0; i < changedTouches.length; i++) { var key = changedTouches[i].target; touches.push({ id : changedTouches[i].identifier, key: key }); } updateKeys(); } function touchEnd(evt) { evt.preventDefault(); var changedTouches = evt.changedTouches; for (var i = 0; i < changedTouches.length; i++) { var index = getTouchIndex(changedTouches[i].identifier); if (index >= 0) { touches.splice(index, 1); } } updateKeys(); } function touchMove(evt) { evt.preventDefault(); var changedTouches = evt.changedTouches; for (var i = 0; i < changedTouches.length; i++) { var touch = changedTouches[i]; var index = getTouchIndex(touch.identifier); if (index >= 0) { var key = document.elementFromPoint(touch.pageX, touch.pageY); if (isKey(key)) touches[index].key = key; } } updateKeys(); } function getTouchIndex(id) { for (var i = 0; i < touches.length; i++) { if (touches[i].id === id) { return i; } } return -1; } 
 #keyboard { background:#333; padding:10px; } .key { height:150px; width:35px; display:inline-block; background: #aee; } .key.down { background: #aae; } 
 <div id="keyboard"> <div class="key"></div> <div class="key"></div> <div class="key"></div> <div class="key"></div> <div class="key"></div> <div class="key"></div> <div class="key"></div> <div class="key"></div> <div class="key"></div> <div class="key"></div> </div> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM