l'essentiel est invisible pour les yeux

Tuesday, November 07, 2006

[Javascript] iTunes7のように画像を水面に反射させたように表示する

賛否両論ある、iTunes7のアートワークの3D表示
でも、画像を水面反射したような見せ方をするだけでCool!と思わせれるかも?しれない。
意外と簡単に作れるので自作のフレームワークに組み込むのもよさそうです。

で、一日一つ新しい事を習得する(と決めた)ための、今日のネタはCanvasタグ。
ネタ自体は既出ですがCanvasタグの練習がてら作ってみました。

デモ

画像のURLを直接入力するか、URLをリストから選んでCreateボタンを押すとアルバムのジャケットのアートワークが水面反射付きで表示される。

現在

  • Saffariでは動作未確認。(動作未確認。Canvasタグの実装にバグがある様子)

ソースはシンプル。

var UA = navigator.userAgent.toLowerCase();
var isIE = (UA.indexOf('msie') != -1) && !window.opera;

function addImageAndReflect(url, opacity) {
var opacity = opacity || 0.7;

/* image and reflection wrap element */
var imgWrapElm = document.createElement('div');
imgWrapElm.className = 'imgWrap';
var image = new Image;
image.src = url;
image.style.display = 'block';

if(isIE) {
var reflectImgElm = document.createElement('img');
reflectImgElm.src = url;
reflectImgElm.style.filter = "flipv progid:DXImageTransform.Microsoft.Alpha(opacity="+(1-opacity)*100+",finishopacity=0,style=1,startx=0,starty=0,finishx=0,finishy="+image.height+")"; // ⑤
imgWrapElm.appendChild(image);
imgWrapElm.appendChild(reflectImgElm);
document.body.appendChild(imgWrapElm);
} else {
/* canvas element */
var canvas = document.createElement('canvas');
image.onload = function() {
canvas.width = image.width;
canvas.height = image.height;
var ctx = canvas.getContext('2d');
with(ctx) {
save();
translate(0, image.height-1);
scale(1,-1);
drawImage(image, 0, 0);
restore();
var gradient = createLinearGradient(0, 0, 0, image.height);
gradient.addColorStop(1, "rgba(0, 0, 0, 1)");
gradient.addColorStop(0, "rgba(0, 0, 0, "+opacity+")");
globalCompositeOperation = 'destination-out';
fillStyle = gradient;
fillRect(0, 0, image.width, image.height);
}
imgWrapElm.appendChild(image);
imgWrapElm.appendChild(canvas);
document.body.appendChild(imgWrapElm);
}
}
}


①でcanvasタグとImageオブジェクトを作成する。
IEなどcanvas.getContextに対応していないブラウザのために処理を条件分岐。

②の解説。
Canvasへの画像の描画は、drawImageを使用する。drawImageをコールする前に画像の読み込みを完了している必要があるため、onloadイベントハンドラとしてCanvasタグの描画を行う。読み込みが完了していないのに、drawImageを呼び出すとExceptionを吐いてスクリプトが停止する。

③の解説。
原点を移動して、グリッドの座標を変換することで画像をY軸に対して反転させる。原点移動の際に-1をしているのは、オリジナルの画像との隙間を無くすため。

④の解説。
グラデーションをかけるための矩形図形を作成する。
画像のサイズと同じ長方形を作成し塗りつぶしを作成したグラデーションに指定する。

rgbaは、RGB値に加えて不透明度(0~1)を指定する。
上記のグラデーションは、黒色の完全に不透明の状態から、不透明度0.7の黒色までのグラデーションを作成している。
var gradient = createLinearGradient(0, 0, 0, image.height);  // ④
gradient.addColorStop(1, "rgba(0, 0, 0, 1)");
gradient.addColorStop(0, "rgba(0, 0, 0, 0.7)");

グラデーションを塗りつぶしに指定した矩形図形を画像の上に表示するように指定している。
参考: Canvas tutorial:Compositing

globalCompositeOperation = 'destination-out';

塗りつぶしを作成したグラデーションに指定してから、画像と同じサイズの図形を描画する。

fillStyle = gradient;
fillRect(0, 0, image.width, image.height);

と、意外と簡単に水面反射を作成することが出来る。

⑤はIE対応。
IEにはCanvasが実装されていないため、フィルタを用いて同様の効果を実現する。
flipvは画像を反転させるフィルタ。

参考
WHATWGのCanvas仕様
Canvas tutorial:Drawing shapes
Canvas tutorial:Compositing
Canvas tutorial:Transformations
Canvas tutorial:Applying styles and colors
Reflection.js