駄プログラム日記

自分用のプログラムをいろいろ書いてるのなかで、気がついたことを記しています。

2011.06.01
HTML5のcanvas objectを使う、其の参

いろいろbugがあるが、まとめて整理するので一部だけ修正。 スクロールしていると、マウス座標の値がかわるので、その補正。

function mouseDownListener(e) {
	// 移動する中心点を計算
	var moveX =( e.clientX - viewInfo.absCenterX + document.body.scrollLeft );
	var moveY =( e.clientY - viewInfo.absCenterY + document.body.scrollTop );
2011.05.28
HTML5のcanvas objectを使う

サーバ上に保管してある写真をみるためのビューアを作りたくで、HTML5を使った画像表示ページを作成中。 最終的には、AJAX化する予定であるが、とりあえず画像の拡大・縮小・回転・移動ができるようになった。 回転は、excanvasではサポートしていないため、chromeでしか動作確認をできていない。 いろいろなところを参考にしたため、冗長な記述もあるかと思う。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta http-equiv="Content-Language" content="ja" />
    <meta http-equiv="Content-Style-Type" content="text/css" />
    <meta http-equiv="Content-Script-Type" content="text/javascript" />
    <title>Photo Image Viewer</title>
    <!--[if IE]><script type="text/javascript" src="/common/js/excanvas.js"></script><![endif]-->
    <script type="text/javascript">

    var MyCanvas;
    var ConMode;
    var MyCanvasRect;
    
    var debugInfo;
    var photo = document.createElement('img');

    var zoomLevel = new Array(1, 1.4, 2, 2.8, 4, 5.6, 8, 11.2, 16);
    var zoomCurrent = 0;
    var angle = 0;
    var canvasWidth = 600;
    var canvasHeight = 600;
    var photoInfo = Object();
    var viewInfo = Object();


    //ページがロードされてから必要な初期処理を行う(んだが、何かイベントが起きないと画像が表示されない)。
    onload = function() {
        //デバック用エレメントの取得
        debugInfo = document.getElementById("debug_info1"); // debug時にdebugInfo.innerTextで書き出すため。通常不要。
        
        //キャンバスオブジェクトへの参照をセット
        MyCanvas = document.getElementById("DrawField");
        MyCanvas.onmousedown = mouseDownListener;           //  mousedown event listener関数を設定
        MyCanvasRect = MyCanvas.getBoundingClientRect();    //  Canvas objectのサイズ

        
        photo.src = "/image.jpg?" + new Date().getTime();    //  AJAX化したときは、動的に与える予定。Dateはキャッシュさせないため。
        photoInfo.width = 3872;                                                 // AJAX化の暁には、EXIF情報で埋め込む予定。
        photoInfo.height = 2592                                                 //  ditto.

        viewInfo.width = canvasWidth;                                       //  canvas上に表示するイメージの幅
        viewInfo.height = canvasWidth/photoInfo.width*photoInfo.height;     //  canvas上に表示するイメージの高さ

        //  canvasの中心で回転させたいので座標系を移動するため。
        viewInfo.offsetX = -viewInfo.width/2;       viewInfo.offsetY = -viewInfo.height/2;

        //  canvasの絶対座標(マウスイベントのため)
        viewInfo.absCenterX = (MyCanvasRect.left + MyCanvasRect.right)/2;
        viewInfo.absCenterY = (MyCanvasRect.top + MyCanvasRect.bottom)/2;
        
        //キャンバスの大きさを変更する(特に必要はないが動的に変更する場合を想定)
        MyCanvas.setAttribute('width', canvasWidth);
        MyCanvas.setAttribute('height', canvasHeight);

        //2次元描画用コンテキストを設定
        ConMode = MyCanvas.getContext('2d');

        RestView();

    };

    //マウスボタンを押した場所を中心にするように画像を移動。
    function mouseDownListener(e) {
        // 移動する中心点を計算
        var moveX =( e.clientX - viewInfo.absCenterX );
        var moveY =( e.clientY - viewInfo.absCenterY );

        //表示された画像を消す。多少範囲を大きめに消さないと、ごみが残る。
        ConMode.clearRect(-MyCanvas.width/2-5, -MyCanvas.height/2-5 ,MyCanvas.width+5, MyCanvas.height+5);

        // 中心位置の変更。座標系が変換されているでそれを反映。
        photoInfo.offsetX = photoInfo.offsetX + moveX*Math.cos(Math.PI*angle/180) + moveY*Math.sin(Math.PI*angle/180);
        photoInfo.offsetY = photoInfo.offsetY + moveY*Math.cos(Math.PI*angle/180) - moveX*Math.sin(Math.PI*angle/180);
        
        if ( photoInfo.offsetX < 0 ) photoInfo.offsetX = 0;     if ( photoInfo.offsetY < 0 ) photoInfo.offsetY = 0;
        if ( photoInfo.offsetX + photoInfo.viewWidth  > photoInfo.width ) photoInfo.offsetX = photoInfo.width  - photoInfo.viewWidth;
        if ( photoInfo.offsetY + photoInfo.viewHeight > photoInfo.height) photoInfo.offsetY = photoInfo.height - photoInfo.viewHeight;

        ConMode.drawImage(photo,photoInfo.offsetX,photoInfo.offsetY,photoInfo.viewWidth,photoInfo.viewHeight,
                                viewInfo.offsetX,viewInfo.offsetY,viewInfo.width,viewInfo.height);
    }

    //  画像回転。回転させた角度を保管する処理。その後はRoteteView()をそのまま呼びだす。
    function Rotate(rot,direction) {
        angle = (angle + rot * direction) % 360;
        RotateView(rot,direction);
    }

    //回転を行う処理。回転中を表現するため、タイマーで再帰的に繰り返す。
    function RotateView(rot,direction){

        if (rot == 0) {
            return;
        } else {
            //表示された画像を消す。多少範囲を大きめに消さないと、ごみが残る。
            ConMode.clearRect(-MyCanvas.width/2-5, -MyCanvas.height/2-5 ,MyCanvas.width+5, MyCanvas.height+5);

            //座標を回転させる
            ConMode.rotate(5 * direction * Math.PI / 180);

            //画像の中心がキャンバス座標の中心点にくるように画像を表示する
            ConMode.drawImage(photo,photoInfo.offsetX,photoInfo.offsetY,photoInfo.viewWidth,photoInfo.viewHeight,
                                    viewInfo.offsetX,viewInfo.offsetY,viewInfo.width,viewInfo.height);

            //10ミリ秒ごとに、5度づつ座標の回転と画像の描画を繰り返す
            if ( rot > 0 ) {
                rot = rot - 5
            } else {
                rot + 5;
            }
            setTimeout(function(){RotateView(rot,direction)},10);
        }
    }

    //画像を拡大表示する処理
    function ResizeView(zoom){

        //表示された画像を消す。多少範囲を大きめに消さないと、ごみが残る。
        ConMode.clearRect(-MyCanvas.width/2-5, -MyCanvas.height/2-5 ,MyCanvas.width+5, MyCanvas.height+5);

        zoomCurrent += zoom;
        if ( zoomCurrent < 0 || zoomCurrent >= zoomLevel.length ) {
            zoomCurrent -= zoom;
        }
        //画像の大きさを変更する。
        photoInfo.viewWidth = photoInfo.width / zoomLevel[zoomCurrent];
        photoInfo.viewHeight = photoInfo.height / zoomLevel[zoomCurrent];

        //表示画像の中心から拡大するように、オフセットを計算。
        photoInfo.offsetX =  photoInfo.offsetX + ( viewWidth  - photoInfo.viewWidth )/2;
        photoInfo.offsetY =  photoInfo.offsetY + ( viewHeight - photoInfo.viewHeight)/2;
        
        if ( photoInfo.offsetX < 0 ) photoInfo.offsetX = 0;      if ( photoInfo.offsetY < 0 ) photoInfo.offsetY = 0;
        if ( photoInfo.offsetX + photoInfo.viewWidth  > photoInfo.width ) photoInfo.offsetX = photoInfo.width  - photoInfo.viewWidth;
        if ( photoInfo.offsetY + photoInfo.viewHeight > photoInfo.height) photoInfo.offsetY = photoInfo.height - photoInfo.viewHeight;

        ConMode.drawImage(photo,photoInfo.offsetX,photoInfo.offsetY,photoInfo.viewWidth,photoInfo.viewHeight,
                                viewInfo.offsetX,viewInfo.offsetY,viewInfo.width,viewInfo.height);
    }

    //画像をリセットする
    function ResetView(){

        //色々なパラメータをもとに戻す
        photoInfo.offsetX = 0;      photoInfo.offsetY = 0;
        photoInfo.viewWidth = photoInfo.width;
        photoInfo.viewHeight = photoInfo.height;
        viewInfo.width = canvasWidth;
        viewInfo.height = canvasWidth/photoInfo.width*photoInfo.height;
        zoomCurrent = 0;
        angle = 0;

        //表示された画像を消す。多少範囲を大きめに消さないと、ごみが残る。
        ConMode.clearRect(-MyCanvas.width/2-5, -MyCanvas.height/2-5 ,MyCanvas.width+5, MyCanvas.height+5);
        //変換マトリックスをリセットする。
        ConMode.setTransform(1,0,0,1,MyCanvas.width/2 ,MyCanvas.height/2);
        //画像の中心がキャンバス座標の中心点にくるように画像を表示する
        ConMode.drawImage(photo,photoInfo.offsetX,photoInfo.offsetY,photoInfo.viewWidth,photoInfo.viewHeight,
                                viewInfo.offsetX,viewInfo.offsetY,viewInfo.width,viewInfo.height);
    }

</script>
</head>
<body>
    <div class="main_frame">
        <canvas id="DrawField" width="600" height="600" class="pframe"></canvas>
    </div>
    <div class="sidebar">
    </div>
    <div class="reset">
        <p>
        <form>
            <input id= "Anticlockwise" type=button value="左回転" onClick="Rotate(90,-1)" />
            <input id= "Clockwise" type=button value="右回転" onClick="Rotate(90,1)" />
            <input id= "Shrink" type=button value="縮小" onClick="ResizeView(-1)" />
            <input id= "Expand" type=button value="拡大" onClick="ResizeView(1)" />
            <input id= "Reset" type=button value="リセット" onClick="ResetView()" />
        </form>
        </p>
    </div>
    <p id="debug_info1"></p>
</body>
</html>