というわけでサンプル
See the Pen css / js / svg animation sample by juner clarinet (@juner) on CodePen.
で具体的な違いについて の概要
css | js | svg | |
---|---|---|---|
表示時 | css | js | svg |
hover時 | css | js | svg |
click時 | css + js | js | svg |
どれも css で装飾していますが、ここで言っている違いで指しているのはアニメーションに関してだけです。 js でトリガーが必要なら js って書いてます。
各種ソースと解説
css
css は簡単で keyframe 作って 表示時 / hover時に於いててはそれを特定タイミングで適用するだけです。
ただ、 click 時については css だけでは無理です。
js 側は イベントベースとなる為、 Promise でシーケンシャルに実行するようにして どうにかしています。
<div class="css">
<h1>css</h1>
<h2>表示時</h2>
<div class="target case-1"></div>
<h2>hover時</h2>
<div class="target case-2"></div>
<h2>click時</h2>
<div class="target case-3"></div>
</div>
:root {
.css {
--animate-color: red;
.target {
height: 100px;
width: 100px;
display:inline-block;
background-color: green;
}
/* css 常時 */
.case-1 {
background: var(--animate-color);
animation: linear 1000ms infinite normal rotate;
}
/* css hover時 */
.case-2:hover {
background: var(--animate-color);
animation: linear 1000ms infinite normal rotate;
}
.case-3.animate {
background: var(--animate-color);
animation: linear 1000ms 1 normal rotate;
}
}
}
@keyframes rotate {
from {
transform: rotate(0);
}
to {
transform: rotate(1turn);
}
}
{
// css click時
let controller = new AbortController();
const target = document.querySelector(".css .target.case-3");
let promise = Promise.resolve();
let i = 0;
target.addEventListener("click", (e) => {
const target = e.currentTarget;
// 前をキャンセル
controller.abort();
const c = new AbortController();
controller = c;
// シーケンシャルに実行しないと タイミングに問題が出る
promise = promise.then(async () => {
target.classList.add("animate");
const signal = c.signal;
// キャンセル時は class を外すだけ
signal.addEventListener("abort", () => {
target.classList.remove("animate");
});
const {resolve, promise} = Promise.withResolvers();
// animationend / animationcancel どちらかが発生するまで待機
target.addEventListener("animationend", resolve);
target.addEventListener("animationcancel", resolve);
await promise;
// キャンセルしていなければ キャンセル
if (!signal.aborted) c.abort();
// イベントの解放
target.removeEventListener("animationend", resolve);
target.removeEventListener("animationcancel", resolve);
});
});
}
js
js では Web Animation API を利用して 比較的簡単に実行/キャンセルができ、待つのも Promise で簡単です。
<div class="js">
<h1>js</h1>
<h2>表示時</h2>
<div class="target case-1"></div>
<h2>hover時</h2>
<div class="target case-2"></div>
<h2>click時</h2>
<div class="target case-3"></div>
</div>
:root {
.js {
--animate-color: red;
.target {
height: 100px;
width: 100px;
display:inline-block;
background-color: green;
}
.animate {
background: var(--animate-color);
}
}
}
const keyframes = [
{ transform: 'rotate(0)'},
{ transform: 'rotate(1turn)'},
];
const options = {
duration: 1000,
iterations: Infinity,
};
const onetime = {
duration: 1000,
iterations: 1,
};
{
// js 表示時
const target = document.querySelector(".js .target.case-1");
target.classList.add('animate');
target.animate(keyframes, options);
}
{
// js hover時
let controller = new AbortController();
const target = document.querySelector(".js .target.case-2");
target.addEventListener('pointerover', (e) => {
const target = e.currentTarget;
target.setPointerCapture(e.pointerId);
target.classList.add('animate');
const animate = target.animate(keyframes, options);
const signal = controller.signal;
signal.addEventListener('abort', () => {
target.classList.remove('animate');
animate.cancel();
})
});
target.addEventListener('pointerout', (e) => {
controller.abort();
controller = new AbortController();
});
}
{
// js click時
let controller = new AbortController();
const target = document.querySelector(".js .target.case-3");
target.addEventListener('pointerup', async (e) => {
const target = e.currentTarget;
// 前をキャンセル
controller.abort();
const c = new AbortController();
controller = c;
const animate = target.animate(keyframes, onetime);
const signal = c.signal;
// キャンセルすると状態をクリアする
signal.addEventListener('abort', () => {
target.classList.remove('animate');
if (animate.playState === "finished") return;
animate.cancel();
});
target.classList.add('animate');
try {
await animate.finished;
}catch(e){ }
// 完了したので状態をクリアする
c.abort();
});
}
svg
svg はなんとcssとは違いクリックしたときという表現がある為、全部svgでできてしまいます。
ただ、 animate要素等で css custom property の指定が無理なのが難点……。
html
<div class="svg">
<h1>svg</h1>
<h2>表示時</h2>
<svg title="表示時">
<rect x="0" y="0" height="100" width="100" class="target case-1" id="svg-target-case-1" >
<animateTransform xlink:href="#svg-target-case-1"
attributeName="transform"
type="rotate"
from="0 50 50"
to="360 50 50"
dur="1s"
repeatCount="indefinite"
/>
<animate xlink:href="#svg-target-case-1"
attributeName="fill"
values="red"
dur="1s"
repeatCount="indefinite" />
</rect>
</svg>
<h2>hover時</h2>
<svg title="hover時">
<rect x="0" y="0" height="100" width="100" class="target case-2" id="svg-target-case-2" fill="green">
<animateTransform xlink:href="#svg-target-case-2"
attributeName="transform"
type="rotate"
from="0 50 50"
to="360 50 50"
dur="1s"
repeatCount="indefinite"
begin="mouseenter"
end="mouseout" />
<animate xlink:href="#svg-target-case-2"
attributeName="fill"
values="red"
dur="1s"
repeatCount="indefinite"
begin="mouseenter"
end="mouseout" />
</rect>
</svg>
<h2>click時</h2>
<svg title="hover時">
<rect x="0" y="0" height="100" width="100" class="target case-3" id="svg-target-case-3" fill="green">
<animateTransform xlink:href="#svg-target-case-3"
attributeName="transform"
type="rotate"
from="0 50 50"
to="360 50 50"
dur="1s"
repeatCount="1"
begin="click" />
<animate xlink:href="#svg-target-case-3"
attributeName="fill"
values="red"
dur="1s"
repeatCount="1"
begin="click" />
</rect>
</svg>
</div>
css
:root {
.svg {
--animate-color: red;
.target {
height: 100px;
width: 100px;
display:inline-block;
background-color: green;
}
svg {
width: 100px;
height: 100px;
overflow: visible;
}
}
}
以上。