通过画布上的引用变量单击函数可能吗?

时间:2014-10-12 22:37:20

标签: javascript canvas onclick web-audio

用我当前的项目碰到一点墙。因此,对于我的复合音乐课程,我们必须创建一个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>

1 个答案:

答案 0 :(得分:1)

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

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

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

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>