feDisplacementMapは元となる画像を変形して様々な効果をつけるものだが,実際に思った通りの効果をつけようとすると非常に面倒.というのもピクセルの移動量を色の濃淡で定義するためだ.単純なものであればsvgのもつグラデーション機能などを使えばよいが,限界もある.
従ってピクセル単位で色の指定が可能なcanvasを使って対処するのが最も汎用的であろうと考えた.事前に画像ソフト等でファイルを用意する手もあるが,javascriptでさっくりと画像を作れてしまうのは魅力.
- 画像に付けたい効果を思い浮かべる.
- ピクセルごとの移動量,移動方向を考える.
- 2で得た移動量を水平方向と垂直方向に分離する.
- 3で得た内容を元にcanvasにイメージを描画する.
ピクセルごとの移動量を色の濃さ:0〜255にマッピングする.(128は移動しないことを表す)
水平方向はredで垂直方向はgreenで表現する.(blueとalphaは無視してよい.) - この時点で画像ファイルを保存する,もしくはgetDataURLでURL文字列として画像を取得する.
- svgのfeDisplacementMap要素において5の画像を参照する.
×=
svgの内容.
<?xml version="1.0" standalone="no"?>
<svg width="200px" height="200px" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<defs>
<filter id="feDisplacementMap" image-rendering="optimizeSpeed" color-interpolation-filters="sRGB" filterRes="1000,1000">
<feImage xlink:href="http://www.h2.dion.ne.jp/~defghi/svgWarp/gradient.png" x="0" y="0" width="200" height="200" result="map"/>
<feDisplacementMap in="SourceGraphic" in2="map" xChannelSelector="R" yChannelSelector="G" scale="140"/>
</filter>
</defs>
<image filter="url(#feDisplacementMap)"
xlink:href="http://www.konami.jp/products/dl_xbox_dracula_hd/images/charlotte.jpg"
width="200" height="200"/>
</svg>
canvasによる画像の生成.
中心から外側に向かうピクセル変異を表現するため,角度毎に色を設定するようにした.kの値を1に変更すると何をしたかったのか判ると思う.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<style type="text/css">
</style>
<script type="text/javascript">
window.onload = function(){
var canvas = document.getElementById("grad");
var ctx = canvas.getContext("2d");
var id = ctx.createImageData(800, 800)
var d = id.data;
for(var y = 0; y < 800; y++){
for(var x = 0; x < 800; x++){
var deg = getDeg(x, y);
var dist = getDistance(x, y);
var k = Math.max(dist - 300, 0) / 300;
var pos = (x + 800 * y) * 4;
//R
d[pos + 0] = 128 + 128 * Math.cos(deg) * k;
//G
if(x == 400 && y < 400){
//境界調整
d[pos + 1] = 128 + 128 * k;
}else{
d[pos + 1] = 128 + 128 * Math.sin(deg) * k;
}
//B
d[pos + 2] = 0;
//A
d[pos + 3] = 255;
}
}
ctx.putImageData(id, 0, 0);
function getDeg(x, y){
var _x = x - 400;
var _y = y - 400;
var deg = _x != 0 ? Math.atan(_y/_x): Math.PI/2;
return _x < 0 ? deg: deg + Math.PI;
}
function getDistance(x, y){
return Math.sqrt(Math.pow(x - 400, 2) + Math.pow(y - 400, 2));
}
};
</script>
</head>
<body>
<canvas width="800px" height="800px" id="grad"/>
</body>
</html>
0 件のコメント:
コメントを投稿