about summary refs log tree commit diff
path: root/src/lib/custom-animations/LikeIcon.tsx
diff options
context:
space:
mode:
authorHailey <me@haileyok.com>2024-09-02 01:37:24 -0700
committerGitHub <noreply@github.com>2024-09-02 01:37:24 -0700
commit1225e8448524633466379d5ac00a78b53e1a9a51 (patch)
tree6215245576fed72912eeee9a51bcce01baf725ba /src/lib/custom-animations/LikeIcon.tsx
parenteb868a042ad4f767b8e1b90d33bf1171b66a5238 (diff)
downloadvoidsky-1225e8448524633466379d5ac00a78b53e1a9a51.tar.zst
Improve animations for like button (#5074)
Diffstat (limited to 'src/lib/custom-animations/LikeIcon.tsx')
-rw-r--r--src/lib/custom-animations/LikeIcon.tsx139
1 files changed, 139 insertions, 0 deletions
diff --git a/src/lib/custom-animations/LikeIcon.tsx b/src/lib/custom-animations/LikeIcon.tsx
new file mode 100644
index 000000000..06d5c2850
--- /dev/null
+++ b/src/lib/custom-animations/LikeIcon.tsx
@@ -0,0 +1,139 @@
+import React from 'react'
+import {View} from 'react-native'
+import Animated, {
+  Keyframe,
+  LayoutAnimationConfig,
+  useReducedMotion,
+} from 'react-native-reanimated'
+
+import {s} from 'lib/styles'
+import {useTheme} from '#/alf'
+import {
+  Heart2_Filled_Stroke2_Corner0_Rounded as HeartIconFilled,
+  Heart2_Stroke2_Corner0_Rounded as HeartIconOutline,
+} from '#/components/icons/Heart2'
+
+const keyframe = new Keyframe({
+  0: {
+    transform: [{scale: 1}],
+  },
+  10: {
+    transform: [{scale: 0.7}],
+  },
+  40: {
+    transform: [{scale: 1.2}],
+  },
+  100: {
+    transform: [{scale: 1}],
+  },
+})
+
+const circle1Keyframe = new Keyframe({
+  0: {
+    opacity: 0,
+    transform: [{scale: 0}],
+  },
+  10: {
+    opacity: 0.4,
+  },
+  40: {
+    transform: [{scale: 1.5}],
+  },
+  95: {
+    opacity: 0.4,
+  },
+  100: {
+    opacity: 0,
+    transform: [{scale: 1.5}],
+  },
+})
+
+const circle2Keyframe = new Keyframe({
+  0: {
+    opacity: 0,
+    transform: [{scale: 0}],
+  },
+  10: {
+    opacity: 1,
+  },
+  40: {
+    transform: [{scale: 0}],
+  },
+  95: {
+    opacity: 1,
+  },
+  100: {
+    opacity: 0,
+    transform: [{scale: 1.5}],
+  },
+})
+
+export function AnimatedLikeIcon({
+  isLiked,
+  big,
+}: {
+  isLiked: boolean
+  big?: boolean
+}) {
+  const t = useTheme()
+  const size = big ? 22 : 18
+  const shouldAnimate = !useReducedMotion()
+
+  return (
+    <View>
+      <LayoutAnimationConfig skipEntering>
+        {isLiked ? (
+          <Animated.View
+            entering={shouldAnimate ? keyframe.duration(300) : undefined}>
+            <HeartIconFilled style={s.likeColor} width={size} />
+          </Animated.View>
+        ) : (
+          <HeartIconOutline
+            style={[{color: t.palette.contrast_500}, {pointerEvents: 'none'}]}
+            width={size}
+          />
+        )}
+        {isLiked ? (
+          <>
+            <Animated.View
+              entering={
+                shouldAnimate ? circle1Keyframe.duration(300) : undefined
+              }
+              style={[
+                {
+                  position: 'absolute',
+                  backgroundColor: s.likeColor.color,
+                  top: 0,
+                  left: 0,
+                  width: size,
+                  height: size,
+                  zIndex: -1,
+                  pointerEvents: 'none',
+                  borderRadius: size / 2,
+                },
+              ]}
+            />
+            <Animated.View
+              entering={
+                shouldAnimate ? circle2Keyframe.duration(300) : undefined
+              }
+              style={[
+                {
+                  position: 'absolute',
+                  backgroundColor: t.atoms.bg.backgroundColor,
+                  top: 0,
+                  left: 0,
+                  width: size,
+                  height: size,
+                  zIndex: -1,
+                  pointerEvents: 'none',
+                  borderRadius: size / 2,
+                },
+              ]}
+            />
+          </>
+        ) : null}
+      </LayoutAnimationConfig>
+    </View>
+  )
+}