JavaScript プログラミング講座

 

トランスフォームについて

 


■トランスフォームについて

 
CSS Transforms は、CSS3 世代の機能です。
 
http://www.w3.org/TR/css3-transforms/ (Working Draft)
 
エレメントをアフィン変換するには、スタイルの transform を使用します。
 
■スタイルの transform を設定する関数
 
スタイルの transform を設定する関数

// ------------------------------------------------------------
// スタイルの transform を設定する関数
// ------------------------------------------------------------
function StyleDeclarationSetTransform(style,value){
	var list = [
		"transform",
		"webkitTransform",
		"MozTransform",
		"msTransform",
		"OTransform"
	];
	var i;
	var num = list.length;
	for(i=0;i < num;i++){
		if(style[list[i]] !== undefined){
			style[list[i]] = value;
			return true;
		}
	}
	return false;
}
 
■指定可能な表記方法
 
以下の関数表記が可能です。(一部抜粋)
 
空白で区切って、複数の関数表記を指定する事もできます。
 
この場合、最後尾から順番に変換(乗算)されます。
 
表記説明
translate(tx,ty)位置を設定する。
rotate(angle)回転を設定する。
scale(sx,sy)拡大縮小率を設定する。(1.0 で等倍)
matrix(a,b,c,d,tx,ty)2D 用マトリックスを設定する。
 
表記説明
translateX(tx)水平位置を設定する。
translateY(ty)垂直位置を設定する。
scaleX(sx)水平方向の拡大縮小率を設定する。(1.0 で等倍)
scaleY(sy)垂直方向の拡大縮小率を設定する。(1.0 で等倍)
 
■トランスフォームの動作について
 
現在(トランスフォーム適用直前)の姿勢が、原点となります。
 
自身の存在矩形自体は、デフォルトの場所(原点)にそのまま残ります。
 
トランスフォームを適用しても、周りのレイアウトが崩れる事はありません
 
要素自体は、変換後の見た目通りに動作します。
 
親がスクロールバーを含む場合、すべて表示されるようにスクロール最大値が補正されます。
 
 
■設定例
 
角度を10度に設定する

// ------------------------------------------------------------
// エレメント
// ------------------------------------------------------------
// エレメントを作成する
var element = document.createElement("div");

// BODY のノードリストに登録する
document.body.appendChild(element);

// ------------------------------------------------------------
// CSSStyleDeclaration オブジェクトを取得
// ------------------------------------------------------------
var style = element.style;

// 基本的なスタイルを設定する
style.cssText = "width:100px; height:100px; border-top:2px solid #f44; border-left:2px solid #44f; background:#ccc;";

// ------------------------------------------------------------
// トランスフォームを設定する
// ------------------------------------------------------------
StyleDeclarationSetTransform(style,"rotate(10deg)");
 
拡大率を(2,3) → 角度を(10deg) → 位置を(100px,200px) の順番で設定する

// ------------------------------------------------------------
// エレメント
// ------------------------------------------------------------
// エレメントを作成する
var element = document.createElement("div");

// BODY のノードリストに登録する
document.body.appendChild(element);

// ------------------------------------------------------------
// CSSStyleDeclaration オブジェクトを取得
// ------------------------------------------------------------
var style = element.style;

// 基本的なスタイルを設定する
style.cssText = "width:100px; height:100px; border-top:2px solid #f44; border-left:2px solid #44f; background:#ccc;";

// ------------------------------------------------------------
// トランスフォームを設定する
// ------------------------------------------------------------
StyleDeclarationSetTransform(style,"translate(100px,200px) rotate(10deg) scale(2,3)");
 


 

トランスフォームの基点について

 


■トランスフォームの基点について

 
トランスフォームの基点を変更するには、transform-origin スタイルを使用します。
 
デフォルトでは、要素の中央が基点となります。
 
■スタイルの transform-origin を設定する関数
 
スタイルの transform-origin を設定する関数

// ------------------------------------------------------------
// スタイルの transform-origin を設定する関数
// ------------------------------------------------------------
function StyleDeclarationSetTransformOrigin(style,value){
	var list = [
		"transformOrigin",
		"webkitTransformOrigin",
		"MozTransformOrigin",
		"msTransformOrigin",
		"OTransformOrigin"
	];
	var i;
	var num = list.length;
	for(i=0;i < num;i++){
		if(style[list[i]] !== undefined){
			style[list[i]] = value;
			return true;
		}
	}
	return false;
}
 
■指定可能な表記方法
 
以下の単位表記が可能です。
 
表記説明
0px 0px基点をピクセル値で指定する。(左端,上端)
50% 50%基点をパーセント値で指定する。(左端,上端)
 
表記例説明
0% 0%左端上端を基点に設定する。
50% 50%中央を基点に設定する。(デフォルト値)
100% 100%右端下端を基点に設定する。
 
■要素の原点について
 
transform-origin を変更した場合、見た目が変化しない事が優先されます。
 
この仕様は、原点の方が ( +水平基点, +垂直基点 ) 分ずれる事で実現されます。
 
■すべての要素の座標系(原点)を統一するには?
 
デフォルトの状態では、要素ごとに、原点が異なる座標系を持っています。
 
これにより transform は、グローバル座標系への配置として機能しません。
 
すべての要素の座標系を統一したい場合、「0px 0px」を指定します。
 
左上端以外を基点にしたい場合は、行列の乗算順序を工夫して吸収します。
 
position:absolute; の指定も必要です。
 
■設定例
 
トランスフォームの左上を基点に設定する

// ------------------------------------------------------------
// エレメント
// ------------------------------------------------------------
// エレメントを作成する
var element = document.createElement("div");

// BODY のノードリストに登録する
document.body.appendChild(element);

// ------------------------------------------------------------
// CSSStyleDeclaration オブジェクトを取得
// ------------------------------------------------------------
var style = element.style;

// 基本的なスタイルを設定する
style.cssText = "width:100px; height:100px; border-top:2px solid #f44; border-left:2px solid #44f; background:#ccc;";

// ------------------------------------------------------------
// トランスフォームの基点を設定する
// ------------------------------------------------------------
StyleDeclarationSetTransformOrigin(style,"0% 0%");

// ------------------------------------------------------------
// トランスフォームを設定する
// ------------------------------------------------------------
StyleDeclarationSetTransform(style,"rotate(10deg)");
 


 

エレメントを移動する

 


■エレメントを移動する

 
移動成分に作用する関数表記は、以下の種類があります。
 
表記説明
translate(tx,ty)位置を設定する。(水平位置,垂直位置)
translateX(tx)水平位置を設定する。
translateY(ty)垂直位置を設定する。
 
■単位表記について
 
単位指定を省略する事はできません。
 
px(単位:ピクセル値)などを指定します。
 
■設定例
 
水平方向に 200px、垂直方向に 100px 移動する

// ------------------------------------------------------------
// エレメント
// ------------------------------------------------------------
// エレメントを作成する
var element = document.createElement("div");

// BODY のノードリストに登録する
document.body.appendChild(element);

// ------------------------------------------------------------
// CSSStyleDeclaration オブジェクトを取得
// ------------------------------------------------------------
var style = element.style;

// 基本的なスタイルを設定する
style.cssText = "width:100px; height:100px; border-top:2px solid #f44; border-left:2px solid #44f; background:#ccc;";

// ------------------------------------------------------------
// トランスフォームを設定する
// ------------------------------------------------------------
StyleDeclarationSetTransform(style,"translate(200px,100px)");
 


 

エレメントを回転する

 


■エレメントを回転する

 
回転成分に作用する関数表記は、以下の種類があります。
 
表記説明
rotate(angle)回転を設定する。
 
■単位表記について
 
角度(deg)とラジアン(rad)の指定が可能です。
 
単位指定を省略する事はできません。
 
表記説明
0deg度数単位で角度を指定する。(時計回りで正の方向)
0radラジアン単位で角度を指定する。(時計回りで正の方向)
 
■設定例
 
エレメントを 30 度回転する

// ------------------------------------------------------------
// エレメント
// ------------------------------------------------------------
// エレメントを作成する
var element = document.createElement("div");

// BODY のノードリストに登録する
document.body.appendChild(element);

// ------------------------------------------------------------
// CSSStyleDeclaration オブジェクトを取得
// ------------------------------------------------------------
var style = element.style;

// 基本的なスタイルを設定する
style.cssText = "width:100px; height:100px; border-top:2px solid #f44; border-left:2px solid #44f; background:#ccc;";

// ------------------------------------------------------------
// トランスフォームを設定する
// ------------------------------------------------------------
StyleDeclarationSetTransform(style,"rotate(30deg)");
 


 

エレメントを拡大縮小する

 


■エレメントを拡大縮小する

 
拡大縮小成分に作用する関数表記は、以下の種類があります。
 
表記説明
scale(sx,sy)拡大縮小率を設定する。(1.0 で等倍)
scaleX(sx)水平方向の拡大縮小率を設定する。(1.0 で等倍)
scaleY(sy)垂直方向の拡大縮小率を設定する。(1.0 で等倍)
 
■単位表記について
 
単位の指定は不要です。
 
1.0 で等倍となります。
 
■設定例
 
エレメントを水平方向に 2.0 倍、垂直方向に 0.5 倍拡大する

// ------------------------------------------------------------
// エレメント
// ------------------------------------------------------------
// エレメントを作成する
var element = document.createElement("div");

// BODY のノードリストに登録する
document.body.appendChild(element);

// ------------------------------------------------------------
// CSSStyleDeclaration オブジェクトを取得
// ------------------------------------------------------------
var style = element.style;

// 基本的なスタイルを設定する
style.cssText = "width:100px; height:100px; border-top:2px solid #f44; border-left:2px solid #44f; background:#ccc;";

// ------------------------------------------------------------
// トランスフォームを設定する
// ------------------------------------------------------------
StyleDeclarationSetTransform(style,"scale(2.0,0.5)");
 


 

エレメントに2D用の行列を適用する

 


■エレメントに2D用の行列を適用する

 
2D用の行列を指定する関数表記は、以下の種類があります。
 
表記説明
matrix(a,b,c,d,tx,ty)2D 用マトリックスを設定する。
 
■プロパティについて
 
赤いベクトルが、 x 軸成分を表しており ( a , b ) ベクトルとなります。
 
青いベクトルが、 y 軸成分を表しており ( c , d ) ベクトルとなります。
 
赤と青の2つのベクトルの交わる点は、座標成分を表していて (tx, ty) 位置ベクトルとなります。
 
a , b , c , d の4つのパラメータの組み合わせにより、回転、拡大、せん断といった要素が決定します。
 
tx, ty の2つのパラメータにより、位置の要素が決定します。
 
 
■単位表記について
 
単位の指定は不要です。
 

■2D用の行列を計算するクラス

 
2D用の行列を計算するクラス

// ------------------------------------------------------------
// 2D用の行列を計算するクラス
// ------------------------------------------------------------
function Matrix32 (a,b,c,d,tx,ty) {
	this.a  = ((a  === undefined) ? 1.0 : a );
	this.b  = ((b  === undefined) ? 0.0 : b );
	this.c  = ((c  === undefined) ? 0.0 : c );
	this.d  = ((d  === undefined) ? 1.0 : d );
	this.tx = ((tx === undefined) ? 0.0 : tx);
	this.ty = ((ty === undefined) ? 0.0 : ty);
}
Matrix32.prototype = {

	// ------------------------------------------------------------
	// 単位行列化
	// ------------------------------------------------------------
	identity:function(){
		this.a  = 1.0; this.b  = 0.0;
		this.c  = 0.0; this.d  = 1.0;
		this.tx = 0.0; this.ty = 0.0;
	},

	// ------------------------------------------------------------
	// 移動成分を乗算
	// ------------------------------------------------------------
	translate:function(tx,ty){
		this.tx += tx;
		this.ty += ty;
	},

	// ------------------------------------------------------------
	// 回転成分を乗算(ラジアン)
	// ------------------------------------------------------------
	rotate:function(angle){
		var cos = Math.cos(angle);
		var sin = Math.sin(angle);
		var a  = this.a;
		var c  = this.c;
		var tx = this.tx;
		this.a  = a  * cos + this.b  * -sin;
		this.b  = a  * sin + this.b  *  cos;
		this.c  = c  * cos + this.d  * -sin;
		this.d  = c  * sin + this.d  *  cos;
		this.tx = tx * cos + this.ty * -sin;
		this.ty = tx * sin + this.ty *  cos;
	},

	// ------------------------------------------------------------
	// 拡大縮小成分を乗算
	// ------------------------------------------------------------
	scale:function(sx,sy){
		this.a  = this.a  * sx;
		this.b  = this.b  * sy;
		this.c  = this.c  * sx;
		this.d  = this.d  * sy;
		this.tx = this.tx * sx;
		this.ty = this.ty * sy;
	},

	// ------------------------------------------------------------
	// 行列を乗算 (this * m)
	// ------------------------------------------------------------
	multiply:function(m){
		var a  = this.a;
		var c  = this.c;
		var tx = this.tx;
		this.a  = a  * m.a + this.b  * m.c;
		this.b  = a  * m.b + this.b  * m.d;
		this.c  = c  * m.a + this.d  * m.c;
		this.d  = c  * m.b + this.d  * m.d;
		this.tx = tx * m.a + this.ty * m.c + m.tx;
		this.ty = tx * m.b + this.ty * m.d + m.ty;
	},

	// ------------------------------------------------------------
	// 座標を変換
	// ------------------------------------------------------------
	transformPosition:function(pos){
		return {
			x:pos.x * this.a + pos.y * this.c + this.tx,
			y:pos.x * this.b + pos.y * this.d + this.ty
		};
	},

	// ------------------------------------------------------------
	// ベクトルを変換
	// ------------------------------------------------------------
	transformVector:function(vec){
		return {
			x:vec.x * this.a + vec.y * this.c,
			y:vec.x * this.b + vec.y * this.d
		};
	},

	// ------------------------------------------------------------
	// 逆行列化
	// ------------------------------------------------------------
	invert: function(){
		var a  = this.a;
		var b  = this.b;
		var c  = this.c;
		var d  = this.d;
		var tx = this.tx;
		var ty = this.ty;
		this.a  = 1.0; this.b  = 0.0;
		this.c  = 0.0; this.d  = 1.0;
		this.tx = 0.0; this.ty = 0.0;

		var b_  = b;
		var d_  = d;
		var ty_ = ty;

		if(a){
			this.a  /= a;
			     b_ /= a;
		}
		     d_  -= c  *      b_;
		this.c   -= c  * this.a;
		     ty_ -= tx *      b_;
		this.tx  -= tx * this.a;

		if(d_){
			this.c /= d_;
		}
		this.tx -= ty_ * this.c;
		this.a  -= b_  * this.c;

		if(a){
			this.b /= a;
			     b /= a;
		}
		     d  -= c  *      b;
		this.d  -= c  * this.b;
		     ty -= tx *      b;
		this.ty -= tx * this.b;

		if(d){
			this.d /= d;
		}
		this.ty -= ty * this.d;
		this.b  -= b  * this.d;
	},

	// ------------------------------------------------------------
	// 複製
	// ------------------------------------------------------------
	clone:function(){
		return new Matrix32(
			this.a,  this.b,
			this.c,  this.d,
			this.tx, this.ty
		);
	},

	// ------------------------------------------------------------
	// 文字列を取得
	// ------------------------------------------------------------
	toString:function(){
		return "matrix(" +
			this.a.toFixed(20)  + "," +
			this.b.toFixed(20)  + "," +
			this.c.toFixed(20)  + "," +
			this.d.toFixed(20)  + "," +
			this.tx.toFixed(20) + "," +
			this.ty.toFixed(20) + 
		")";
	}
};
 
■使用例
 
拡大率(2,3) → 角度(10度) → 位置(100,200) の順番で乗算する

// ------------------------------------------------------------
// Matrix32 オブジェクトを作成
// ------------------------------------------------------------
var mtx32 = new Matrix32();

// ------------------------------------------------------------
// 各成分を順番に乗算する
// ------------------------------------------------------------
// 単位行列化
mtx32.identity();

// 拡大縮小成分を乗算
mtx32.scale(2.0,3.0);

// 回転成分を乗算(ラジアン)
mtx32.rotate(10 * Math.PI / 180);

// 移動成分を乗算
mtx32.translate(100,200);

// ------------------------------------------------------------
// 出力テスト
// ------------------------------------------------------------
console.log(mtx32.toString()); // matrix(1.96961,0.34729,-0.52094,2.95442,100,200) 
 
3つの行列を順番に乗算する

// ------------------------------------------------------------
// Matrix32 オブジェクトを作成
// ------------------------------------------------------------
var mtx32_a = new Matrix32(1,0,0,1,0,0);

// 拡大縮小成分
mtx32_a.scale(2.0,3.0);

// ------------------------------------------------------------
// Matrix32 オブジェクトを作成
// ------------------------------------------------------------
var mtx32_b = new Matrix32(1,0,0,1,0,0);

// 回転成分(ラジアン)
mtx32_b.rotate(10 * Math.PI / 180);

// ------------------------------------------------------------
// Matrix32 オブジェクトを作成
// ------------------------------------------------------------
var mtx32_c = new Matrix32(1,0,0,1,0,0);

// 移動成分
mtx32_c.translate(100,200);

// ------------------------------------------------------------
// 行列を順番に乗算 a * b * c
// ------------------------------------------------------------
mtx32_a.multiply(mtx32_b);
mtx32_a.multiply(mtx32_c);

// 出力テスト
console.log(mtx32_a.toString()); // matrix(1.96961,0.34729,-0.52094,2.95442,100,200) 
 

■設定例

 
拡大率(2,3) → 角度(10度) → 位置(100,200) の順番で乗算した行列を設定する

// ------------------------------------------------------------
// Matrix32 オブジェクトを作成
// ------------------------------------------------------------
var mtx32 = new Matrix32();

// ------------------------------------------------------------
// 各成分を順番に乗算する
// ------------------------------------------------------------
// 単位行列化
mtx32.identity();

// 拡大縮小成分を乗算
mtx32.scale(2.0,3.0);

// 回転成分を乗算(ラジアン)
mtx32.rotate(10 * Math.PI / 180);

// 移動成分を乗算
mtx32.translate(100,200);

// ------------------------------------------------------------
// エレメント
// ------------------------------------------------------------
// エレメントを作成する
var element = document.createElement("div");

// BODY のノードリストに登録する
document.body.appendChild(element);

// ------------------------------------------------------------
// CSSStyleDeclaration オブジェクトを取得
// ------------------------------------------------------------
var style = element.style;

// 基本的なスタイルを設定する
style.cssText = "width:100px; height:100px; border-top:2px solid #f44; border-left:2px solid #44f; background:#ccc;";

// ------------------------------------------------------------
// トランスフォームを設定する
// ------------------------------------------------------------
StyleDeclarationSetTransform(style,mtx32.toString());