CSSだけで作る、TikTok100フォロワー記念の数字スライドアニメーション

アニメーション

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は、100を別要素に分け、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--skumonosu-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前提なので長文には向かない(文言差し替え注意)
  • レイアウト固定用のゴースト文字は内容変更時に更新が必要