CSS三角関数実践ガイド:sin(), cos(), tan()で作るアニメーション
CSS Values and Units Module Level 4で導入された三角関数は、複雑な数学計算をCSSだけで実現できる強力な機能です。この記事では、sin()、cos()、tan()、atan2()の実践的な使い方を解説します。
CSS三角関数の基本
利用可能な関数
CSS Trigonometric Functionsは以下の関数を提供します。
- sin() - サイン(正弦)
- cos() - コサイン(余弦)
- tan() - タンジェント(正接)
- asin() - アークサイン(逆正弦)
- acos() - アークコサイン(逆余弦)
- atan() - アークタンジェント(逆正接)
- atan2() - 2引数アークタンジェント
ブラウザサポート
2026年現在、主要ブラウザすべてでサポートされています。
/* モダンブラウザ全てで動作 */
.element {
transform: translateX(calc(sin(45deg) * 100px));
}
基本的な構文
/* 角度指定(deg、rad、grad、turn) */
sin(45deg) /* 度数法 */
sin(0.785rad) /* ラジアン */
sin(50grad) /* グラード */
sin(0.125turn) /* 回転(1turn = 360deg) */
/* 結果は-1から1の範囲の数値 */
cos(0deg) /* → 1 */
sin(90deg) /* → 1 */
tan(45deg) /* → 1 */
円形配置(Circular Layout)
三角関数の最も一般的な用途は、要素を円形に配置することです。
基本的な円形配置
<div class="circle-container">
<div class="item" style="--index: 0"></div>
<div class="item" style="--index: 1"></div>
<div class="item" style="--index: 2"></div>
<div class="item" style="--index: 3"></div>
<div class="item" style="--index: 4"></div>
<div class="item" style="--index: 5"></div>
</div>
.circle-container {
position: relative;
width: 400px;
height: 400px;
}
.item {
--total: 6; /* 総数 */
--radius: 150px; /* 半径 */
/* 角度を計算(360度を等分) */
--angle: calc(360deg / var(--total) * var(--index));
/* sin/cosで座標を計算 */
--x: calc(cos(var(--angle)) * var(--radius));
--y: calc(sin(var(--angle)) * var(--radius));
position: absolute;
top: 50%;
left: 50%;
/* 中心からのオフセット */
transform: translate(
calc(-50% + var(--x)),
calc(-50% + var(--y))
);
width: 50px;
height: 50px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 50%;
}
回転する円形配置
.circle-container {
position: relative;
width: 400px;
height: 400px;
animation: rotate 10s linear infinite;
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.item {
--total: 8;
--radius: 150px;
--angle: calc(360deg / var(--total) * var(--index));
--x: calc(cos(var(--angle)) * var(--radius));
--y: calc(sin(var(--angle)) * var(--radius));
position: absolute;
top: 50%;
left: 50%;
transform: translate(
calc(-50% + var(--x)),
calc(-50% + var(--y))
);
width: 60px;
height: 60px;
background: #3b82f6;
border-radius: 50%;
/* カウンターローテーション(常に正立) */
animation: counter-rotate 10s linear infinite reverse;
}
@keyframes counter-rotate {
from {
transform: translate(
calc(-50% + var(--x)),
calc(-50% + var(--y))
) rotate(0deg);
}
to {
transform: translate(
calc(-50% + var(--x)),
calc(-50% + var(--y))
) rotate(360deg);
}
}
波形アニメーション
sin()を使えば、滑らかな波形アニメーションを作成できます。
横波アニメーション
<div class="wave-container">
<div class="wave-item" style="--index: 0"></div>
<div class="wave-item" style="--index: 1"></div>
<div class="wave-item" style="--index: 2"></div>
<div class="wave-item" style="--index: 3"></div>
<div class="wave-item" style="--index: 4"></div>
<div class="wave-item" style="--index: 5"></div>
<div class="wave-item" style="--index: 6"></div>
<div class="wave-item" style="--index: 7"></div>
</div>
.wave-container {
display: flex;
gap: 10px;
padding: 50px;
}
.wave-item {
width: 30px;
height: 30px;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 50%;
/* sin関数で上下に動かす */
animation: wave 2s ease-in-out infinite;
animation-delay: calc(var(--index) * 0.1s);
}
@keyframes wave {
0%, 100% {
transform: translateY(0);
}
50% {
/* sin関数的な動き(CSSアニメーションのease-in-outがsin曲線に近い) */
transform: translateY(-40px);
}
}
sin()を直接使った波形
.wave-item {
--frequency: 2; /* 周波数 */
--amplitude: 50px; /* 振幅 */
--phase: calc(var(--index) * 45deg); /* 位相 */
width: 30px;
height: 30px;
background: #3b82f6;
border-radius: 50%;
/* sin関数で位置を計算 */
transform: translateY(
calc(sin(var(--phase)) * var(--amplitude))
);
animation: wave-phase 2s linear infinite;
}
@keyframes wave-phase {
from {
--phase: calc(var(--index) * 45deg);
}
to {
--phase: calc(var(--index) * 45deg + 360deg);
}
}
ローディングアニメーション
回転ドットローダー
<div class="loader">
<div class="dot" style="--index: 0"></div>
<div class="dot" style="--index: 1"></div>
<div class="dot" style="--index: 2"></div>
<div class="dot" style="--index: 3"></div>
<div class="dot" style="--index: 4"></div>
<div class="dot" style="--index: 5"></div>
<div class="dot" style="--index: 6"></div>
<div class="dot" style="--index: 7"></div>
</div>
.loader {
position: relative;
width: 100px;
height: 100px;
}
.dot {
--total: 8;
--radius: 40px;
--angle: calc(360deg / var(--total) * var(--index));
--x: calc(cos(var(--angle)) * var(--radius));
--y: calc(sin(var(--angle)) * var(--radius));
position: absolute;
top: 50%;
left: 50%;
transform: translate(
calc(-50% + var(--x)),
calc(-50% + var(--y))
);
width: 12px;
height: 12px;
background: #3b82f6;
border-radius: 50%;
/* フェードイン・アウト */
animation: fade 1.2s ease-in-out infinite;
animation-delay: calc(var(--index) * 0.15s);
}
@keyframes fade {
0%, 100% {
opacity: 0.2;
transform: translate(
calc(-50% + var(--x)),
calc(-50% + var(--y))
) scale(0.8);
}
50% {
opacity: 1;
transform: translate(
calc(-50% + var(--x)),
calc(-50% + var(--y))
) scale(1.2);
}
}
パルスローダー
.pulse-loader {
position: relative;
width: 80px;
height: 80px;
}
.pulse-ring {
position: absolute;
inset: 0;
border: 3px solid #3b82f6;
border-radius: 50%;
animation: pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite;
animation-delay: calc(var(--index) * 0.2s);
}
@keyframes pulse {
0% {
transform: scale(0.8);
opacity: 1;
}
100% {
transform: scale(1.5);
opacity: 0;
}
}
3D効果
ペンデュラム(振り子)
<div class="pendulum-container">
<div class="pendulum">
<div class="pendulum-bob"></div>
</div>
</div>
.pendulum-container {
height: 300px;
display: flex;
justify-content: center;
padding-top: 20px;
}
.pendulum {
width: 2px;
height: 200px;
background: #333;
transform-origin: top center;
animation: swing 2s ease-in-out infinite;
}
@keyframes swing {
0%, 100% {
/* sin関数的な動き */
transform: rotate(-30deg);
}
50% {
transform: rotate(30deg);
}
}
.pendulum-bob {
position: absolute;
bottom: -20px;
left: 50%;
transform: translateX(-50%);
width: 40px;
height: 40px;
background: radial-gradient(circle at 30% 30%, #fbbf24, #f59e0b);
border-radius: 50%;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
3D回転カード
<div class="card-3d">
<div class="card-front">Front</div>
<div class="card-back">Back</div>
</div>
.card-3d {
width: 200px;
height: 300px;
perspective: 1000px;
cursor: pointer;
}
.card-3d:hover .card-front {
transform: rotateY(180deg);
}
.card-3d:hover .card-back {
transform: rotateY(0deg);
}
.card-front,
.card-back {
position: absolute;
inset: 0;
backface-visibility: hidden;
transition: transform 0.6s;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
font-weight: bold;
color: white;
}
.card-front {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
transform: rotateY(0deg);
}
.card-back {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
transform: rotateY(-180deg);
}
atan2()を使った追従エフェクト
atan2()は2点間の角度を計算できます。
<div class="follower-container">
<div class="follower">
<div class="arrow"></div>
</div>
</div>
.follower-container {
position: relative;
height: 400px;
background: #f3f4f6;
cursor: none;
}
.follower {
--mouse-x: 50%;
--mouse-y: 50%;
--center-x: 50%;
--center-y: 50%;
position: absolute;
top: var(--center-y);
left: var(--center-x);
width: 60px;
height: 60px;
background: #3b82f6;
border-radius: 50%;
transform: translate(-50%, -50%);
transition: all 0.3s ease;
}
.arrow {
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 20px solid white;
/* atan2()でマウス方向を向く */
transform: translate(-50%, -50%)
rotate(calc(atan2(
var(--mouse-y) - var(--center-y),
var(--mouse-x) - var(--center-x)
) + 90deg));
}
// JavaScriptでマウス座標を更新
const container = document.querySelector('.follower-container');
const follower = document.querySelector('.follower');
container.addEventListener('mousemove', (e) => {
const rect = container.getBoundingClientRect();
const x = ((e.clientX - rect.left) / rect.width) * 100;
const y = ((e.clientY - rect.top) / rect.height) * 100;
follower.style.setProperty('--mouse-x', `${x}%`);
follower.style.setProperty('--mouse-y', `${y}%`);
});
パフォーマンスの最適化
CSS変数との組み合わせ
:root {
--pi: 3.14159265359;
}
.element {
/* 定数を使って可読性向上 */
--angle-rad: calc(var(--angle-deg) * var(--pi) / 180);
transform: translateX(calc(cos(var(--angle-rad) * 1rad) * 100px));
}
will-changeの活用
.animated-element {
will-change: transform;
/* GPUアクセラレーションを有効化 */
transform: translateZ(0);
}
まとめ
CSS三角関数の主な用途をまとめます。
- 円形配置 - ナビゲーション、アバター配置
- 波形アニメーション - ローディング、装飾
- 3D効果 - カード、ギャラリー
- 追従エフェクト - インタラクティブUI
- 物理シミュレーション - 振り子、回転
CSS三角関数を使えば、従来JavaScriptで実装していた複雑なアニメーションを、CSSだけで実現できます。パフォーマンスも向上し、コードもシンプルになります。
モダンなWebデザインを作るなら、CSS三角関数は必須のテクニックです。