可以通过画布上的引用变量来单击函数

click functions through references variables on a canvas-possible?

本文关键字:变量 单击 函数 引用 可以通过      更新时间:2023-09-26

我当前的项目遇到了一些麻烦。因此,对于我的comp音乐课程,我们必须创建一个24键(2倍频程)键盘,首先使用画布渲染键盘,然后使用网络音频加载和播放24种不同的声音。我已经成功地将我的剪辑加载到了一个数组中(或者我希望如此!),但对于如何处理点击事件和播放每个声音,我有点困惑。在互联网上搜索只能通过找到点击的坐标并通过它执行某个事件来处理点击事件。这可能不适用于我的项目,因为我首先渲染白键(其中14个),然后在顶部渲染黑键(其中10个)。这将使通过坐标检测点击变得困难,因为白键不再是矩形。我渲染密钥的代码看起来像

function createKeys() {
var r = document.getElementById('piano');
var key = r.getContext('2d');
// creates white piano keys
key.beginPath();
for (i = 0; i < 14; i++)
{
wKey = [key.rect(i*70, 0, 70, 120)];
key.fillStyle = "#FFFFFF";
key.fill();
key.lineWidth = 2;
key.strokeStyle = 'black';
key.stroke();
}
key.closePath();
// begin black keys 
key.beginPath();
var bKey1 = key.rect(53, 0, 35, 80);
key.fillStyle = "#000000";
key.fill();
key.lineWidth= 2;
key.strokeStyle = '#888888';
key.stroke();
var bKey2 = key.rect(123, 0, 35, 80);
key.fillStyle = "#000000";
key.fill();
key.lineWidth= 2;
key.strokeStyle = '#888888';
key.stroke();
var bKey3 = key.rect(263, 0, 35, 80);
key.fillStyle = "#000000";
key.fill();
key.lineWidth= 2;
key.strokeStyle = '#888888';
key.stroke();
var bKey4 = key.rect(333, 0, 35, 80);
key.fillStyle = "#000000";
key.fill();
key.lineWidth= 2;
key.strokeStyle = '#888888';
key.stroke();
var bKey5 = key.rect(403, 0, 35, 80);
key.fillStyle = "#000000";
key.fill();
key.lineWidth= 2;
key.strokeStyle = '#888888';
key.stroke();
var bKey6 = key.rect(543, 0, 35, 80);
key.fillStyle = "#000000";
key.fill();
key.lineWidth= 2;
key.strokeStyle = '#888888';
key.stroke();
var bKey7 = key.rect(613, 0, 35, 80);
key.fillStyle = "#000000";
key.fill();
key.lineWidth= 2;
key.strokeStyle = '#888888';
key.stroke();
var bKey8 = key.rect(753, 0, 35, 80);
key.fillStyle = "#000000";
key.fill();
key.lineWidth= 2;
key.strokeStyle = '#888888';
key.stroke();
var bKey9 = key.rect(823, 0, 35, 80);
key.fillStyle = "#000000";
key.fill();
key.lineWidth= 2;
key.strokeStyle = '#888888';
key.stroke();
var bKey10 = key.rect(893, 0, 35, 80);
key.fillStyle = "#000000";
key.fill();
key.lineWidth= 2;
key.strokeStyle = '#888888';
key.stroke();
key.closePath();
}

现在,正如你所看到的,我为白键创建了一个参考变量,一个名为"wKey"的数组,索引为0到13(表示每个键),为黑键创建了单独的变量bKey1-10,因为找出这样一个模式的公式让我的大脑炸开了锅。我只是想知道我是否可以创建一个函数来检查这些引用是否被点击,而不是使用坐标跟踪来对每个键执行操作(如更改颜色和播放声音文件)。理想情况下,我想做一些类似的事情

 If wKey[i] = clicked
 then
 play soundfile[i]
 else if bKey1 = clicked
 play soundfile[14]
 else if bKey2 = clicked
 play soundfile[15]
 ... and so on

我不确定这是否可行,因为我从来没有在画布上乱搞过,更不用说在画布上执行功能了。我想听听一些新鲜人对此有什么看法。

编辑:由于我正在创建一个画布,我认为发布HTML可能是有益的

<body>
    <h1><u>The Cory Matthews "UNDAPANTS" Piano</u> by Chris C.</h1>
    <div id = "controls_toolbar">

    </div>
    <canvas id="piano" width = "980" height = "120" style ="border:1px solid #000000;" class="center" onclick ="keyClicked()"> </canvas>
        <script>
            const PATH = '/mp3/'
                  SOUNDS = ['DOWNUnderPantsC', 'DOWNUnderPantsD', 'DOWNUnderPantsE', 'DOWNUnderPantsF', 'DOWNUnderPantsG',
                  'DOWNUnderPantsA', 'DOWNUnderPantsB', 'UPUnderPantsC', 'UPUnderPantsD', 'UPUnderPantsE', 'UPUnderPantsF',
              'UPUnderPantsG', 'UPUnderPantsA', 'UPUnderPantsB', 'DOWNUnderPantsC#', 'DOWNUnderPantsD#', 'DOWNUnderPantsF#',
          'DOWNUnderPantsG#', 'DOWNUnderPantsA#', 'UPUnderPantsC#', 'UPUnderPantsD#', 'UPUnderPantsF#', 'UPUnderPantsG#', 'UPUnderPantsA#']
            createKeys();
            init();
            fetchSounds();
        </script>
    <p>Volume: <input type="range" min="0" max="100" value="100" oninput="VolumeSample.changeVolume(this);" /></p>
    <img class="center" src="images/cm.png" alt="UNDAPANTS" style="margin-top: 20px">
</body>

基本上,您必须为画布实现手动按钮解决方案。这并不像听起来那么复杂(顺便说一句,地图也是一种方式,但我不在这里介绍它)。

这个解决方案是动态的,这意味着你可以扩展八度音阶的数量、画布的大小等。所有的东西都会自动调整。

首先,让音符数组定义每个八度音阶中的键数,以及哪些键应该是黑色的;我们将使用#附录来确定:

var notes = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B'];
var keys = notes.length * octaves;

有关渲染的详细执行方式,请参阅以下来源。

使用白键的单独索引循环键的数量(因为黑键是相对的),我们得到了一个完整的钢琴键盘。我们将计算出的键的x位置存储在两个不同的数组中,一个用于黑键,另一个用于白键,这使得以后更容易进行命中测试(另一种选择是存储键形状-最多4个-并将其用作命中测试路径)。

当钢琴被渲染时,我们可以检查鼠标按下事件(如果你想在鼠标按下时拖动以播放不同的键,你需要在鼠标按下的时候设置一个向下标志,然后在鼠标移动事件中使用其他代码并进行一些优化-这里没有显示)。

// check for mouse down
canvas.addEventListener('mousedown', function(e) {
    // adjust mouse position
    var rect = canvas.getBoundingClientRect(),
        x = e.clientX - rect.left,
        y = e.clientY - rect.top,
        i, a;
    // fill color for key down
    ctx.fillStyle = '#fa2';
    //in blacks?
    for(i = 0; a = arrayBlacks[i++];) {
        ctx.beginPath();                               // start new path for test
        ctx.rect(a[0], 0, blackKeyWidth - 2, h * .67); // add a rect to path
        if (ctx.isPointInPath(x, y)) {                 // test if point is in path
            ctx.fill();                                // yes, fill it
            outputKey(a[1], a[2]);                     // show/play note
            return;
        }
    }
    //in whites? (same as above, but for arrayWhites)
    for(i = 0; a = arrayWhites[i++];) {
    ...cut... see full source
}, false);

当鼠标释放时,为了简单起见,我们只是重新渲染所有内容。对于较大的键盘(额外的八度音阶),您可能需要考虑按路径对象方法,其中每个键都存储为形状。

// if mouse up, re-render all
canvas.addEventListener('mouseup', function(e) {
    renderPiano(false);
}, false);

所有详细信息都可以在所附的实时片段中找到。希望这能有所帮助!

// some initial values/setup
var canvas = document.getElementById('piano'),  // get canvas
    ctx = canvas.getContext('2d'),              // get context
    h = canvas.height,                          // cache dimension
    w = canvas.width,
    
    octaves = 2,                                // octaves to render
    notes = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B'],
    keys = notes.length * octaves,              // pre-calc num of keys
    noteIndex = 0,                              // note index
    keyIndex = 0,                               // white key index
    note,                                       // note
    whiteKeyWidth = (w / (7 * octaves))|0,      // width of whites
    blackKeyWidth = whiteKeyWidth * 0.75,       // width of blacks
    i,                                          // iterator
    x,                                          // x pos of key
    
    arrayBlacks = [],                           // store black positions
    arrayWhites = []                            // store white positions
;
// calc piano key positions
for (i = 0; i < keys; i++) {
    noteIndex = i % notes.length;               // force within notes range
    note = notes[noteIndex];                    // get note name
    x = keyIndex * whiteKeyWidth;               // start pos. of key
    
    if (note.length === 1) {                    // white key (no sharp)
        arrayWhites.push([x, note, i]);         // store position
        keyIndex++;                             // next white key index
    }
    else {
        x -= blackKeyWidth * .5;                // adjust for black key
        arrayBlacks.push([x, note, i]);
    }
}
renderPiano(false);
// common render function based on whites/blacks arrays
// special switch: onlyBlacks to override white key rendered while down
function renderPiano(onlyBlacks) {
    var i, a;
    
    //render white keys
    if (!onlyBlacks) {
        ctx.clearRect(0, 0, w, h); // clear canvas for full render
        ctx.fillStyle = '#ffe';
        for(i = 0; a = arrayWhites[i++];) {
            ctx.fillRect(a[0], 0, whiteKeyWidth - 2, h - 1);
            ctx.strokeRect(a[0], 0, whiteKeyWidth - 2, h - 1);
        }
    }
  
    //render black keys
    ctx.fillStyle = '#000';
    for(i = 0; a = arrayBlacks[i++];) {
        ctx.fillRect(a[0], 0, blackKeyWidth - 2, h * .67);
    }
}
// check for mouse down
canvas.addEventListener('mousedown', function(e) {
    // adjust mouse position
    var rect = canvas.getBoundingClientRect(),
        x = e.clientX - rect.left,
        y = e.clientY - rect.top,
        i, a;
    ctx.fillStyle = '#fa2';
    //in blacks?
    for(i = 0; a = arrayBlacks[i++];) {
        ctx.beginPath();
        ctx.rect(a[0], 0, blackKeyWidth - 2, h * .67);
        if (ctx.isPointInPath(x, y)) {
            ctx.fill();
            outputKey(a[1], a[2]);
            return;
        }
    }
    //in whites?
    for(i = 0; a = arrayWhites[i++];) {
        ctx.beginPath();
        ctx.rect(a[0], 0, whiteKeyWidth - 2, h - 1);
        if (ctx.isPointInPath(x, y)) {
            ctx.fill();
            renderPiano(true);     // render black keys on top!
            outputKey(a[1], a[2]);
            return;
        }
    }
}, false);
// if mouse up, re-render all
canvas.addEventListener('mouseup', function(e) {
    renderPiano(false);
}, false);
// format output here, ie. play correct note etc.
function outputKey(key, index) {
    out.innerHTML = key + ((index / 12)|0);
    // play note
}
<canvas id="piano" width=540 height=160></canvas>
<br><output id="out"></output>