Y's note

Web技術・プロダクトマネジメント・そして経営について

本ブログの更新を停止しており、今後は下記Noteに記載していきます。
https://note.com/yutakikuchi/

html5のcanvasを使ってブラウザ上でのお絵描きやニコニコ動画風テロップを実装する

概要

html5canvasで遊んでみます。canvasの2dは学生の頃から使ってGoogleMap上にお絵描きできるシステムを作りました。またcanvasを巧く使えばニコニコ動画風のテロップも作れると思って今回実装してみました。次回は3dに挑戦したいです。

ブラウザ上でのお絵描き

仕組み

作り方は非常に簡単でhtmlにcanvasタグを埋め込み、それをJavascriptでdocument.getElementById( 'canvas' ).getContext( '2d' );とするだけでcanvasの2dオブジェクトが操作できます。mousedown,mousemove,mouseupのイベントを追加して、downしたら描画開始/moveしたら軌跡を残す/upしたら描画終了という処理の流れになります。当然の事ながらマウスのポイントを取得する必要がありevent.clientX/event.clientYで取得します。canvas上に軌跡を残すにはマウスポイントの一つ前の座標と新しい移動座標をからmoveTo、lineTo、strokeといったcanvasの2dオブジェクトが持っているメソッドを利用するだけです。jsも全部で50行ぐらいです。必要なことを箇条書きでも記します。

項目 簡易コード
canvas2dの取得 document.getElementById( 'canvas' ).getContext( '2d' );
マウスイベント追加 addEventListener( 'mousedown', Canvas.mousedown, false);
addEventListener( 'mousemove', Canvas.mousemove, false);
addEventListener( 'mouseup' , Canvas.mouseup , false);
マウスポイント取得 { x:e.clientX, y:e.clientY };
canvasに軌跡を書く var can = document.getElementById( 'canvas' ).getContext( '2d' );
can.beginPath();
can.moveTo(last.x, last.y);
can.lineTo(new.x, new.y);
can.closePath();
can.stroke();
ソースコード

html,javascriptともにgithubに上げました。

(function(){
    Canvas.init = function(){
        addEventListener( 'mousedown', Canvas.mousedown, false);
        addEventListener( 'mousemove', Canvas.mousemove, false);
        addEventListener( 'mouseup'  , Canvas.mouseup  , false);
    };

    Canvas.getObject = function() {
        return document.getElementById( 'canvas' ).getContext( '2d' );
    }

    Canvas.getPosition = function(e) {
        return { x:e.clientX, y:e.clientY };
    };

    Canvas.mousedown = function(e) {
        Canvas.attr.drawing = true;
        Canvas.attr.lastposition = Canvas.getPosition(e);
    };

    Canvas.mouseup = function(e) {
        Canvas.attr.drawing = false;
    };

    Canvas.mousemove = function(e) {
        if( Canvas.attr.drawing === false ) {
            return false;
        }
        Canvas.attr.position = Canvas.getPosition(e);
        var can = Canvas.attr.Object;
        can.beginPath();
        can.moveTo(Canvas.attr.lastposition.x, Canvas.attr.lastposition.y);
        can.lineTo(Canvas.attr.position.x, Canvas.attr.position.y);
        can.closePath();
        can.stroke();
        Canvas.attr.lastposition = Canvas.attr.position;
    };

    Canvas.attr = {};
    Canvas.attr.drawing = false;
    Canvas.attr.lastposition = { x:0, y:0 };
    Canvas.attr.position = { x:0, y:0 };
    Canvas.attr.Object = Canvas.getObject();
    Canvas.attr.Object.strokeStyle = 'black'; 
    Canvas.attr.Object.lineWidth = 10;
})();

ニコニコ動画風テロップ

仕組み

手書きの件よりも少し複雑で文字列の描画とその消去を繰り返す実装を行いました。描画と消去の呼び出しをsetIntervalで定期的に関数を呼び出すのですが、setInterval関数の第二引数の間隔が重要になります。短くした方が描画がスムーズで今回は0.025秒間隔で文字列再描画のx座標を減らしていきます。文字列をcanvas上に描画するためにfillStyleプロパティとfillTextメソッドを利用します。また消去するためにはclearRectメソッドにてcanvas上のデータ全部を削除します。箇条書きで項目を記載します。

項目 簡易コード
canvas2dの取得 document.getElementById( 'canvas' ).getContext( '2d' );
文字列描画 var can = document.getElementById( 'canvas' ).getContext( '2d' );
can.fillStyle = 'black';
can.fillText( text, text.start.x - text.offset , text.start.y );
文字列消去 var can = document.getElementById( 'canvas' ).getContext( '2d' );
can.fillStyle = 'white';
can.clearRect( 0, 0, 800, 800 );
ソースコード

html,javascriptともにgithubに上げました。

(function(){
    Nico.init = function() {
        if( Nico.attr.timer ) {
            clearInterval( Nico.attr.timer );
        }
        Nico.attr.timer = setInterval( 'Nico.play()', 25 );
    };

    Nico.getObject = function() {
        return document.getElementById( 'canvas' ).getContext( '2d' );
    };

    Nico.play = function() {
        Nico.remove();
        for( var i=0; i<=Nico.attr.text.length-1; i++ ) {
            var data = Nico.attr.text[i];
            Nico.attr.Object.fillStyle = 'black';
            Nico.attr.Object.fillText( data.node, data.start.x - data.offset , data.start.y );
            Nico.attr.text[i].offset += 7;
        }
    };

    Nico.remove = function() {
        Nico.attr.Object.fillStyle = 'white';
        Nico.attr.Object.clearRect( 0, 0, 800, 800 );
    };

    Nico.attr = {};
    Nico.attr.Object = Nico.getObject();
    Nico.attr.Object.font = '40pt Arial';
    Nico.attr.text = Array();
    for( var i = 0; i<=10; i++ ) {
        Nico.attr.text[i] = {};
        var prefix = '';
        for( var j = 0; j<=i; j++ ) {
            prefix += '    ';
        }
        Nico.attr.text[i].node = prefix + "(´;ω;`)";
        Nico.attr.text[i].start = { x:800, y:80*i };
        Nico.attr.text[i].offset = 0;
    }
})();