質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
canvas

HTML5の<canvas>要素用のタグです。CanvasはHTML5から導入された、二次元の図形描写が可能な要素です。

Chrome

Google Chromeは携帯、テレビ、デスクトップなどの様々なプラットフォームで利用できるウェブブラウザです。Googleが開発したもので、Blink (レンダリングエンジン) とアプリケーションフレームワークを使用しています。

HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

3回答

10464閲覧

大量の画像を表示するとブラウザがクラッシュする

flc

総合スコア13

canvas

HTML5の<canvas>要素用のタグです。CanvasはHTML5から導入された、二次元の図形描写が可能な要素です。

Chrome

Google Chromeは携帯、テレビ、デスクトップなどの様々なプラットフォームで利用できるウェブブラウザです。Googleが開発したもので、Blink (レンダリングエンジン) とアプリケーションフレームワークを使用しています。

HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

1クリップ

投稿2018/12/18 05:14

編集2022/01/12 10:55

ローカルHTML + JavaScriptで、自分用の画像ビューアのようなものを作っています(探してみても目的に合ったフリーソフトがないため)。

表示したいフォルダーは決まっており、フォルダー内の画像はすべて

  • ファイル名 … 「0000.jpg」~「9999.jpg」(実際は3000枚程度)
  • 大きさ … 1280 * 720px
  • 容量 … 200KB前後

となっています。フォルダーの総容量はおよそ1GB台、使用PCのRAMは4GBです。

これらを一度に表示しようとすると、メモリを使いすぎるせいかページ自体が表示されなかったり、ブラウザ(Chrome)がクラッシュしたりしてしまいます。

やってみたこと

  1. 画像を一旦canvasに小さく描く(1/2サイズ)
  2. その小さいcanvasをtoBlob()もしくはtoDataURL()で<img>タグに反映

としてみることで、若干ながら軽量化を図ってみました。

それでも、表示枚数を何千枚という値にしてみるとやはりクラッシュしてしまいます…

JavaScript

1 2const canvas = document.getElementById('canvas'); 3const ctx = canvas.getContext('2d'); 4 5const WIDTH = 1280; 6const HEIGHT = 720; 7 8const divide = 2; 9 10const w = WIDTH / divide; //1280 ÷ 2 = 640 11const h = HEIGHT / divide; //720 ÷ 2 = 360 12 13canvas.width = w; 14canvas.height = h; 15 16const max = 3000; //ひとまず3000枚を表示してみる 17 18for( let i=0; i<max; i++ ){ 19 20 const serial = `000${i}`.slice(-4); 21 const img = new Image(); 22 23 //ローカル画像を読み込むため、Chromeの起動オプションにあらかじめ--allow-file-access-from-filesをつけてあります 24 img.crossOrigin = "Anonymous"; 25 img.src = `file:///C:/新しいフォルダー/${serial}.jpg`; //元のサイズは1920 * 1080px 26 27 img.onload = () => { 28 29 ctx.drawImage( img, 0, 0, this.width, this.height, 0, 0, w, h ); 30 31 canvas.toBlob( (blob) => { 32 33 const newImg = document.createElement('img'); 34 newImg.crossOrigin = "Anonymous"; 35 const url = URL.createObjectURL( blob ); 36 37 //あるいは、toBlobを使わずに 38 //const url = canvas.toDataURL('image/jpeg', 1); 39 //画質を落とせば軽量化はされるものの、できれば綺麗に表示したい 40 41 newImg.src = url; 42 newImg.title = serial; 43 document.body.appendChild( newImg ); 44 45 }); 46 47 } 48 49} 50

他に考えついた方法

画面内に表示されていない(スクロール外の)画像はメモリから解放し(ただのプレースホルダーとしての空白にし)、スクロールされた時点で画像を表示する… という手法も考えられそうですが

スクロールされてから画像を読み込んでいては遅いですし、かといって適切なプリロードのタイミングというのもいまいちイメージできず…

何かよい方法はないでしょうか?

(もしかしたらそもそもHTML + JavaScriptで作ることを諦めたほうがよかったりするのかもしれませんが、知識不足でして…)

追記 再帰的?にしてみたもの

for文をやめて以下に書き換えてみると、toBlob()する前に生成していた元画像ぶんの余計なメモリ使用が抑えられたようです
(それでも3000枚は表示できませんが)

都度delete演算子で画像オブジェクトのプロパティを削除することで、ガベージコレクションがメモリを解放してくれるのを期待してこのような形ですが(そういうことを意識するのは初めてなので合っているかわかりません)

そのため、すぐにメモリが解放されるわけではなく時間差があるようで、Chromeに内属されたタスクマネージャで見ていると一旦メモリがある程度増加してから下がる… という挙動を見せています

なのでだんだんblobによるメモリ使用量が増えてくる後半、ヒヤヒヤしながら見守るのは変わらず…(うちのPCでは600MBほど使うとChromeが落ちる?ようです)

JavaScript

1let tags = []; 2 3const blobbing = (index) => { 4 5 const serial = `000${index}`.slice(-4); 6 let img = new Image(); //あとでdeleteしてメモリから解放するので、constではなくlet 7 8 //ローカル画像を読み込むため、Chromeの起動オプションにあらかじめ--allow-file-access-from-filesをつけてあります 9 img.crossOrigin = "Anonymous"; 10 img.src = `file:///C:/新しいフォルダー/${serial}.jpg`; //元のサイズは1920 * 1080px 11 12 img.onload = () => { 13 14 ctx.drawImage( img, 0, 0, this.width, this.height, 0, 0, w, h ); 15 delete img; //ここで元画像をメモリから解放(すぐに解放されるわけではなく時間差がある?) 16 17 canvas.toBlob( (blob) => { 18 19 const url = URL.createObjectURL( blob ); 20 tags[index] = `<img src="${url}" title="${serial}" style="width: ${w}px; height: ${h}px;">`; 21 index++; 22 23 if( index === max ) { 24 25 $('.images').append( tags.join() ); 26 } 27 else { 28 29 blobbing( index ); //自分自身を呼び出してループ 30 } 31 32 }); 33 34 } 35 36 37} 38 39blobbing( 0 ); //ループ開始

あるいはdeleteを使わなくても、単にlet img = new Image(); で最初に作ったオブジェクトをそのままずっと使いまわしてもいいのかもしれません(そのほうがよかったりするのかはわかりませんが)

JavaScript

1let tags = []; 2let img = new Image(); 3 4const blobbing = (index) => { 5 6 const serial = `000${index}`.slice(-4); 7 8 //ローカル画像を読み込むため、Chromeの起動オプションにあらかじめ--allow-file-access-from-filesをつけてあります 9 img.crossOrigin = "Anonymous"; 10 img.src = `file:///C:/新しいフォルダー/${serial}.jpg`; //元のサイズは1920 * 1080px 11 12 img.onload = () => { 13 14 ctx.drawImage( img, 0, 0, this.width, this.height, 0, 0, w, h ); 15 16 canvas.toBlob( (blob) => { 17 18 const url = URL.createObjectURL( blob ); 19 tags[index] = `<img src="${url}" title="${serial}" style="width: ${w}px; height: ${h}px;">`; 20 index++; 21 22 if( index === max ) { 23 24 $('.images').append( tags.join() ); 25 } 26 else { 27 28 blobbing( index ); //自分自身を呼び出してループ 29 } 30 31} 32 33blobbing( 0 ); //ループ開始

2000枚近く読み込めるようにはなったものの、

net::ERR_INSUFFICIENT_RESOURCES 200 (OK)

というのが出ますね… やはりリソースがあと少し足りないようで

(ここまでしてBlobを使って画像を縮小する必要があるのか、そもそも複数の方からのご意見があったようにJavaScript以外の手段で縮小画像を作っておくべきなのかという話にもなってきますが)

引き続きご意見をお待ちしております。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ockeghem

2018/12/18 05:19

1920*1080のフルカラー画像は伸長後で6MBくらいになります。3000枚というと、掛け算で18GBということになります。テストされたPCの搭載メモリはどれくらいでしょうか?
flc

2018/12/18 05:35

写真ではないためか、1枚1枚の容量はそこまでにはなっていないようです。 伸長といいますと… JPEGが圧縮画像であることと関係していますか? フォルダーの容量は総計およそ1GB台、PCのメモリは4GBになります。 それと申し訳ありません、1920 * 1080というのは質問文を書く際の誤表記でした。正しくは1280 * 720です。
flc

2018/12/18 05:38

ちょっと検証が必要ですが、もしかしたら単に拡張子をjpgとしただけのpngなのかもしれません…
guest

回答3

0

imgタグのsrc越しに画像ファイルを落としてきて200kb、
そこからキャンバスに載せたり、blobで取得したりで複製という形で
単純に3000枚200kb≒600MB…ではなく、600MBn個の複製=数GB単位でメモリを消耗してしまっているのでしょう。

まずはタスクマネージャー(Windows)やアクティビティモニタ(Mac)等を開いて、
ブラウザがどれほどのメモリを消耗しているのか、自分のマシンのメモリの許容量はいくらかを探る必要があるでしょう。

また、3000回document.body.appendChild( newImg );を繰り返すと、
都度レンダリングのやり直し扱いとなり、ブラウザが再描画の為に大忙しになります。
予めタグを全部作っておいてinnerHTMLで一気に流し込んだ方が負荷が減るでしょう。

改善案としてはこんな感じです。

HTML

1<body> 2<div class="images"></div> 3<script> 4const images = Array(3000) 5 .fill(0) 6 .map((_, i) => `000${i}`.slice(-4)) 7 .map(serial => `<img src="file:///C:/新しいフォルダー/${serial}.jpg" alt="${serial}" style="width: 1920px; height: 1080px;">`) 8 .join(''); 9document.querySelector('.images').innerHTML(images); 10</script> 11</body>

投稿2018/12/18 05:34

編集2018/12/18 05:42
miyabi-sun

総合スコア21158

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

miyabi-sun

2018/12/18 05:36

ああ、確かにJPEG画像を描画するために展開すればそれだけ容量食いますよね。。。
flc

2018/12/18 07:07 編集

あっ…! いつもjQueryとかではそのようにしていたのに、経過を見たくて都度appendChildする形式のままになってました… それも負荷の一因となっていたかもしれませんね Chromeに内蔵されたタスクマネージャによると、どうも画像1500枚ほど・メモリ使用量600MB前後が限度で、それ以上はクラッシュするようです ん、ということは画像1枚につき400KBほど…? JPEGを展開してるにしてはあまりメモリ使用してない気もしますね… メモリ削減したくてやっていたBlobが逆にメモリ使用量の増加を招いていたのでしょうか 解放すればいいとかそういう話でもないのでしょうかね… いや、きっとonloadのタイミングのせいもあるかもしれません 0000.jpgに対するonloadの中身が完了したらそれ(letした変数img)をdeleteで解放し、つぎに0001.jpgの処理を…というふうに変えてみたら…ちゃんとメモリ使用量削減できてるっぽいです!(それでも3000枚までは行けずに途中で落ちますが)
flc

2018/12/18 07:32

delete演算子を使ったコードを追記いたしました
guest

0

200KBといってもそれはJPEG圧縮されたファイルのサイズで、実際に画面に描画されるときには、1枚で約6MBの容量が必要になります。

予めサムネイル用の小さな画像を作っておくのが一般的な対応ですね。

投稿2018/12/18 05:34

kasa0

総合スコア578

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

flc

2018/12/18 05:44

乱暴に言えばビットマップとしての容量ということでしょうか? 並べた画像を詳細に見比べたいので、できればあまり小さくしたくはないのですが… (クリックした1枚だけを拡大とかではなく)
kasa0

2018/12/18 05:53

はい、画面に表示するためには、非圧縮の画像サイズ分の容量が必要になります。 比較したいのであれば、比較する画像を選択して、別画面で選択された画像だけを表示するようにするとかですかね。
flc

2018/12/18 06:10 編集

選択して別画面とかそういう話になってくると、スクロールだけで見比べられるものと比べだいぶかけ離れてきてしまいますね…
guest

0

本質的な解決方法としてはサーバー側の処理でuploadされた際に
サムネイルをつくるしかないです
その手の処理が入れられないのであればページングなど1ページあたりの
容量を減らして下さい

投稿2018/12/18 05:44

編集2018/12/18 05:45
yambejp

総合スコア114583

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

flc

2018/12/18 05:48

質問文にも書かれているとおり、サーバーには上げずローカルで使うHTMLになります。
yambejp

2018/12/18 05:51

それならなおさら、ローカルでサムネイルをつくって同時アップするだけの話でしょう またローカルであってもhttpdを導入して処理は可能です
flc

2018/12/18 07:43

アップとは…?
yambejp

2018/12/18 07:46

あくまでもローカルというならコピーと言い換えてもよいかもしれません
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問