ラベル Canvas の投稿を表示しています。 すべての投稿を表示
ラベル Canvas の投稿を表示しています。 すべての投稿を表示
2014/04/06

かずー氏アイコンジェネレータをHTML5で作ってみた

HTML/CSS/JavaScriptを駆使した画像の重ね方 | DevAchieveという記事、
かずー氏クラスタアイコンの作り方の記事を1年前くらいに書いていました。
そこで Canvas を使用してかずー氏 (@kazoo04)のアイコン背景と
@wada811 のアイコンを合成できていたので
ユーザーがアップロードした画像を読み込んで
同じように Canvas に表示してあげて合成すれば
かずー氏アイコンジェネレータになるのではないか
というのがこのプロジェクトの始まりでした。

そして、途中で開発が止まるなど紆余曲折がありましたがついに完成しました!
かずー氏アイコンジェネレータ

わずか 3STEP で、かずー氏アイコンを作成できるという優れものです。
(本当はページ開いた瞬間にファイル選択が出て、選択したら自動ダウンロードということもできます。)
今回サーバいらずで、クライアントサイドの技術のみでかずー氏アイコンを生成することができます。
この手のちょっとしたネタサイト作るためにサーバの用意とかお金かけるとかしたくない人におすすめです。

技術的な話

Drag and Drop API

実は画像をドラッグ・アンド・ドロップでアップロードすることが可能です。
デザイン上、ドラッグ・アンド・ドロップができるとわかるようになっていないので
何かいい感じのデザインになる Pull Request を待っています。

Drag and Drop API 機能の実装は簡単で、ドラッグ・アンド・ドロップ系のイベントを適切に処理するだけです。
<canvas id="jsKazoo04Icon"></canvas>
$(document).ready(function(){
    // ...
    addDropListener('jsKazoo04Icon');
});

function addDropListener(id){
    var jsDropBox = document.getElementById(id);
    document.addEventListener('dragover', dropNone);
    document.addEventListener('drop', dropNone);
    jsDropBox.addEventListener('dragover', dropCopy);
    jsDropBox.addEventListener('drop', drop);
}
ブラウザに画像ファイルをドロップすると画像をそのまま表示してしまうので処理を止めています。
document.addEventListener('dropover', dropNone); 
document.addEventListener('drop', dropNone);
function dropNone(event) {
    event.stopPropagation(); // イベントのバブリング(親要素への伝搬)を停止する
    event.preventDefault(); // ブラウザのデフォルトの処理(画像の表示)を防ぐ
    event.dataTransfer.dropEffect = 'none'; // D&D中のファイルにアイコンがつかないようにする
}
ドロップを受け付ける要素に addEventListener でリスナーを登録します。
jsDropBox.addEventListener('dragover', dropCopy);
jsDropBox.addEventListener('drop', drop);
function dropCopy(event) {
    event.stopPropagation(); // イベントのバブリング(親要素への伝搬)を停止する
    event.preventDefault(); // ブラウザのデフォルトの処理(画像の表示)を防ぐ
    event.dataTransfer.dropEffect = 'copy'; // D&D中のファイルにプラスアイコンがつくようにする
}

function drop(event){
    // 画像ビューアとして開かないようにする
    dropCopy(event);
    // ドロップされたファイルを取得
    var file = event.dataTransfer.files[0];
    if(file.type !== 'image/png'){
        alert('PNG画像のみを受け付けています。');
        return;
    }
    handleFile(file);
}

参考
[HTML5] Drag & Drop API おさらい 「ファイルの DnD」 | Developers.IO

JavaScript から参照する要素には js プレフィックスをつけたほうがいいよという話。
CSSのセレクタ部分(IDやCLASS)にハイフンとか使われるの好きじゃないなー。ボクはあまり好きじゃないなー - latest log

File API

input[type="file"] で画像アップロードを受け付け、
アップロードされた画像をプレビュー表示する際に使用しました。
<input type="file" id="jsUpload" class="upload" onchange="upload(this.value)" accept="image/*" />

まず、画像のみを受け付けるためには accept="image/*" を指定します。
参考
HTML5時代のinput要素のaccept属性について | 富永日記帳
4.10.5.1 States of the type attribute — HTML Standard

input[type="file"] で画像アップロードした際のイベントを取得するには onchange="upload(this.value)" を指定します。
今回は fileName は未使用ですが、何かに使えるかも?
スマホの場合はセキュリティのためダミーのパスが入るので使えないかも?
function upload(fileName){
    var files = document.getElementById('jsUpload').files;
    var file = files[0];
    handleFile(file);
}
File API で画像を読み込みます。
function handleFile(file){
    var fileReader = new FileReader();
    fileReader.readAsDataURL(file);
    // ファイル読み込み失敗時
    fileReader.onerror = function(e){
        alert('画像の読み込みに失敗しました。');
    };
    // ファイル読み込み完了時
    fileReader.onload = function(event){
        var image = new Image();
        image.src = event.target.result;
        image.onload = function(){
            // 画像が読み込み完了したら Canvas に描画
            var canvas = document.getElementById('jsKazoo04Icon');
            var ctx = canvas.getContext('2d');
            ctx.drawImage(image, 0, 0);
        };
    };
}

参考
Using files from web applications | MDN

Canvas を toDataURL で Data URL 形式の画像に変換する

解説はしていないがHTML/CSS/JavaScriptを駆使した画像の重ね方 でもできていたもの。
Canvas の描画内容を Data URL に変換して画像タグを貼り付けるだけです。
function generateKazoo04Icon(){
    var canvas = document.getElementById('jsKazoo04Icon');
    var ctx = canvas.getContext('2d');

    var dataURL = canvas.toDataURL('image/png');
    var imgTag = '<img src="' + dataURL + '" width="' + canvas.width + '" height="' + canvas.height + '" alt="かずー氏背景合成画像">';
    $('#jsKazoo04Icon').after(imgTag).remove();
    // ...
}

Data URL 形式の画像を Blob に変換して Blob URL 形式に変換

Canvas に toBlob もあるのですが、ほとんどのブラウザで実装されていないようです。
仕方ないので dataURL から Blob に変換する関数をかませて
window.URL.createObjectURL で Blob URL 形式に変換します。
function generateKazoo04Icon(){
    // ...
    var blob = Base64toBlob(dataURL);
    window.URL = window.URL || window.webkitURL;
    $('#jsDownload').attr("href", window.URL.createObjectURL(blob));
}

function Base64toBlob(_base64){
    var i;
    var tmp = _base64.split(',');
    var data = atob(tmp[1]);
    var mime = tmp[0].split(':')[1].split(';')[0];

    //var buff = new ArrayBuffer(data.length);
    //var arr = new Uint8Array(buff);
    var arr = new Uint8Array(data.length);
    for (i = 0; i < data.length; i++) {arr[i] = data.charCodeAt(i);}
    var blob = new Blob([arr], { type: mime });
    return blob;
}

参考
Canvasの画像(スクリーンショット)をローカルダウンロードします。 - jsdo.it - Share JavaScript, HTML5 and CSS
Base64をblobオブジェクトに変換します。 - jsdo.it - Share JavaScript, HTML5 and CSS
URL.createObjectURL - Web API Interfaces | MDN

だいたいオッケー、でも…

Drag and Drop による画像アップロードがわかりにくい点、
読み込んだ画像を移動、回転、拡大・縮小できない点がユーザー的に微妙ですね!
さぁあなたの Pull Request を待っています!

ちなみに「読み込んだ画像を移動、回転、拡大・縮小できない点」は
Fabric.js Javascript Canvas Libraryというものを使えば解決できます。

まだ時間をかければ実装はできますが、とりあえずリリースすることが大事ってばっちゃが言ってた。
2013/03/30

HTML/CSS/JavaScriptを駆使した画像の重ね方

いわゆる、かずー氏クラスタアイコンの作り方

Way to cover an image with another image
サンプルページへはこちらのリンクまたは画像をクリック!

@wada811のアイコンが、
他の人のかずー氏クラスタアイコンのように
全体サイズになっていないのは
重ねるのが簡単すぎて
重ねたい画像が同じサイズの特殊事例にしか
対応できないように見えるため
あえて小さく重ねています。

方法としては6つもありました!
正直自分でも驚きです。
  1. background: absolute; で絶対配置で重ねる方法
  2. 背景画像を指定した div に img タグを置いて重ねる方法
  3. 2の方法を擬似要素 :after の content: url() で重ねる方法
  4. 3の方法で content ではなく背景画像に指定して重ねる方法
  5. CSS3 の複数画像の背景指定で重ねる方法
  6. Canvas で複数画像を読み込んで重ねる方法

background: absolute; で絶対配置で重ねる方法は安心安定の指定方法。

HTML の記述量が多くなるので繰り返しが多い部分には使いたくないが、
ワンタイムのページでいちいち CSS ファイルを編集するのがめんどくさい時には便利。

背景画像を指定した div に img タグを置いて重ねる方法はプログラムで使いやすい。

背景画像をプログラムなどで差し替えたいときや、
状態判定してカバー画像をかける、かけないというの場合分けなどで便利。

2の方法を擬似要素 :after の content: url() で重ねる方法は IE7 以下で使えない。

今回は transform: scale(); で無理やり画像を小さくしているので
IE8 で画像が小さくならなくて背景画像をはみ出てしまう。
使うときはちゃんとサイズの合った画像を使おう。

3の方法で content ではなく背景画像に指定して重ねる方法は IE8 以下で使えない。

CSS3 の background-size を使っているので IE8 以下は画像が小さくならなくて切れてしまう。
使うときはちゃんとサイズの合った画像を使おう。

CSS3 の複数画像の背景指定で重ねる方法は IE8 以下で表示すらできない。

background-image が CSS3 で複数枚の画像を指定できるようになっている。
この使い方では微妙だが、サイトの画像をコンポーネント化できるので微妙に違う画像が多い時は便利かも?
スマホなら background-image の複数画像指定をサポートしているはずなので
画像を色んな組み合わせで一つの画像として表示したいときに使えるかも。
キャッシュされればそれぞれ早くなりそうな気がする。

Canvas で複数画像を読み込んで重ねる方法は IE8 以下では使えない。

IE8 以下でも Canvas を使えるようにするライブラリもあるようだが完全ではないようです。
参考: HTML5 CanvasをIEにも対応させる魔法excanvasの使い方 | DECONCEPTER
複数の画像を合成した画像を Data URI スキームで表現できるので
サンプルページにあるように Canvas で作った画像を画像タグで置換することもできる。

最後に

目的に合わせていろいろな方法を使いこなせるようになろう。
Canvas を使って面白そうなことができるのでやってみたい。

@kazoo04のアイコン素材を使わせて頂きました。
この場を借りて感謝します。ありがとうございます。

タグ(RSS)