![](https://habrastorage.org/storage2/1b3/e2e/dee/1b3e2edee7f731f1f6024a83224284ff.png)
この記事では、HTML5を使用して、通常の画像に基づいてASCIIアートを生成する簡単なアプリケーションを作成する方法について説明します。 この記事は、私がHTML5のような素晴らしいテクノロジーに精通し始めたばかりの人を対象としています。 専門家が自分自身にとって新しいものを見つけることはまずありません。
夕方だった、何もすることがなかった
最近、インターネットで壁紙を探して掘り当てたところ、1つの興味深い画像 (1.1 MB)に出会いました。 そして、私はカラフルな文字で画像を描くというアイデアに「夢中になりました」。 インターネットで大騒ぎして、これがASCII-artと呼ばれることを知りました。 そしてもちろん、最初に考えたのは、「そして、この方法でお気に入りの壁紙をペイントするアプリケーションを埋めます!」
すぐに言ってやった。 時間がある、欲求がある-試してみませんか。
ブラウザにアプリケーションを実装することが決定されました。 私は長い間HTML5を見て唇をなめましたが、それでも私の手が手を伸ばすことができませんでした。 そして何? この技術はファッショナブルで有望であり、試してみませんか そして、プロジェクトは複雑ではありません。何か新しいことを研究するためです。まさにそれです。 これで停止しました。
問題の声明
アプリケーションは次の要件を満たしている必要があります。
- 元の画像を読み込むには2つの方法があります。ファイル選択フィールドを使用して、特別な領域(以下「受信領域」と呼びます)にドラッグします。
- 複雑な設定の欠如。 最も必要なのは、背景色、使用するテキスト、フォントサイズのみです。
- 背景が透明な画像を処理する機能。
- 作業はブラウザでのみ行われ、サーバーへのアクセスやページの再読み込みは行われません。
古いブラウザをサポートするという問題が生じないことは明らかです。
始めるために、htmlマークアップをスケッチしましょう。 アプリケーションページは3つの論理部分に分かれています。
1.元の画像のダウンロード領域
<h2>Source image</h2> <div class="row"> <div class="source-image-area-out"> <!-- ( ) --> <div id="source-image-area"> Drop source image here... </div> </div> </div> <div class="row"> <!-- --> <label for="source-image-file" style="width: 150px">Or select this here:</label> <input type="file" id="source-image-file" /> </div>
2.設定エリア
<h2>Settings</h2> <div class="left"> <!-- --> <div class="row"> <label for="input-used-text">Used text:</label> <input type="text" id="input-used-text" value="B" /> </div> <!-- --> <div class="row"> <label for="input-font-size">Font size:</label> <input type="number" id="input-font-size" min="3" max="20" step="1" value="8" style="width: 65px" /> px </div> </div> <div class="right"> <!-- --> <div class="row"> <label for="input-background-color">Background:</label> <input type="color" id="input-background-color" /> </div> <!-- --> <div class="row"> <label for="input-background-transparent">Transparent:</label> <input type="checkbox" id="input-background-transparent" /> </div> </div>
3.プレビューエリア
<h2>Previews</h2> <!-- --> <div id="preview-result" class="left"> <img src="" id="image-result" alt="" /> </div> <!-- --> <div id="preview-source" class="right"> <img src="" id="image-source" alt="" /> </div>
ソース画像をダウンロード
まず、元の画像をダウンロードする方法を理解しましょう。
ユーザーが選択したファイルに送信せずにアクセスするために、サーバーは
FileReader
クラスを使用します。 その
readAsDataURL()
メソッドは、ファイルのコンテンツをdata:URLスキームとして返します。 それでは、試してみましょう。
// , . var fileData = null; // . var loadFromField = function(event) { loadFile(event.target.files[0]; }; // “ ”. var loadFromArea = function(event) { event.stopPropagation(); event.preventDefault(); loadFile(event.dataTransfer.files[0]); }; // dragover “ ”. var areaDragOverHandler = function(event) { event.stopPropagation(); event.preventDefault(); event.dataTransfer.dropEffect = "copy"; }; // . // fileData. var loadFile = function(file) { var reader = new FileReader(); reader.onload = function(data) { fileData = data.target.result; } reader.readAsDataURL(file); } // . // document.getElementById("source-image-file").addEventListener("change", loadFromField, false); // “ ”. document.getElementById("source-image-area").addEventListener("drop", loadFromArea, false); document.getElementById("source-image-area").addeventListener("dragover", areaDragOverHandler, false);
これで、データの形式の元の画像:URLができました。 それで何ができますか? 画像の
src
属性値として使用できます。 それでは、ユーザーに元の画像を見せましょう。
var sourceImage = document.getElementById("mage-source"); sourceImage.src = fileData;
まあ、それは非常に明確です。 今最も重要なこと:この画像を処理する必要があります。
設定
ユーザーが設定を変更するたびに設定が保存されるわけではありません。 代わりに、画像処理の直前に一度だけカウントします。
var usedText = document.getElementById("input-used-text").value; var fontSize = document.getElementById("input-font-size").value; var backgroundColor = (document.getElementById("input-background-transparent").checked == true) ? "rgba(0,0,0,0)" : document.getElementById("input.background-color").value;
では、芸術の生成に直接進みましょう。
画像処理
プロセス全体をいくつかの段階に分けることができます。
- 元の画像に関するデータを取得します。 より正確には、各ピクセルの色が必要です。
- アートが形成される文字のサイズの計算。
- 各シンボルの色とその色の計算。
- 直接芸術の生成;
- ユーザーが自分の努力の成果を保存できるように、画像としてのアートのプレゼンテーション。
ソース画像データの取得
元の画像のピクセルの色を知るには、 キャンバスを作成して画像を適用する必要があります。 最初にキャンバスをページに追加します。
<canvas id="canvas"></canvas>
次に、元の画像と同じ寸法を指定して、この画像を適用します。 そして、キャンバスに関する情報を取得し、その結果、元の画像に関する情報を取得します。
var canvas = document.getElementById("canvas"); var context = canvas.getContext("2d"); canvas.width = sourceImage.width; canvas.height = sourceImage.height; context.drawImage(sourceImage, 0, 0); var sourseData = context.getImageData(0, 0, canvas.width, canvas.height).data;
getImageData()
メソッドは、キャンバス情報を返します。 データフィールドには各ピクセルの説明が含まれており、必要なものだけが含まれています。
これで必要な情報が得られました。 それは最高の形で提示されていないだけです。 これは1次元配列で、最初の4つの要素は最初のピクセル(rgba)を記述し、5番目から8番目の要素は2番目のピクセルを記述します。 最後まで。 これをどのように扱うか、私にはほとんど考えがありません。 したがって、この一連の数字を人間の形にしましょう。
var getPixelsGrid = function(source) { var res = []; for (var i = 0; i < source.length; i += 4) { var y = Math.floor(i / (canvas.width * 4)); var x = (i - (y * canvas.height * 4)) / 4; if (typeof res[x] === "undefined") { res[x] = []; } res[x][y] = { r: source+0], g: source[i+1], b: source[i+2], a: source[i+3] } } return res; } var pixelsGrid = getPixelsGrid(sourseData);
これで、各ピクセルがオブジェクトで表される2次元配列ができました。 彼と一緒に働き続けます。
文字サイズ
正確な文字サイズを取得する方法は? フォントサイズではなく、画面上で文字が占める面積は? 気にしないために、このシンボルで一時的なスパンを作成し、そのサイズを測定します。
var countUsedTextSize = function(symbol, size) { var block = document.createElement("span"); block.innerHTML = symbol; block.style.fontSize = size + "px"; block.style.fontFamily = "Monospace"; document.body.appendChild(block); var re = [(block.offsetWidth, Math.floor(block.offsetHeight * 0.8)] document.body.removeChild(block); return re; }; // , . var size = countUsedTextSize(usedText[0], fontSize); var usedTextWidth = size[0] var usedTextHeight[1];
![](http://habrastorage.org/storage2/1ed/5fd/eae/1ed5fdeae418f78a902d8bb5771dd9a9.png)
文字の位置と色の計算
ここで、「シンボルマップ」を作成する必要があります。これは、最終的な画像が形成される各シンボルに関する情報を含むリストです。 シンボルの座標と色を知る必要があります。
シンボルの色として、このシンボルが占める元の画像の領域の中心に位置するピクセルの色を使用します。 まあ、またはその隣に、片側に奇数のピクセルがある領域の場合。
var getAvgPixelsList = function(grid) { var res = []; var stepX = usedTextWidth; var stepY = usedTextHeight; var countStepsX = canvas.width / stepX; var countStepsY = canvas.height / stepY; for (var y = 0; y < countStepsY; y++) { for (var x = 0; x < countStepsX; x++) { res.push({ x: x * stepX, y: y * stepY, r: grid[x * stepX][y * stepY].r, g: grid[x * stepX][y * stepY].g, b: grid[x * stepX][y * stepY].b, a: grid[x * stepX][y * stepY].a }); } } return res; }; var avgPixelsList = getAvgPixelsList(pixelsGrid);
また、ユーザーが入力したテキストから次の文字を返す関数を定義します。 そして最後に到達したら、最初からやり直してください。
var nextUsedChart = 0; var getNextUsedChart = function() { var re = usedText.substring(nextUsedChart, nextUsedChart+1); nextUsedChart++; if(nextUsedChart == str.length) { nextUsedChart = 0; } return re; };
アートジェネレーション
これで、必要なものすべてが揃いました。画像を形成する文字の位置と色のリスト、およびこれらの文字を返す関数です。 アートを生成しましょう。
var getResultData = function(list) { // . context.clearRect(0, 0, canvas.width, canvas.height); context.fillStyle = backgroundColor; context.fillRect(0, 0, canvas.width, canvas.height); // . for (var i = 0; i < list.length; i++) { var px = list[i]; context.fillStyle = "rgba(" + px.r +", " + px.g + ", " + px.b + ", " + px.a + ")"; context.font = fontSize + "px Monospace"; context.fillText(getNextUsedChart(), px.x, px.y); } return canvas.toDataURL(); }; var resultData = getResultData(avgPixelsList);
いいね! アートの準備ができました。 ユーザーに表示するためだけに残ります。
var resultImage = document.getElementById("image-result"); resultimage.src = resultData;
まとめ
おめでとうございます、ジェネレーターの準備ができました! さらに、それも動作します。
以下に例を示します。
〜150Kb
![](http://habrastorage.org/storage2/f1c/b9a/435/f1cb9a435c81ed29108667ffce23787b.jpg)
![](http://habrastorage.org/storage2/a51/b68/248/a51b68248a2fa0f0b1b3392a47dd5487.png)
〜750Kb
![](http://habrastorage.org/storage2/d59/f46/712/d59f4671258075a6341b9f64d439ffb0.jpg)
![画像](http://habrastorage.org/storage2/84c/040/632/84c04063218a0bf9bd23cb1223da8405.png)
しかし、透明な背景で〜300Kb
![](http://habrastorage.org/storage2/335/076/077/33507607734a4c3a3959919b1e86e442.jpg)
![](http://habrastorage.org/storage2/75c/814/c68/75c814c6869e86a652198c300b3bed2e.png)
ここで 、得られたものを見ることができます。
そして、 ここにソースコードがあります。 これらは記事で説明されているものとは若干異なります(機能はクラスに分割されます)が、作業のロジックは同じです。
興味深い点がいくつかあります。
- クロムでは、アプリケーションは他のブラウザよりも何倍も速く動作します。
- 大きな画像(1000x1000以上)を処理する場合。 Chromeは、「メモリ不足」というメッセージを表示してタブを殺す間、新しいウィンドウで開くことを拒否します。 他のブラウザでは、ゆっくりですが、これは機能します。 これは、URLを介して画像を送信する場合、文字列が長すぎるという事実によると思います。
おわりに
今日は、キャンバスの操作やファイルのダウンロードなど、HTML5の興味深い便利な側面に出会いました。 同時に、実用的で、最も重要なことは、実用的なアプリケーションを作成しました。 もちろん、これで終わりではありません。 アプリケーションを完成させる必要があります。
- これで、ユーザーが古いブラウザでログインした場合、アプリケーションは何も言わず、機能しなくなります。 使用するテクノロジーをサポートするためにテストを追加する必要があります。 ユーザー入力の検証を追加することも必要です。
- 元の画像のサイズがシンボルのサイズの倍数でない場合、空のバーが右下に表示されます。 この問題を解決する必要があります。
- まあ、クロスブラウザの互換性。 iPhoneでクロム25、クロム27、Firefox 21、オペラ12、サファリのみをチェックする機会がありました(バージョンは見つかりませんでした)。 他のブラウザでは、バグをテストして修正する必要があります。
しかし、それは後であります。 ここで私は仕事の原則だけを見せたかった。
これで終わりです。 最後まで読んでくれてありがとう。 自分に役立つものを見つけてほしい。 細身のコード。