Help us understand the problem. What is going on with this article?

Canvasイベント操作まとめ

More than 3 years have passed since last update.

はじめに

前回Canvasアニメーションの要点という記事を投稿しました。
今回はCanvasでのイベント操作について調べたものをまとめます。

Canvasアニメーションの要点
http://qiita.com/nekoneko-wanwan/items/33afa5d20264c83b2bd1

今回のイベント範囲

ここで取り上げるイベントの種類は以下です。
(ひとまずtouchイベントは置いておきます...)

  • click
  • double click
  • mouse over / out
  • mouse down / up
  • mouse move
  • mouse wheel
  • key up / down

なおそれぞれのコードについては個人的にポイントと思う箇所のみを記述します。実際の動き(全てのコード)は↓ページで確認ができます
http://nekoneko-wanwan.github.io/demo/canvas/event/

サンプルコード

イベント登録は以下のように設定します。
※IE8以前は、addEventListener()に未対応ですが、ここでは考慮していません

sample.js
function action() {
    var canvas = document.getElementById('e_click');
    var context = canvas.getContext('2d');

    function eventHandler() {
        draw();
    }

    function draw() {
        // 描画処理
        context.fillRect(...);
    }

    /* canvas要素に対してイベントを設定 */
    canvas.addEventListener('event', eventHandler, false);
}
action();

各イベントの要点

clickイベント

  • クリックした座標の取得には、getBoundingClientRect()とclientX / Yの取得が必要

anim_click.gif

click.js
var x = 0;
var y = 0;
function onClick(e) {
    /*
     * rectでcanvasの絶対座標位置を取得し、
     * クリック座標であるe.clientX,e.clientYからその分を引く
     * ※クリック座標はdocumentからの位置を返すため
     * ※rectはスクロール量によって値が変わるので、onClick()内でつど定義
     */
    var rect = e.target.getBoundingClientRect();
    x = e.clientX - rect.left;
    y = e.clientY - rect.top;

    draw();
}

function draw() {
    // 描画処理
    ctx.fillRect(x, y, 10, 10);
}

canvas.addEventListener('click', onClick, false);

double clickイベント

  • clickイベントとほぼ同じなので省略

mouse over / out イベント(Canvas全体に対する)

  • canvasに対してmouseover / out のイベントを設定するだけ

anim_over-out.gif

mouseover-out.js
function onMouseOver() {
    draw();
}
function onMouseOut() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
}
function draw() {
    // 描画処理
}
canvas.addEventListener('mouseover', onMouseOver, false);
canvas.addEventListener('mouseout', onMouseOut, false);

mouse over / out イベント(Canvas内要素に対する)

  • Canvasは1画像のため、そのままmouse overでは中の要素のみを対象とはできない
  • つど要素との当たり判定を行う、別途Canvasを用意する、など一工夫が必要(のハズ...)
  • ここでは当たり判定を採用

anim_over-out2.gif

mouseover-out2.js
var targetFlag = false; // trueでマウスが要素に乗っているとみなす
var rect = null;

/* Canvas上にマウスが乗った時 */
function onMouseOver(e) {
    rect = e.target.getBoundingClientRect();
    canvas.addEventListener('mousemove', onMouseMove, false);
}
/* Canvasからマウスが離れた時 */
function onMouseOut() {
    canvas.removeEventListener('mousemove', onMouseMove, false);
}
/* Canvas上でマウスが動いている時 */
function onMouseMove(e) {
    /* マウスが動く度に要素上に乗っているかかどうかをチェック */
    moveActions.updateTargetFlag(e);

    /* 実行する関数には、間引きを噛ませる */
    if (targetFlag) {
        moveActions.throttle(moveActions.over, 50);
    } else {
        moveActions.throttle(moveActions.out, 50);
    }
}

/* mouseMoveで実行する関数 */
var moveActions = {
    timer: null,
    /* targetFlagの更新 */
    updateTargetFlag: function(e) {
        var x = e.clientX - rect.left;
        var y = e.clientY - rect.top;

        /* 最後の50は、該当する要素の半サイズを想定 */
        var a = (x > w / 2 - 50);
        var b = (x < w / 2 + 50);
        var c = (y > h / 2 - 50);
        var d = (y < h / 2 + 50);

        targetFlag = (a && b && c && d); // booleanを代入
    },
    /* 連続イベントの間引き */
    throttle: function(targetFunc, time) {
        var _time = time || 100;
        clearTimeout(this.timer);
        this.timer = setTimeout(function () {
            targetFunc();
        }, _time);
    },
    out: function() {
        drawRect();
    },
    over: function() {
        drawRectIsHover();
    }
};

function drawRect(color) {
    // デフォルトもしくはマウスが要素から離れた時の描画処理
}
function drawRectIsHover() {
    // マウスが要素に乗った時の描画処理
}

canvas.addEventListener('mouseover', onMouseOver, false);
canvas.addEventListener('mouseout', onMouseOut, false);

drawRect();

mouse down / up イベント

  • mouse downとclickとの違いは、すぐに発動するかどうか
  • down: 押した時
  • click: 離れた時

anim_down-up.gif

mousedown-up.js
function onMouseDown() {
    drawOn();
}
function onMouseUp() {
    drawOff();
}
function drawOn() {
    // マウスが押された時の描画処理
}
function drawOff() {
    // デフォルトもしくはマウスが離れた時の描画処理
}

canvas.addEventListener('mousedown', onMouseDown, false);
canvas.addEventListener('mouseup', onMouseUp, false);

drawOff();

mouse move イベント

  • mousemoveは連続してイベントが発生するので、重たい処理を行うときには間引きを行った方が良い(即時性を求めない場合)

anim_move.gif

mousemove.js
var x = 0;
var y = 0;
var timer;

/*
 * 連続イベント間引き処理
 * @param {obj} targetFunc 処理したい関数
 * @param {num} time 何ミリ秒以内の処理をスキップするか
 */
function throttle(targetFunc, time) {
    var _time = time || 100;
    clearTimeout(this.timer);
    this.timer = setTimeout(function () {
        targetFunc();
    }, _time);
}

function onMouseMove(e) {
    draw(e);
}

function draw(e) {
    throttle(function() {
        // 描画処理
    }, 100);
}

canvas.addEventListener('mousemove', onMouseMove, false);

mouse wheelイベント

  • canvas内にマウスが乗っている時に発動する模様
  • 特に難しいものは無いのでコード・画像は省略

key up / downイベント

  • canvasに対してこれらのイベント設定するには、canvasにtabindexを付与する必要がある
  • documentにイベント設定すれば動作はするが、canvasにfocusが当たってない時も反応してしまう

使っているキャプチャソフトだと上手く取れなかったので画像は省略します...

keyup-down.js
/* キーが押された時 */
function onKeyDown(e) {
    // e.keyCodeによって処理を変えると良いかと
    var str = e.keyCode;
    draw(str);
}
/* キーが離れた時 */
function onKeyUp(e) {
    var str = 'キーから離れました';
    draw(str);
}
function draw(str) {
    // 描画処理
}

canvas.setAttribute('tabindex', 0); // focusしている時のみ、keyDown,up を有効に
canvas.addEventListener('keydown', onKeyDown, false);
canvas.addEventListener('keyup', onKeyUp, false);

おわりに

まだまだ分かっていないことばかりなので、不正確なところがあればコメントや編集リクエストをお願いします

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした