From 66c4e8064b929b8cf8325fb55cba85bc01e67bf6 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sun, 8 Nov 2020 17:08:51 +0900
Subject: [PATCH] Add bounce MFM animation

---
 locales/ja-JP.yml                                   |  2 ++
 src/client/components/mfm.ts                        |  4 ++++
 src/client/components/misskey-flavored-markdown.vue |  8 ++++++++
 src/client/pages/mfm-cheat-sheet.vue                | 11 +++++++++++
 4 files changed, 25 insertions(+)

diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 75b4b803d1..6dcae50182 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -644,6 +644,8 @@ _mfm:
   tadaDescription: "ジャーン!という感じのアニメーションを与えます。"
   jump: "アニメーション(ジャンプ)"
   jumpDescription: "飛び跳ねるようなアニメーションを与えます。"
+  bounce: "アニメーション(バウンド)"
+  bounceDescription: "ぽよんぽよん弾むようなアニメーションを与えます。"
   shake: "アニメーション(ぶるぶる)"
   shakeDescription: "ぶるぶるするアニメーションを与えます。"
   twitch: "アニメーション(ブレ)"
diff --git a/src/client/components/mfm.ts b/src/client/components/mfm.ts
index 0b25f0b183..d83cd41528 100644
--- a/src/client/components/mfm.ts
+++ b/src/client/components/mfm.ts
@@ -117,6 +117,10 @@ export default defineComponent({
 							style = this.$store.state.device.animatedMfm ? 'animation: mfm-jump 0.75s linear infinite;' : '';
 							break;
 						}
+						case 'bounce': {
+							style = this.$store.state.device.animatedMfm ? 'animation: mfm-bounce 0.75s linear infinite; transform-origin: center bottom;' : '';
+							break;
+						}
 						case 'flip': {
 							const transform =
 								(token.node.props.args.h && token.node.props.args.v) ? 'scale(-1, -1)' :
diff --git a/src/client/components/misskey-flavored-markdown.vue b/src/client/components/misskey-flavored-markdown.vue
index e20c19884b..3123d7aa80 100644
--- a/src/client/components/misskey-flavored-markdown.vue
+++ b/src/client/components/misskey-flavored-markdown.vue
@@ -37,6 +37,14 @@ export default defineComponent({
 	100% { transform: translateY(0); }
 }
 
+@keyframes mfm-bounce {
+	0% { transform: translateY(0) scale(1, 1); }
+	25% { transform: translateY(-16px) scale(1, 1); }
+	50% { transform: translateY(0) scale(1, 1); }
+	75% { transform: translateY(0) scale(1.5, 0.75); }
+	100% { transform: translateY(0) scale(1, 1); }
+}
+
 // const val = () => `translate(${Math.floor(Math.random() * 20) - 10}px, ${Math.floor(Math.random() * 20) - 10}px)`;
 // let css = '';
 // for (let i = 0; i <= 100; i += 5) { css += `${i}% { transform: ${val()} }\n`; }
diff --git a/src/client/pages/mfm-cheat-sheet.vue b/src/client/pages/mfm-cheat-sheet.vue
index c3e2120278..c9346d3fc9 100644
--- a/src/client/pages/mfm-cheat-sheet.vue
+++ b/src/client/pages/mfm-cheat-sheet.vue
@@ -175,6 +175,16 @@
 			</div>
 		</div>
 	</div>
+	<div class="_section">
+		<div class="_title">{{ $t('_mfm.bounce') }}</div>
+		<div class="_content">
+			<p>{{ $t('_mfm.bounceDescription') }}</p>
+			<div class="preview _panel">
+				<Mfm :text="preview_bounce"/>
+				<MkTextarea v-model:value="preview_bounce"><span>MFM</span></MkTextarea>
+			</div>
+		</div>
+	</div>
 	<div class="_section">
 		<div class="_title">{{ $t('_mfm.spin') }}</div>
 		<div class="_content">
@@ -240,6 +250,7 @@ export default defineComponent({
 			preview_jelly: `[jelly 🍮]`,
 			preview_tada: `[tada 🍮]`,
 			preview_jump: `[jump 🍮]`,
+			preview_bounce: `[bounce 🍮]`,
 			preview_shake: `[shake 🍮]`,
 			preview_twitch: `[twitch 🍮]`,
 			preview_spin: `[spin 🍮] [spin.left 🍮] [spin.alternate 🍮]\n[spin.x 🍮] [spin.x,left 🍮] [spin.x,alternate 🍮]\n[spin.y 🍮] [spin.y,left 🍮] [spin.y,alternate 🍮]`,