このサイトでは、分析、カスタマイズされたコンテンツ、および広告に Cookie を使用します。このサイトを引き続き閲覧すると、Cookie の使用に同意するものと見なされます。
Hi, Developers,
straightapps.com ロゴ
作成 November 18, 2022
トップページ > Web 開発トップ > 画像を自由に拡大・縮小してもらう
line
Web 開発
line

Canvas に描画した画像をスライダー操作でズームします。

※ 表示が正しくないと思う場合は、JavaScript を有効にしてください。

縮小 拡大
スライダー操作で、上の画像は Canvas 内で、下の画像は Canvas ごと、画像サイズを調整できます。初期状態に戻すにはページをリロードしてください。


自由に拡大・縮小率を指定できなくても良い場合は、CSS だけで、初期表示サイズと原寸をタップ(クリック)で切り替えることもできます。 原寸表示になっても再度タップ(クリック)すれば、元のサイズに戻せます。


現時点でのテスト済みブラウザは、

  • Windows 10 PC の Edge, Chrome, FireFox
  • Android 9 スマホの Chrome, FireFox, Edge
  • です。

    スマホやタブレットで開く場合は、 この QR コード ( URL の QR コード
    https://www.straightapps.com/web/js-expand-and-shrink.html
    )
    が、このページの URL になっています。 QR コード読み取りアプリで読み取ると便利です。

    ▲ページ先頭へ

    ここから先は開発情報です

    投稿 November 18, 2022

    ここから先は、上記、スライダーによる canvas 描画画像の拡大・縮小を行った際に調べたりした JavaScript コードについての情報です。

    まずは、HTML 部です。

    スライダーと JavaScript を使う、最初の 2 画像の部分の HTML コードです。

    <p>
    <canvas id="canvas-main"></canvas>
    </p>
    
    <p>
    縮小
    <input type="range" id="zoom-slider" style="width: 240px;">
    拡大<br>
    <span class="sz80p red">スライダー操作で、上の画像は Canvas 内で・・・</span>
    </p>
    
    <p>
    <canvas id="canvas-sub"></canvas>
    </p>
    
    <script type="text/javascript" src="js/expand-and-shrink.js"></script>
    

    canvas id="canvas-main" の部分は、canvas-main という名前を持った canvas の定義です。 ここに 1 つ目の画像、すなわち固定サイズの canvas に拡大・縮小された画像を描画するものです。

    次の段落にある input type="range" の部分は、zoom-slider という名前を付けたスライダーです。 width: 240px; のスタイル指定により、サイズを自由に決めることができます。 これを省略すると、デフォルトのサイズで描画されます。

    スライダーの次の行にある span で指定されている sz80p ( CSS 定義
    .sz80p { font-size:80%; }
    )
    は小さい文字、 red ( CSS 定義
    .red { color:red; }
    )
    はテキストから―赤で、あらかじめ別に指定している CSS を参照しています。 テキスト部分は省略しています。

    canvas id="canvas-sub" の部分は第 2 のキャンバス canvas-sub で、こちらはキャンバスごとサイズ変更してみています。

    そして最後の script で、js/expand-and-shrink.js を読み込んでいます。 初期化の部分で canvas-main 等を参照していますので、それらの定義よりあとに記述されている必要があります。

    以降のセクションで、それぞれ詳細を確認していきます。

    なお、ご参考までに、JavaScript ソースコードそのものを、拡張子 js を拡張子 txt に変えて、次のリンクより開けるようにしてあります。
    expand-and-shrink.txt
    ※ 作成時はタブを 4 文字としていますので、環境によってはタブが 8 文字のため、コメント等がずれて見えるかも知れません。
    ※ 念のため書き添えますが、このソースコードをこのまま転載・公開することはご遠慮ください。

    ▼ セクション一覧

    ページ読み込み時の処理
    イメージの読み込み完了を待つ
    スライダー機能を実現する
    CSS で画像サイズを切り替える

    なお、本サイトのご利用に際しては、必ずプライバシーポリシー(免責事項等)をご参照ください。

    ▲ページ先頭へ

    ページ読み込み時の処理

    投稿 November 18, 2022

    ページ読み込み時に実行される JavaScript コードを追っていきます。 スライダー機能を実現するための addEventListener 設定については、別途見ていきます。

    まずは 2 つのキャンバスの準備です。

    var canvas = document.getElementById('canvas-main');
    
    // 基本となる canvas のサイズ(単位:ピクセル)を指定します。
    var w = 360;
    var h = 270;
    
    // これより画面横サイズが狭い場合は、縦横比を保ったまま小さくします。
    if ( w > document.documentElement.clientWidth ){
    	w = document.documentElement.clientWidth;
    	h = w * 270 / 360;
    }
    
    // canvas のサイズを指定します。
    canvas.width = w;
    canvas.height = h;
    

    まずは HTML で定義した canvas-main と名付けた canvas を探し、変数 canvas に設定します。

    この canvas のサイズを横 360 ピクセル、縦 270 ピクセルになるよう、変数 w と h に設定しています。 このサイズであれば該当する可能性はまずありませんが、万一画面の横方向が 360 ピクセルより狭い場合を想定し、 document.documentElement.clientWidth で画面サイズを調べ、 収まらないようならそのサイズを最大にし、縦横比を保つように、高さ h も更新します。 そして canvas に設定し、サイズを決定しています。

    続けて canvas ごと拡大・縮小するサブキャンバスを準備します。

    var canvasSub = document.getElementById('canvas-sub');
    
    canvasSub.width = w;
    canvasSub.height = h;
    

    HTML で定義した canvas-sub を探し、変数 canvasSub に設定します。 サイズは 1 つ目のキャンバスと同じにします。

    var ctx = canvas.getContext('2d');
    var ctxSub = canvasSub.getContext('2d');
    

    描画のためのコンテキストを、それぞれ ctxctxSub に設定し、あとで使います。

    スライダーを準備します。

    const slider = document.getElementById('zoom-slider');
    slider.min = 1;			// 最小値(デフォルト値 0)
    slider.max = 255;		// 最大値(デフォルト値 100)
    slider.value = 128;		// 初期値(min/max 指定の後で設定すること)
    slider.step = 'any';		// 粒度(デフォルト値 1、any 指定でなめらかに変更可能)
    

    HTML で定義した、input type="range" で表されるスライダー zoom-slider を探し、変数 slider に設定しています。 なお、type で指定している range がスライダーです。 他に指定できる文字列とその例は、 「入力欄(フォーム入力)要素」 を参照するとよさそうです。

    プロパティを設定します。

    minmax が、それぞれスライダーの最小値、最大値となります。 デフォルト値は 0 と 100 ですから、特に変更しなくてもいいのですが、1 から 255 に設定してみました。

    value現在指している値ですが、ここでは初期値になります。 ここでは中央となる 128 にしています。

    最後に step、「刻み」です。 any を指定すると「なめらかに変更可能」となり、 指している値 value を取得するときに、実数(小数)で値が返されることになります。 省略すると 1 刻みになるようですので、整数値しか返されなくなります。 5% 刻みにしたい場合は 5 を指定するなど、お好みの値を指定すると希望の刻みに調整できます。

    ▲ページ先頭へ

    イメージの読み込み完了を待つ

    投稿 November 18, 2022

    表示する画像イメージを読み込みますが、注意が必要です。

    const myimg = new Image();
    myimg.src = 'jsimg/sf.jpg';
    

    mying に新しい Image オブジェクトを作成し、jsimg サブフォルダにある sf.jpg を読み込んでいます。

    var imgW = 0;
    var imgH = 0;
    

    画像のサイズを保持するべく、変数 imgWimgH を用意しています。 値 0 を設定していますが、ここで画像サイズを取得して、myimg.widthmyimg.height を設定するほうが良さそうにも見えます。

    が、うまくいきませんでした。

    この時点ではまだ画像が読み込み完了していないため、どちらも値が 0 になってしまいました。 ローカルでテストしていてこうですから、実際にサーバーに画像を置いたら、当然同じような結果になるでしょう。 ちなみに画像がキャッシュされると正しい値が返される可能性がありますが、それは危険だと思います。

    そこで、次のように画像データの読み込みを待ちます(正しくは、読み込みが完了したら呼び出される関数を仕込んでおく、です)。

    myimg.onload = () =>
    {
    	// sf.jpg 画像サイズは 720x540 ピクセルで用意しています。
    	imgW = myimg.width;
    	imgH = myimg.height;
    
    	// canvas 全体にイメージを描画します。
    	ctx.drawImage( myimg, 0, 0, canvas.width, canvas.height );
    
    	// サブキャンバスはキャンバスのサイズを画像サイズに連動させます。
    	ctxSub.drawImage( myimg, 0, 0, canvasSub.width, canvasSub.height );
    }
    

    まずは Image オブジェクト myimgonload イベントについてです。

    この書き方にいまだに慣れないわけですが、 まず基本としては、イコールの右辺に、 読み込みが完了したとき ( onload イベントの意味です ) に呼び出される関数を指定する、です。

    で、別途関数を用意するほどでもない場合には、直接関数を記述できる、ということらしいです。

    onload のハンドラ(関数)は引数を取らないので、まず 「()」と書いています。

    そしてそれがここに記述されている、という意味(ではないかも知れませんが)で、「=>」と書いたあと、 「{ 〜 }」に関数の本体を書く、という流れのようです。 「=>」の意味については理解が不十分ですので、必要でしたら調査してください。

    中身をみていきます。

    もう myimg に画像データが読み込まれていますので、 widthheight プロパティを参照し、画像のサイズを取得できます。 それぞれ imgWimgH に保存しています。

    用意したキャンバス 2 つは初期状態で同じサイズで、それぞれのコンテキストは ctxctxSub に設定してありますので、 drawImage 関数で、キャンバス全体に画像を描画しています。 画像は 720 x 540 ピクセルで用意してあり、キャンバスは 360 x 270 ピクセルにしていますので、50% のサイズということになります。

    ここで画像の描画を行わない場合、スライダーを操作するまで画像が表示されない、ということになります。

    また、onload イベントを待たずに描画を行うと、(タイミングにより)画像が読み込まれていませんので、描画はうまくいきません。

    ▲ページ先頭へ

    スライダー機能を実現する

    投稿 November 18, 2022

    ここからがメインです。

    まずは構成を確認します。

    function sliderInput(evt)
    {
    	// 実行したい処理を記述
    }
    slider.addEventListener('input', sliderInput, false);
    

    スライダーが操作されたときに呼び出される関数(ハンドラ)を定義しています。 ここでは関数名を sliderInput としていますが、特に決まりはありません。 引数は 1 つで、名前を evt、「イベント」としています。

    その定義のあと(前でも問題ないようですし、もっともっと離れても大丈夫みたいです)、 HTML で定義した zoom-slider を取得した slider を使って、 addEventListener 関数 を呼び出します。

    最初の引数には文字列 input を指定しています。 これが入力を意味しているようです。

    次の引数で、slider に input イベントがあったときに呼び出す関数名を指定しています。

    最後の false 指定は省略しても false ですので、なくてもよさそうです。 詳細は、 「EventTarget.addEventListener()」 にあります。

    改めてまとめると、スライダー slider に入力を表す input イベントが起きたとき、 関数 sliderInput が呼び出されるように、sliderInput 関数を用意し、それをシステムに登録した、ということになります。

    // slider.addEventListener('input', sliderInput(), false); // こう書いてはいけない // slider.addEventListener('change', sliderInput, false); // 確定したときのみ

    ここからは、スライダーが操作されたときに呼び出されるsliderInput 関数の中身です。

    	// canvas 全体をいったんクリアします。
    	ctx.fillStyle = "rgb( 230, 255, 230 )";
    	ctx.fillRect(0, 0, canvas.width, canvas.height);
    

    まずはスライダーの上にある、1 つ目のキャンバスの背景を明るい緑でクリア(塗りつぶし)しています。 これをやらなければ、縮小方向に描画する場合に、キャンバス内の画像領域外に前の画像が残って汚くなります。

    	// 描画倍率を決めます。
    	const val = evt.target.value;		// min と max の間のスライダー位置(実数値)
    

    まずは書き換え不可の変数 val に、スライダーが現在指している値を取得しています。 刻みを「any」にしたため、値は実数(小数付きの値)となっています。

    evt.target は、addEventListener 関数を呼び出した slider、HTML で定義した zoom-slider を表しています。 この関数を他のスライダー等の要素と共有していませんので、常に slider と同じ意味となります。

    プロパティ value が現在指している値です。 つまり書き換え不可の変数 val に、min と max の間のスライダー位置が実数値で設定されることになります。 min を 1 に、max を 255 に設定しましたので、この間の値が設定されます。

    	const scale = val / 255.0;		// スライダー位置が 255 のとき 100% 表示
    	ctx.scale( scale, scale );
    	ctx.drawImage( myimg, canvas.width / 2 / scale - imgW / 2, canvas.height / 2 / scale - imgH / 2, imgW, imgH );	// canvas に画像を描画(scale 適用)
    	ctx.setTransform( 1, 0, 0, 1, 0, 0 );	// "変形行列" を単位行列に戻します(scale のクリア)
    

    描画倍率を決定します。

    スライダーの値 val の最大値が 255 ですので、255 で割った値は 0 〜 100% の意味になります。 ただ、最小値が 0 だと問題があるかもしれませんので、0% にならないよう、最小値は 1 としています。

    コンテキストの scale 関数で、横および縦方向の描画倍率を設定しています。 この設定を行えば、画像描画の際に倍率を計算する必要がなくなります。 ただ、常にこの方式にすべきかと言えば、そうでもないような気がします。

    続いて drawImage 関数で、画像を描画しています。 最初の引数で myimg を指定していますので、読み込んだ sf.jpg が描画されます。

    続く 2 つは左上の画像ですが、センタリングしたいために面倒な式になっています。 ポイントは、ここで指定する座標も scale 設定が有効になっている、ということです。

    最後の 2 つは描画サイズですが、scale 設定が有効ですので、画像サイズ imgW と imgH で常に変わりはありません。

    続く setTransform 関数ですが、これは scale 設定を無効にするためです。 scale( 1, 1 )でどうなのか、と思いましたが、すでに有効な scale に対して 100% を指定していることになるようで、解決しませんでした。 縮小した分、あるいは拡大した分だけ、正確に拡大、あるいは縮小し戻せば良さそうにも思えますが、 それよりはこの関数を使って、すべてリセットにしたほうが簡単みたいです。

    	// サブキャンバスはキャンバスのサイズを画像サイズに連動させます。
    	canvasSub.width = imgW * scale;
    	canvasSub.height = imgH * scale;
    	ctxSub.drawImage( myimg, 0, 0, canvasSub.width, canvasSub.height );
    

    最後にスライダーの下に描画している、2 つ目のキャンバスの処理です。

    こちらは canvasSub のサイズを直接拡大、あるいは縮小してしまい、その全面に画像を描画しています。 画像は自動的に拡大・縮小されることになります。

    1 つ目のキャンバスの背景を白などのページの背景色にすれば、ページのレイアウトのままで画像が拡大・縮小されることになります。

    2 つ目のキャンバスごとサイズ変更のケースでは、レイアウトが変化してしまいますが、ダイナミック感はあります。

    ▲ページ先頭へ

    CSS で画像サイズを切り替える

    投稿 November 18, 2022

    JavaScript で拡大・縮小できるようなコードを仕上げてみたものの、ちょっと使うには面倒ですし、スライダーが画像ごとにあるのは嫌です。 ハンドラ(関数)は共用できそうですから、コード的にはそれほど面倒にはならないと考えられますが、その必要がある、重要な部分だけに使いたいです。

    そこで、自由に拡大・縮小はできないものの、その場でサイズをもとに戻せる、CSS だけで実現できるコードも作成しました。

    2 つのキャンバスの下、横線の下にある画像は、初期状態で同じサイズ、360 x 270 ピクセルに縮小されて表示されていますが、 クリック(タップ)すると、オリジナルのサイズに拡大されて表示されるようになっています。 もう 1 度クリック(タップ)すると、また小さいサイズに戻ります。

    これを実現するために、HTML の head 〜 /head 内に、次のようなコードを記述しています。 もちろん外部 css ファイルにおくほうが良いと思います。

    <style type="text/css">
    
    		/* input 要素は常に非表示にします */
    		p.imgExpShr input
    		{
    			display: none;
    		}
    
    		/* input 内の img のサイズを決定(JavaScript の初期サイズあわせです) */
    		p.imgExpShr input + img
    		{
    			/*width: 360px;*/
    			cursor: pointer;
    		}
    
    		/* input がチェック状態の場合の img のサイズを原寸に指定 */
    		p.imgExpShr input:checked + img
    		{
    			width: auto !important;
    		}
    </style>
    

    p タグに imgExpShr が指定されることを基本としています。

    この imgExpShr が適用されている中では、input は表示しないと指定しています。 この input は、タイプに checkbox を指定していますので、チェックボックスです。 動作を確認するには、この display 指定をコメントアウトします。

    (正確ではないかもしれませんが)チェックボックスの内容に指定された img のスタイルを決定しています。 cursor は、マウスポインタがある場合、クリック可能なポインタに変更する、というものです。 その前のコメントアウトされた width 指定は、すべての画像が同じサイズで表示されるなら、ここで定義するほうが簡単ですので、残しています。 ここでは固定サイズではなく、それぞれ別々にサイズが指定できるよう、コメントアウトしています。

    最後は、チェックボックスがチェックされたときの img のスタイルです。 元のサイズの width 指定を上書きできるよう、!important を付与した width です。 指定している auto は、画像の元のサイズということになります。 ここで 100% のような指定をしてしまうと、画面ベースの 100% 表示になってしまい、原寸表示ではなくなります。

    これを使う HTML は、次のようになっています。

    <p class="imgExpShr">
    	<label for="imgSF">
    		<input type="checkbox" id="imgSF">
    		<img src="jsimg/sf.jpg" alt="jsimg/sf.jpg がありません" title="San Francisco Cable Car" style="width: 360px;">
    	</label>
    </p>
    

    まずは p タグで imgExpShr クラスを指定しています。

    その内側で、まずは label を指定し、グループの名前を決めているようです。 「フォーム」関係のタグには、label を指定するようです。

    続いて input タグにタイプ checkbox を指定して、チェックボックスを作成しています。 ここで指定している id には、label の for で指定している文字列を同じものを指定する必要があるようです。

    画像データを記述します。

    src にはファイル名を、alt には画像が読み込めなかったときに表示させる文字列を、そして title にはポインタが乗った時の説明文を記述します。

    そして style に、「初期状態の画像サイズ」を記述しています。 画像ごとにサイズ指定は面倒ですが、こうしておけば画像ごとに初期サイズを決められます。 すべての画像で同じサイズでよければ、CSS 側で width を指定し、画像ごとの style を省略することが可能です。

    簡単ですね。

    ▲ページ先頭へ
    line
    関連トピックス
    line


    その他のおすすめ

    ウェブ開発に関するトピックは、「ウェブ開発トップ」にまとめられています。



    © 2017-2022 StraightApps.com 無断転載を禁じます。No reproduction without permission.