デモ
デモを読み込めませんでした。
仕様
- 用途
- アイコン
- 動作
- DOM
- 種類
- レシピ
- 使うもの
- MorphSVGPlugin
- 動作確認
- three 0.186.0・gsap 3.15.0
- 公開日
しくみと調整
再生の三角を、縦の割れ目で左右 2 枚の四角形に分けて描きます(右の 1 枚は、先端で 2 点を重ねた四角形です)。押すと、2 枚がそれぞれ一時停止の棒へ形を変え、三角が縦に割れて棒が起き上がるように見えます。もう一度押すと、同じ動きを逆にたどって三角に戻ります。
変形は MorphSVGPlugin で、0.45 秒の power3.inOut です。4 つの形の点の順を、左上 → 右上 → 右下 → 左下にそろえ、shapeIndex を 0 にして、点をそのまま対応させます。自動の対応に任せると、点の組み合わせがずれて、形がねじれながら変わることがあります。
角は、塗りと同じ色の線(幅 2・角は丸)を重ねて丸めます。線は形の外側へ 1 ずつ太るので、形はそのぶん小さく描いておきます。
切り替えは、ボタンの click ではなく、audio の play と pause のイベントで行います。最後まで再生して止まったときも、アイコンの形がそろいます。ボタンの名前も「再生」と「一時停止」で書き換えます。
このデモでは、再生中にジャケットの後ろへ光をにじませ、イコライザーの棒を揺らしています。時間と ease、三角の割れ目の位置、棒の幅と間隔で印象が変わります。音楽やポッドキャスト、動画のプレーヤー、スライドショーの自動再生の切り替えに向いています。
コード
<div class="player">
<!-- 音源はサイトのものに差し替える -->
<audio class="player__audio" src="/audio/sample.mp3" preload="metadata"></audio>
<!-- 名前(aria-label)は、押したら何が起きるかを書く。script.js が「再生」と「一時停止」を入れ替える -->
<button class="player__toggle" type="button" aria-label="再生">
<svg class="player__icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
<!-- 三角を左右 2 枚の四角形に割る(右は先端で 2 点を重ねる)。点の順は、左上 → 右上 → 右下 → 左下 -->
<path class="player__left" d="M8 6.5 L13 9.25 L13 14.75 L8 17.5 Z" />
<path class="player__right" d="M13 9.25 L18 12 L18 12 L13 14.75 Z" />
</svg>
</button>
</div>
.player__toggle {
--size: 48px; /* ボタンの直径 */
--icon: 24px; /* アイコンの大きさ */
--ground: #eeece6; /* 丸の色(暗い地に置く明るい主ボタン) */
--ink: #111111; /* アイコンの色 */
display: inline-grid;
place-items: center;
width: var(--size);
height: var(--size);
padding: 0;
border: 0;
border-radius: 50%;
background-color: var(--ground);
color: var(--ink);
cursor: pointer;
-webkit-appearance: none;
appearance: none;
}
/* 塗りと同じ色の線(幅 2・角は丸)を重ねて、角を丸める。見た目は四方に 1 ずつ太る */
.player__icon {
width: var(--icon);
height: var(--icon);
overflow: visible;
fill: currentColor;
stroke: currentColor;
stroke-width: 2;
stroke-linejoin: round;
}
.player__toggle:focus-visible {
outline: 2px solid var(--ground);
outline-offset: 4px;
}
import { gsap } from "gsap";
import { MorphSVGPlugin } from "gsap/MorphSVGPlugin";
gsap.registerPlugin(MorphSVGPlugin);
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
// 一時停止の 2 本の棒。点の順を三角の 2 枚とそろえてあるので、shapeIndex: 0 でそのまま対応させる
const PAUSE_LEFT = "M7 6 L9.5 6 L9.5 18 L7 18 Z";
const PAUSE_RIGHT = "M14.5 6 L17 6 L17 18 L14.5 18 Z";
document.querySelectorAll(".player").forEach((player) => {
const audio = player.querySelector(".player__audio");
const button = player.querySelector(".player__toggle");
const left = button.querySelector(".player__left");
const right = button.querySelector(".player__right");
// 三角 → 2 本の棒。止めるときは同じタイムラインを逆再生する
const toggle = gsap
.timeline({ paused: true })
.to(left, { morphSVG: { shape: PAUSE_LEFT, shapeIndex: 0 }, duration: 0.45, ease: "power3.inOut" }, 0)
.to(right, { morphSVG: { shape: PAUSE_RIGHT, shapeIndex: 0 }, duration: 0.45, ease: "power3.inOut" }, 0);
// 動きを減らす設定では、ほぼ一瞬で切り替える
if (reduceMotion) toggle.timeScale(20);
// 読み込めずに再生が始まらなかったときは、play が起きないので形も変わらない
button.addEventListener("click", () => (audio.paused ? audio.play().catch(() => {}) : audio.pause()));
// 最後まで再生して止まったときも形がそろうよう、ボタンではなく audio のイベントで切り替える
audio.addEventListener("play", () => {
toggle.play();
button.setAttribute("aria-label", "一時停止");
});
audio.addEventListener("pause", () => {
toggle.reverse();
button.setAttribute("aria-label", "再生");
});
});