Grayscale Image Gallery with filter effects
公開:
更新:
以前、「元画像を変更せずに画像をグレースケールで表示する」という記事で「canvasでの変換は重いから現実的じゃない」みたいなことを書いたのですが、-webkit-filter
が使えるようになった(なる)ことにより、もう少し負担を減らして使えるのではないかと思いました。
そこで、なるべくcanvas
での変換を行わずにやってみようと思います。
今回作ったものはこれです。
2012年3月5日 21:24 追記:
Firefoxをご利用の方で、環境によっては、CSSグラデーションをbackground-size
で小さくしてbackground-repeat
で敷き詰めると、gfx.direct2d.disabledがfalseの場合にフリーズするようです(Firefox 10.0.2で確認)。
Firefoxで見ても安全なデモページはこちらです。
本来のデモページから小さな点々を除いたものになります。
使うものはHTML, CSS, SVG, JavaScriptです。
まずはHTMLから。
<ul class="gallery">
<li>
<a href="#">
<span><img src="images/01.jpg" width="200" height="200" alt="" /></span>
</a>
</li>
...
</ul>
次にCSSを。SVG用のCSSも一緒に書いていきます。
どうでもいい部分は省きます。
.gallery {
background : #fff;
list-style : none;
}
/* clearfix */
.gallery:after {
clear : both;
display: block;
content: "";
}
.gallery li {
float : left;
margin: 10px;
width : 200px;
height: 200px;
}
.gallery a {
display : block;
width : 200px;
height : 200px;
text-decoration: none;
}
.gallery span {
display : block;
position : relative;
-webkit-transition: opacity linear 0.5s;
-moz-transition : opacity linear 0.5s;
-ms-transition : opacity linear 0.5s;
-o-transition : opacity linear 0.5s;
transition : opacity linear 0.5s;
}
/* 小さい点々とぼかしたような効果を出すためのグラデーション */
.gallery span:before {
position : absolute;
top : 0;
left : 0;
z-index : 9;
width : 200px;
height : 200px;
background-image : -webkit-radial-gradient(center, circle, rgba(0, 0, 0, 0.1) 1px, transparent 1px),
-webkit-radial-gradient(center, circle, transparent 40%, rgba(0, 0, 0, 0.5));
background-image : -moz-radial-gradient(center, circle, rgba(0, 0, 0, 0.1) 1px, transparent 1px),
-moz-radial-gradient(center, circle, transparent 40%, rgba(0, 0, 0, 0.5));
background-image : -ms-radial-gradient(center, circle, rgba(0, 0, 0, 0.1) 1px, transparent 1px),
-ms-radial-gradient(center, circle, transparent 40%, rgba(0, 0, 0, 0.5));
background-image : -o-radial-gradient(center, circle, rgba(0, 0, 0, 0.1) 1px, transparent 1px),
-o-radial-gradient(center, circle, transparent 40%, rgba(0, 0, 0, 0.5));
background-image : radial-gradient(circle at center, rgba(0, 0, 0, 0.2) 1px, transparent 1px),
radial-gradient(circle at center, transparent 40%, rgba(0, 0, 0, 0.5));
background-size : 4px 4px, 100% 100%;
background-repeat: repeat, no-repeat;
content : "";
}
.gallery img {
/* Chrome 19+ */
-webkit-filter: grayscale(1);
/* Firefox 3.5+ */
filter : url(filter.svg#grayscale);
/* Internet Explorer 5.5 - 9.0 */
filter : progid:DXImageTransform.Microsoft.BasicImage(grayscale=1);
}
/* マウスオーバーでグレースケール画像を透明に */
.gallery a:hover span {
opacity: 0;
filter : progid:DXImageTransform.Microsoft.Alpha(opacity=0);
}
/* Opera用 */
.gallery svg {
width : 200px;
height: 200px;
}
/* Opera用 */
image {
filter: url(filter.svg#grayscale);
}
一応、標準のグラデーションの指定は新しい構文にしてあります。
img
で指定した画像をグレースケールにするのですが、ここで各ブラウザごとの指定を行います。
CSSフィルタもSVGフィルタも使えないOperaですが、画像をSVGにすることでSVGフィルタの効果を使うことができます。
filter.svgの中身は次のようになっています。
グレースケール用の指定はFilter Effects 1.0に載っています。
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="grayscale">
<feColorMatrix type="matrix"
values="0.2126 0.7152 0.0722 0 0
0.2126 0.7152 0.0722 0 0
0.2126 0.7152 0.0722 0 0
0 0 0 1 0"/>
</filter>
</defs>
</svg>
これらのどれも使えないブラウザにはcanvas
によるData URI変換で対応します。
最後にjQueryを使って色々やります。
jQuery(function ($) {
// フィルタが使えるかどうかのチェック
var dummy = document.createElement('div'),
checkStyle = function (el) {
return el.currentStyle || document.defaultView.getComputedStyle(el, null);
},
svgFlag = false,
canvasFlag = false;
document.body.appendChild(dummy);
if (checkStyle(dummy).webkitTransition !== undefined) {
// webkitTransitionがあるかどうか
// webkitTransitionが使えて、webkitFilterがあるかどうか
canvasFlag = checkStyle(dummy).webkitFilter ? false : true;
} else if (checkStyle(dummy).msTransition !== undefined) {
// msTransitionがあるかどうか(IE 10かどうか)
canvasFlag = true;
} else if (checkStyle(dummy).OTransition !== undefined) {
// OTransitionがある(Operaかどうか)
svgFlag = true;
}
document.body.removeChild(dummy);
// チェック終わり
$('.gallery').find('a').each(function () {
var img = $(this).find('img').eq(0),
imgUrl = img.attr('src'),
imgW = img.width(),
imgH = img.height();
// svgFlag は Opera のみ true になる
if (svgFlag) {
// 画像をSVGの埋め込み画像に
var svgImg = '<svg width="' + imgW + '" height="' + imgH + '"><image xlink:href="' + imgUrl + '" width="' + imgW + '" height="' + imgH + '" /></svg>';
img.replaceWith(svgImg);
}
// canvasFlagはwebkitFilterのないWebkitとIE 10でtrue
if (canvasFlag) {
// 画像がロードされたらData URIに変換
img.load(function () {
this.src = grayscale(this.src);
});
}
// 元々の画像をa要素の背景として指定する
$(this).css('background-image', 'url(' + imgUrl + ')');
});
/*!
* Grayscale Image Hover
* http://webdesignerwall.com/tutorials/html5-grayscale-image-hover
*/
function grayscale(src) {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var imgObj = new Image();
imgObj.src = src;
canvas.width = imgObj.width;
canvas.height = imgObj.height;
ctx.drawImage(imgObj, 0, 0);
var imgPixels = ctx.getImageData(0, 0, canvas.width, canvas.height);
for (var y = 0; y < imgPixels.height; y++) {
for (var x = 0; x < imgPixels.width; x++) {
var i = (y * 4) * imgPixels.width + x * 4;
var avg = (imgPixels.data[i] + imgPixels.data[i + 1] + imgPixels.data[i + 2]) / 3;
imgPixels.data[i] = avg;
imgPixels.data[i + 1] = avg;
imgPixels.data[i + 2] = avg;
}
}
ctx.putImageData(imgPixels, 0, 0, 0, 0, imgPixels.width, imgPixels.height);
return canvas.toDataURL();
}
});
コメント入れておいたので、やりたいことは分かると思います。
ブラウザ分岐は適当なので信用しないほうがいいです……。
canvas
を使った変換は「HTML5 Grayscale Image Hover」のコードを使用しました。
IE 7以下ではa:hover span
の部分が動きませんが、この辺はやりようはあると思います。
それと、画像をspan
で囲んでいるのはFirefox以外では擬似要素のみのTransitionができないからです。特に必要なものではありません。
canvas
変換による負担を減らしたかっただけなので、今回はこれで。