どんなものが出来るのか
このgifを参考にしてください。 これは一番単純な動きです。
#作り方
仕組みは単純で、border-widthの値を変更することで実装します。
環境
CSSリセットとしてress、アニメーションはanime.jsで記述していきます。
お使いの環境に合わせて適宜変更していってください。
コーディング
HTML
<div class="wrap">
<div class="border"></div>
<img src="icon.png">
</div>
CSS
.wrap {
position: relative;
width: 128px;
height: 128px;
}
.wrap > .border {
position: absolute;
left: 0;
top: 0;
z-index: 1;
width: 128px;
height: 128px;
border-width: 64px;
border-color: #03a9f4;
border-style: solid;
border-radius: 50%;
box-sizing: border-box;
}
.wrap > img {
position: absolute;
left: 0;
top: 0;
z-index: 0;
width: 128px;
height: 128px;
border-radius: 50%;
}
今回は大きさを128pxで設定しています。scss、stylusなどの場合は変数化しておくとラクだと思います。
重要なのはborder-weight
を大きさの半分に設定することです。
border: 64px solid #03a9f4
と書いても問題ありません。
JS
var wrap = basic.getElementsByClassName("wrap")[0];
var border = wrap.getElementsByClassName("border")[0];
var img = wrap.getElementsByTagName("img")[0];
var timeline = anime.timeline();
timeline
.add({
targets: border,
duration: 500,
easing: "easeInExpo",
scale: [0, 1],
begin: () => (img.style.visibility = "hidden")
})
.add({
targets: border,
duration: 500,
easing: "easeOutExpo",
borderWidth: 0,
begin: () => (img.style.visibility = "visible")
});
何をやっているか
アニメーション開始時点 (1回目のbegin関数)
img
要素のvisibility
をhidden
にして非表示に設定する。
ボーダー要素を拡大する (scaleX: [0, 1])
scale
を0から1へアニメーションする。
ボーダーの拡大が完了 (2回目のbegin関数)
img
要素のvisibility
をvisible
にして非表示を解除する。
ボーダーを0pxに縮小する (borderWidth: 0)
タイトル通りです。
基本の動きは以上です。
コツ
前回の記事でも触れましたが、拡大するときにはイージングをeaseIn*
系、縮小する際にはeaseOut*
を設定すると見栄えが良くなると思います。
また、easeOutExpo
のような急なイージングの際には縮小の際にボーダーが1px近くの小さな値で留まりやすいです。(下の画像のように)
ボーダー要素のopacity
もアニメーションさせておくと違和感が残らなくて良いかもしれません。
または、ボーダー要素のscale
を1.05
のように少しだけ大きくするのも一つの手でしょう。
応用
その1
HTMLは一緒です。
CSS
.wrap {
border-radius: 50%;
overflow: hidden;
}
コツで書いたようにボーダー要素のscale
を変更する手段を取った場合、この2行を追加してください。
JS
コードはほぼ一緒なので、timelineの部分だけ抜き出します。
timeline
.add({
targets: border,
duration: 500,
easing: "easeInExpo",
scale: [0, 1],
begin: () => (img.style.visibility = "hidden")
})
.add({
targets: border,
duration: 500,
easing: "easeOutExpo",
borderWidth: 0,
scale: 1.05,
begin: () => {
img.style.visibility = "visible";
anime({
targets: img,
duration: 1000,
easing: "easeOutElastic",
scale: [0, 1],
rotateZ: [`${0.25}turn`, 0]
});
}
});
画像側のアニメーションを2回目のbegin関数に追加しました。
turn
というのは見慣れない単位かもしれませんが、1turn
は360deg
です。別に90deg
でも構いません。
また、ボーダー要素のscale
を1.05
になるように設定しています。
その2
前回の応用1でやったことの応用で、ボーダー要素を2個にします。
先程のコードを改造します。
HTML
<div class="wrap">
<div class="border"></div>
<div class="border"></div>
<img src="icon.png">
</div>
CSS
.wrap > .border:nth-type-of(1) {
background-color: #03a9f4;
z-index: 2;
}
.wrap > .border:nth-type-of(2) {
background-color: #0277bd;
z-index: 1;
}
JS
var wrap = adv_1.getElementsByClassName("wrap")[0];
var border1 = wrap.getElementsByClassName("border")[0];
var border2 = wrap.getElementsByClassName("border")[1];
var img = wrap.getElementsByTagName("img")[0];
var timeline = anime.timeline();
timeline
.add({
targets: [border1, border2],
duration: (target, index) => 500 - index * 100,
easing: "easeInExpo",
scale: [0, 1],
begin: () => (img.style.visibility = "hidden")
})
.add({
targets: [border1, border2],
duration: 500,
delay: (target, index) => index * 100,
easing: "easeOutExpo",
borderWidth: 0,
scale: 1.05,
begin: () => {
img.style.visibility = "visible";
anime({
targets: img,
duration: 1000,
easing: "easeOutElastic",
scale: [0, 1],
rotateZ: [`${0.125}turn`, 0]
});
}
});
その3
その2を更に応用して、パーティクルを出します。
HTML
<div class="wrap">
<div class="border"></div>
<div class="border"></div>
<div class="particle"></div>
<div class="particle"></div>
<div class="particle"></div>
<div class="particle"></div>
<div class="particle"></div>
<div class="particle"></div>
<div class="particle"></div>
<div class="particle"></div>
<div class="particle"></div>
<div class="particle"></div>
<img src="icon.png">
</div>
pugなどが使える環境ではループを使って書いたほうがよっぽど衛生的だと思います。
CSS
.wrap > .particle {
position: absolute;
top: 0;
left: 0;
z-index: 0;
width: 128px;
height: 128px;
opacity: 0;
border-radius: 50%;
background: #03a9f4;
}
コードには含めていませんが、.wrap
のoverflow: hidden
を削除してください。
そしてopacity: 0
を忘れないでください。書き忘れるとこうなります。
JS
var particles = wrap.getElementsByClassName("particle");
var timeline = anime.timeline();
timeline
.add({
targets: [border1, border2],
duration: (target, index) => 500 - index * 100,
easing: "easeInExpo",
scale: [0, 1],
begin: () => (img.style.visibility = "hidden")
})
.add({
targets: [border1, border2],
duration: 500,
delay: (target, index) => index * 100,
easing: "easeOutExpo",
borderWidth: 0,
begin: () => {
img.style.visibility = "visible";
anime({
targets: img,
duration: 1000,
easing: "easeOutElastic",
rotateZ: [`${0.125}turn`, 0]
});
anime({
targets: particles,
duration: 350,
delay: (target, index) => index * 10,
easing: "easeOutQuad",
translateX: () => `${(Math.random() - 0.5) * 200}%`,
translateY: () => `${(Math.random() - 0.5) * 200}%`,
scale: [() => Math.min(Math.random() * 2, 1), 0],
opacity: 1
});
}
});
パーティクルのアニメーションを追加しました。
あまりtranslateX
やtranslateY
の値を大きくすると良くないかもしれません。
このサンプルでは-100%
から100%
の範囲で動きます。
また、今回はボーダーの縮小にscale: 1.05
を使うとハミ出るので削除します。
最後
間違ってたらコメント欄でご指摘ください。