アニメーション
CSSだけで作る、TikTok100フォロワー記念の数字スライドアニメーション
2026/02/23
2026/2/16
節目の表示を、ただのテキストではなく演出として見せたい。
このサンプルは、数字が横方向に滑り込む動きとマスクによる見せ方で「100 FOLLOWERS」を演出し、その後に「thank you」へ切り替える2シーン構成のアニメーションです。
HTMLは最小限で、見せ方はほぼCSSで制御します。
Preview プレビュー
Code コード
<div class="kumonosu-main-wrapper">
<!-- 100 Followers Scene -->
<div class="kumonosu-scene">
<div class="kumonosu-number-wrapper">
<div class="kumonosu-ghost-number">100</div>
<div class="kumonosu-move-number">
<span class="kumonosu-last-item">0</span>
</div>
<div class="kumonosu-number-container">
<span class="kumonosu-mask-number">10</span>
</div>
</div>
<div class="kumonosu-followers-text">FOLLOWERS</div>
</div>
<!-- Thank you Scene -->
<div class="kumonosu-scene2">
<div class="kumonosu-move-letter">
<span class="kumonosu-last-item">u</span>
</div>
<div class="kumonosu-msn-container">
<span class="kumonosu-mask-number">thank yo <span>u</span></span>
</div>
</div>
</div>
@import url('https://fonts.googleapis.com/css2?family=Fjalla+One&display=swap');
/* CSS Variables / Properties */
@property --s {
syntax: '<percentage>';
inherits: false;
initial-value: 0%;
}
@property --sm {
syntax: '<percentage>';
inherits: false;
initial-value: 0%;
}
@property --x {
syntax: '<percentage>';
inherits: false;
initial-value: -180%;
}
@property --x2 {
syntax: '<percentage>';
inherits: false;
initial-value: 0%;
}
@property --sc {
syntax: '<number>';
inherits: false;
initial-value: 1;
}
@property --ry {
syntax: '<angle>';
inherits: false;
initial-value: -33deg;
}
@property --ry2 {
syntax: '<angle>';
inherits: false;
initial-value: 0deg;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Fjalla One', sans-serif;
}
body {
background: #000;
overflow: hidden;
width: 100%;
height: 100vh;
}
.kumonosu-main-wrapper {
width: 100%;
height: 100vh;
display: grid;
place-items: center;
position: relative;
overflow: hidden;
/* はみ出し防止 */
}
/* シーン共通設定 */
.kumonosu-scene,
.kumonosu-scene2 {
position: relative;
color: #fff;
transform-style: preserve-3d;
perspective: 1000px;
text-align: center;
white-space: nowrap;
/* 改行を禁止 */
}
/* --- Scene 1: 100 Followers --- */
.kumonosu-scene {
/* 画面幅に合わせてサイズを可変にする (15vw = 画面幅の15%) */
font-size: 15vw;
display: flex;
flex-direction: column;
align-items: center;
}
/* PCなど大きな画面で大きくなりすぎないよう制限 */
@media (min-width: 1024px) {
.kumonosu-scene {
font-size: 14rem;
}
.kumonosu-number-wrapper {
height: 14rem !important;
}
}
.kumonosu-number-wrapper {
position: relative;
height: 15vw;
/* font-sizeと合わせる */
display: flex;
align-items: center;
justify-content: center;
}
.kumonosu-ghost-number {
opacity: 0;
pointer-events: none;
user-select: none;
}
.kumonosu-number-container {
position: absolute;
left: 0;
mask: linear-gradient(to right, transparent 0%, black 0%, black var(--s), transparent var(--s));
-webkit-mask: linear-gradient(to right, transparent 0%, black 0%, black var(--s), transparent var(--s));
transform: translateZ(2px);
animation: kumonosu-move2 9s ease-in-out forwards;
}
.kumonosu-move-number {
position: absolute;
right: 0;
transform-style: preserve-3d;
perspective: 1000px;
transform: translateX(var(--x));
animation: kumonosu-move 9s linear forwards;
will-change: transform;
}
.kumonosu-followers-text {
font-size: 3vw;
/* 文字サイズもレスポンシブに */
letter-spacing: 0.5vw;
color: #fff;
text-transform: uppercase;
opacity: 0;
transform: translateY(-10px);
animation: kumonosu-showFollowers 9s ease-in-out forwards;
margin-top: 0.5rem;
width: 100%;
text-align: center;
}
/* 大きな画面でのテキストサイズ調整 */
@media (min-width: 1024px) {
.kumonosu-followers-text {
font-size: 2.5rem;
letter-spacing: 0.8rem;
}
}
.kumonosu-scene span {
display: block;
}
.kumonosu-last-item {
color: red;
transition: transform 0.3s linear;
}
/* --- Scene 2: Thank you --- */
.kumonosu-scene2 {
position: absolute;
font-size: 12vw;
/* レスポンシブサイズ */
}
@media (min-width: 1024px) {
.kumonosu-scene2 {
font-size: 10rem;
}
}
.kumonosu-move-letter {
position: absolute;
left: var(--x2);
right: 0;
opacity: 0;
transform: rotate(var(--ry2));
animation: kumonosu-moveletter .9s 4s ease-in-out forwards;
display: inline-block;
width: fit-content;
}
.kumonosu-msn-container {
mask: linear-gradient(to right, transparent 0%, black 0%, black var(--sm), transparent var(--sm));
-webkit-mask: linear-gradient(to right, transparent 0%, black 0%, black var(--sm), transparent var(--sm));
animation: kumonosu-msn-container 9s 4.07s ease-in-out forwards;
display: flex;
justify-content: center;
}
.kumonosu-mask-number {
display: flex;
white-space: nowrap;
}
.kumonosu-mask-number span {
color: red;
}
/* Animations */
@keyframes kumonosu-move {
0% {
--x: -180%;
--sc: 1;
--ry: -33deg;
}
1% {
--sc: 2;
}
10%, 30.2% {
--sc: 2;
--x: 0%;
}
22%, 30.2% {
--sc: 1;
--ry: 0deg;
}
30.2%, 40% {
--x: 0%;
--ry: -33deg;
--sc: 2;
}
40%, 100% {
--x: -180%;
--sc: 1;
}
40% {
opacity: 1;
}
42% {
opacity: 0;
}
100% {
--x: -187%;
--sc: 1;
--ry: -33deg;
opacity: 0;
}
}
@keyframes kumonosu-move2 {
0% {
--s: 0%;
}
11%, 30% {
--s: 100%;
}
39%, 100% {
--s: 0%;
}
}
@keyframes kumonosu-showFollowers {
0%, 10% {
opacity: 0;
transform: translateY(-10px);
}
15% {
opacity: 1;
transform: translateY(0);
}
35% {
opacity: 1;
transform: translateY(0);
}
40%, 100% {
opacity: 0;
transform: translateY(10px);
}
}
@keyframes kumonosu-msn-container {
0% {
filter: blur(30px) contrast(10);
--sm: 0%;
}
11%, 100% {
filter: blur(0px) contrast(10);
--sm: 82%;
}
}
@keyframes kumonosu-moveletter {
0% {
opacity: 0;
--x2: 0%;
--ry2: 0deg;
}
2%, 100% {
opacity: 1;
}
100% {
--x2: 82%;
--ry2: 720deg;
}
}
Explanation 詳しい説明
仕様
構成は「Scene 1(100 FOLLOWERS)」と「Scene 2(thank you)」の2つです。
どちらも文字をそのまま動かすのではなく、マスク(mask / -webkit-mask)で見える範囲を操作しながら、要素自体の移動や回転を組み合わせています。
Scene 1は、10と0を別要素に分け、0だけを横方向にスライドさせて数字の切り替え感を作っています。
--sをアニメーションさせてマスクの表示幅を伸縮し、「現れる→見せる→消える」を1つの流れにしています。同時にFOLLOWERSのテキストもフェードイン/アウトで同期します。
Scene 2は、thank yoと最後のuを分離し、最後の文字だけを回転しながら移動させて“締め”の動きを作っています。こちらも--smでマスク幅を制御し、ブラーからくっきりに変化させて登場感を出しています。
補助として、レイアウト崩れを防ぐためのゴースト文字(透明の100)を置き、数字エリアの幅・高さを安定させています。
- 2シーン構成(「100 FOLLOWERS」→「thank you」)
- マスク幅(
--s/--sm)で表示領域を制御 - 数字/文字を分割して“最後だけ動かす”演出
- ゴースト文字でレイアウト固定&ズレ防止
@propertyでCSS変数をアニメ可能にしている
カスタム
カスタムは「速度」「タイミング」「サイズ」「色」で調整すると狙った雰囲気に寄せやすいです。
- 全体の尺:
animation: ... 9s ...の9s - 数字の移動量:
@keyframes kumonosu-moveの--x(-180%など) - マスクの見せ幅:
kumonosu-move2の--s、kumonosu-msn-containerの--sm - 回転の派手さ:
kumonosu-moveletterの--ry2: 720deg - 強調色:
.kumonosu-last-item { color: red; }
レスポンシブはvwベースで組まれているので、PCで大きくなりすぎる場合は、既に入っている@media (min-width: 1024px)の上限値を調整するのが早いです。
注意点
この実装は@propertyで宣言したカスタムプロパティをアニメーションさせています。未対応環境では変数アニメが効かず、マスクの動きが期待通りにならない場合があります。
必要なら、対応ブラウザを前提にするか、フォールバック(マスクなしのフェードなど)を用意すると安全です。
また、maskはブラウザ差が出やすいので、-webkit-maskを併記している点は重要です。
運用時に文字列を差し替える場合は、ゴースト文字やwhite-space: nowrapの影響で改行されない前提になっているため、文言の長さに注意してください。
@property未対応環境ではアニメが崩れる可能性maskは互換性のため-webkit-mask併記が前提nowrap前提なので長文には向かない(文言差し替え注意)- レイアウト固定用のゴースト文字は内容変更時に更新が必要