カーブのアニメーション(3次ベジェ曲線)

カーブを描いてアニメーションするには、3次ベジェ曲線を使用して表現することができます。

デモ

3次ベジェ曲線について

まず図のように3次ベジェ曲線として始点P1から終点P2があり、曲線のポイントとなるP2とP3があるとします。

次に図のようにP1からP2のポイントP'1、P2からP3のポイントP'2、P3からP4のポイントP'3を設け、それぞれP'1からp'3のポイントをP1からP4で移動する時間と同じ時間移動させるようにします。

同様に次の図のようにP'1からP'2のポイントP''1、P'2からP'3のポイントP''2を設け、それぞれP''1とP''2のポイントを移動させるようにします。

あとは図のようにP''1からP''2を移動するポイントを設けて移動させればカーブを描くことができます。

計算式

3次ベジェ曲線での座標取得は次の計算式を使用します。

x=(1-t)三乗×x1)+(3×(1-t)二乗×t×x2)+(3×(1-t)×t二乗×x3)+(t三乗×x4)、y=(1-t)三乗×y1)+(3×(1-t)二乗×t×y2)+(3×(1-t)×t二乗×y3)+(t三乗×y4)

x  ...  得たX座標
y  ...  得たY座標
t  ...  経過時間の割合
x1  ...  始点ポイントのX座標
y1  ...  始点ポイントのY座標
x2  ...  制御ポイント1のX座標
y2  ...  制御ポイント1のY座標
x3  ...  制御ポイント2のX座標
y3  ...  制御ポイント2のY座標
x4  ...  終点ポイントのX座標
y4  ...  終点ポイントのY座標

サンプルコード

HTML

<div id="stage">
	<div class="point" id="start"></div>
	<div class="point" id="end"></div>
	<div class="point" id="control1"></div>
	<div class="point" id="control2"></div>
	<div class="point" id="move"></div>
</div>

CSS

div#stage {
	position:relative;
	width:300px;
	height:300px;
}

	/* ポイント */
	div#stage .point {
		position:absolute;
		top:0;
		left:0;
		border-radius:50%;
	}

	/* 始点と終点のポイント */
	div#stage #start,
	div#stage #end {
		width:14px;
		height:14px;
		background-color:#ed1e79;
	}

	/* コントロールポイント */
	div#stage #control1,
	div#stage #control2 {
		width:10px;
		height:10px;
		background-color:#999;
	}

	/* 移動ポイント */
	div#stage #move {
		width:20px;
		height:20px;
		background-color:#265cff;
	}

	/* 始点ポイントの位置 */
	div#stage #start {
		top:90px;
	}

	/* 終点ポイントの位置 */
	div#stage #end {
		top:286px;
		left:286px;
	}

	/* コントロールポイント1の位置 */
	div#stage #control1 {
		left:240px;
	}

	/* コントロールポイント2の位置 */
	div#stage #control2 {
		top:180px;
		left:288px;
	}

JavaScript

/**
 * 3次ベジェ曲線でのポイント座標位置取得
 * @param {number} t 経過時間の割合
 * @param {number} x1 始点ポイントのX座標
 * @param {number} y1  始点ポイントのY座標
 * @param {number} x2 制御ポイント1のX座標
 * @param {number} y2 制御ポイント1のY座標
 * @param {number} x3 制御ポイント2のX座標
 * @param {number} y3 制御ポイント2のY座標
 * @param {number} x4 終点ポイントのX座標
 * @param {number} y4 終点ポイントのY座標
 * @returns {{x:number,y:number}} 得た座標位置を返す
 */
var getPointBezierCurve = function(t, x1, y1, x2, y2, x3, y3, x4, y4) {
	var tp = 1 - t,
	    x = (Math.pow(tp, 3) * x1) + (3 * Math.pow(tp, 2) * t * x2) + (3 * tp * Math.pow(t, 2) * x3) + (Math.pow(t, 3) * x4),
	    y = (Math.pow(tp, 3) * y1) + (3 * Math.pow(tp, 2) * t * y2) + (3 * tp * Math.pow(t, 2) * y3) + (Math.pow(t, 3) * y4);

	return { x : x, y : y };
};

// 要素の取得
var p1Elem   = document.getElementById('start'),
    p2Elem   = document.getElementById('end'),
    c1Elem   = document.getElementById('control1'),
    c2Elem   = document.getElementById('control2'),
    moveElem = document.getElementById('move');

// アニメーションする時間(ミリ秒)
var time = 1000;

// 始点ポイントの位置
var startPos = {
	x : p1Elem.offsetLeft,
	y : p1Elem.offsetTop
};

// 終点ポイントの位置
var endPos = {
	x : p2Elem.offsetLeft,
	y : p2Elem.offsetTop
};

// 制御ポイント1の位置
var ctrl1Pos = {
	x : c1Elem.offsetLeft,
	y : c1Elem.offsetTop
};

// 制御ポイント2の位置
var ctrl2Pos = {
	x : c2Elem.offsetLeft,
	y : c2Elem.offsetTop
};

/**
 * 位置の補正
 */
var correct = function(x, y) {
	return {
		x : x - ((moveElem.clientWidth - p1Elem.clientWidth) / 2),
		y : y - ((moveElem.clientHeight - p1Elem.clientHeight) / 2)
	};
};

/**
 * 座標を要素に設定
 */
var setPosition = function(x, y) {
	moveElem.style.left = x + 'px';
	moveElem.style.top  = y + 'px';
};

// 初期の位置を設定
var point = correct(startPos.x, startPos.y);
setPosition(point.x, point.y);

// アニメーション開始時間
var startTime = (new Date()).getTime();

// アニメーション
var sId = setInterval(function() {
	// 経過時間
	var elapsedTime = (new Date()).getTime() - startTime;

	// 移動する要素の次の位置を取得
	var point = getPointBezierCurve(
		elapsedTime / time,
		startPos.x,
		startPos.y,
		ctrl1Pos.x,
		ctrl1Pos.y,
		ctrl2Pos.x,
		ctrl2Pos.y,
		endPos.x,
		endPos.y
	);

	// 移動する位置の補正
	point = correct(point.x, point.y);

	// 位置を要素に設定
	setPosition(point.x, point.y);

	// 時間になったらアニメーションを終了
	if (elapsedTime >= time) clearInterval(sId);
});

JavaScript逆引きリファレンス一覧へ戻る