まず図のように3次ベジェ曲線として始点P1から終点P2があり、曲線のポイントとなるP2とP3があるとします。
カーブのアニメーション(3次ベジェ曲線)
カーブを描いてアニメーションするには、3次ベジェ曲線を使用して表現することができます。
デモ
3次ベジェ曲線について
次に図のように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 | ... | 得た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);
});