HTML5画布的多点触摸

Multi-touch for HTML5 Canvas

本文关键字:多点 触摸 HTML5      更新时间:2023-09-26

我正在用HTML5画布创建一种乐器,在将多点触摸功能集成到代码中时遇到了问题。我想让用户能够一次播放多个键。我是整个HTML5概念的新手,并将感谢在此过程中的任何帮助。

我发现了一些对我有帮助的JS文件,比如hammer.JS和magictouch.JS。然而,我不知道如何将它们用于我自己的代码中。

在这种情况下,我试图在我的钢琴中实现多点触摸,它是由画布上的一个函数绘制的。如何开始多点触摸?如何将其与画布一起使用?

        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)};
            }               
    }  

要支持某种多点触摸功能,您必须处理触摸事件touchstart touchmove touchend touchcancel(如果您想支持IE,还必须处理指针事件)。触摸事件具有changedTouches属性。读取此属性中添加或删除的触摸并存储该信息。

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();
}

棘手的事件是touchmove,因为事件目标仍然是相应的touchstart事件的目标。在你的情况下,当手指从一个键移动到另一个键时,你必须计算下一个钢琴键。

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();
}

钢琴示例:

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>