こんにちは、フロントエンドエンジニアのはっちゃんです。
今日は「スマホやタブレットで見たときだけ、コインがフリップでめくれるようなアニメーション」を作っていきたいと思います。
前回の記事と組み合わせると、LIGブログのメンバーページと同じ仕様になります。
コインのようにくるくる回転するホバーアニメーションを作ってみよう【いろんな動きを作ってみようシリーズ】
画像を用意
前回同様、表と裏の画像を用意します。
HTML
前回のHTMLを少しだけ修正します。JSのトリガーとなるclassを追加します。
<div class="flip js-flip"><!-- js-flipを追加 -->
<div class="flip-inner js-flip-inner"><!-- js-flip-innerを追加 -->
<div class="front">
<img class="thumbnail" src="https://82mou.github.io/src/images/coin-omote.jpg" alt="コイン表">
</div>
<div class="back">
<img class="thumbnail thumbnail-hover" src="https://82mou.github.io/src/images/coin-ura.jpg" alt="コイン裏">
</div>
</div>
</div>
CSS
CSSも修正します。hoverではなく、classの付け替えて回転するような仕組みにします。
もともと.flip-innerにデフォルトでついていたtransition: transform .8s ease;は、特定のclassが付いたときのみ適用されるようにします。
.flip {
position: absolute;
left: 50%;
transform: translateX(-50%);
width: 300px;
height: 300px;
&.is-pc {
.flip-inner {
transition: transform .8s ease;
}
&.is-hover {
.flip-inner {
transform: rotateY(180deg);
}
}
}
&.is-animating {
.flip-inner {
transition: transform .8s ease;
}
}
/*&:hover {*/
/* .flip-inner {*/
/* transform: rotateY(180deg);*/
/* }*/
/*}*/
.flip-inner {
transform-style: preserve-3d;
transform: rotateY(0deg);
height: 100%;
width: 100%;
.front, .back {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: 50%;
overflow: hidden;
}
.front {
transform: translateZ(1px);
}
.back {
transform: rotateY(180deg);
}
.thumbnail {
width: 100%;
}
}
}
JS
// UA判定のJSライブラリーを実行
const uAParser = UAParser();
const $flip = $('.js-flip');
const $flipInner = $('.js-flip-inner');
const flipWidth = $flip.width();
// 円の直径を180度に割り当てる
const rate = 180 / flipWidth;
// .flip-innerクラスにつけるtransitionのduration値
const DURATION = 800;
// touchMoveのフラグ
let moving = false;
// touchEnd後、アニメーションしているときのフラグ
let animating = false;
// タップし始めたときのx座標
let startPointX = 0;
// タップを離したときのx座標
let endPointX = 0;
// 現在の回転量
let currentRotationVal = 0;
// デバイスを判定
let judgeDevice = () => {
(uAParser.device.type !== 'mobile') ? $flip.addClass('is-pc') : $flip.removeClass('is-pc');
};
// スワイプしはじめたとき
let touchStartHandler = (e) => {
if (uAParser.device.type !== 'mobile') return;
if (moving) return;
let touch = e.originalEvent.touches[0];
// 触り始めたときのタップしたx,y座標を保存
startPointX = touch.screenX;
};
// スワイプ中
let touchMoveHandler = (e) => {
if (uAParser.device.type !== 'mobile') return;
if (animating) return;
moving = true;
let touch = e.originalEvent.touches[0];
// スワイプ量を保存
endPointX = (touch.screenX - startPointX) * rate;
// 半回転以上回転させないように-180度以上、180度以下に制限
if (endPointX > 180 || endPointX < -180) {
return;
}
roll(currentRotationVal + endPointX);
};
// 指を離したとき
let touchEndHandler = () => {
if (uAParser.device.type !== 'mobile') return;
if (!moving || animating) {
return;
}
moving = false;
// タップを離したとき、プラス方向に半分よりめくれていたら最後までめくる
if(currentRotationVal + endPointX > currentRotationVal + 90) {
currentRotationVal = currentRotationVal + 180;
}
// タップを離した時、マイナス方向に半分以上めくれていたら最後までめくる
if(currentRotationVal + endPointX < currentRotationVal - 90) {
currentRotationVal = currentRotationVal - 180;
}
// transitionしつつめくる
$flip.addClass('is-animating');
$flipInner.css('transform', 'rotateY(' + currentRotationVal + 'deg)');
animating = true;
setTimeout(() => {
// cssで設定したduration分動いたら、transitionを切る
$flip.removeClass('is-animating');
animating = false;
}, DURATION);
// スワイプし始めた位置を初期化
startPointX = 0;
};
// 回転
let roll = (rotationVal) => {
$flipInner.css('transform', 'rotateY(' + (rotationVal) + 'deg)');
};
// リセット用イベント
$(window).on('resize', judgeDevice);
// イベント
$flip.on('touchstart', touchStartHandler);
$flip.on('touchmove', touchMoveHandler);
$flip.on('touchend', touchEndHandler);
$flip.on('mouseover', (e) => {
if ($(e.currentTarget).hasClass('is-pc')) {
$(e.currentTarget).addClass('is-hover');
}
});
$flip.on('mouseout', (e) => {
if ($(e.currentTarget).hasClass('is-pc')) {
$(e.currentTarget).removeClass('is-hover');
}
});
// 初期化
judgeDevice();
コードの説明
コード内に細かく機能説明は記載しましたが、最初は下記のように大枠から考えました。
あとはそれに沿って具体的な処理を書いていけばOKです。
- デバイス判定でスワイプをSPのときのみに限定する
- 触ったときのイベントの設定(touchStart)
- 触り始めたときの座標を保存
- 動かしているときのイベントの設定(touchMove)
- スワイプの量を保存
- 現在の回転量にスワイプの量を加算し、回転させる
- 離したときのイベントの設定(touchEnd)
- 離したときのスワイプ量から、めくれる方向を判定
- 判定した方向に、めくりきれる量を回転させる
- hover時のイベントをPCのときのみに限定(mouseover、mouseout)
デモ
※ スマホ、タブレットで見て開き、画像を指で横になぞってみてください。
See the Pen
coin rotation animation on the flip by k_hatsushi (@hatsushi_kazuya)
on CodePen.
まとめ
指の動きと連動させることで、よりリアルに表現できました。
細かいところですが、実際の動きに近づけるとそれだけでリッチに感じますね。
みなさんもぜひ遊んでみてくださいね!
LIGはWebサイト制作を支援しています。ご興味のある方は事業ぺージをぜひご覧ください。