this string has no description
heart-anim.tsx
1
2File filter
3import React, { useRef, useState } from 'react'
4import { StyleSheet, Animated, Dimensions } from 'react-native'
5import { Ionicons } from '@expo/vector-icons'
6
7export interface HeartAnimationProps {
8 onComplete?: () => void
9}
10
11export interface HeartAnimationRef {
12 trigger: (x: number, y: number) => void
13}
14
15const getSeed = () => Math.floor(Math.random())
16
17export const HeartAnimation = React.forwardRef<HeartAnimationRef, HeartAnimationProps>(
18 ({ onComplete }, ref) => {
19 const [showHeart, setShowHeart] = useState(false)
20 const [heartPosition, setHeartPosition] = useState({ x: 0, y: 0 })
21 const heartScale = useRef(new Animated.Value(0)).current
22 const heartOpacity = useRef(new Animated.Value(0)).current
23 const heartRotation = useRef(new Animated.Value(getSeed())).current
24
25 const triggerHeartAnimation = (x: number, y: number) => {
26 const { width, height } = Dimensions.get('window')
27
28 // Constrain heart position to screen bounds (heart is 80px, so 40px radius)
29 const constrainedX = Math.max(40, Math.min(width - 40, x))
30 const constrainedY = Math.max(40, Math.min(height - 40, y))
31
32 setHeartPosition({ x: constrainedX, y: constrainedY })
33 setShowHeart(true)
34
35 // Generate random rotation between -15 and 15 degrees
36 const randomRotation = (Math.random() - 0.5) * 30
37 heartRotation.setValue(randomRotation)
38
39 Animated.parallel([
40 Animated.sequence([
41 Animated.timing(heartScale, {
42 toValue: 1.2,
43 duration: 150,
44 useNativeDriver: true,
45 }),
46 Animated.timing(heartScale, {
47 toValue: 1,
48 duration: 100,
49 useNativeDriver: true,
50 }),
51 ]),
52 Animated.sequence([
53 Animated.timing(heartOpacity, {
54 toValue: 1,
55 duration: 150,
56 useNativeDriver: true,
57 }),
58 Animated.delay(500),
59 Animated.timing(heartOpacity, {
60 toValue: 0,
61 duration: 300,
62 useNativeDriver: true,
63 }),
64 ]),
65 ]).start(() => {
66 setShowHeart(false)
67 heartScale.setValue(0)
68 heartOpacity.setValue(0)
69 heartRotation.setValue(0)
70 onComplete?.()
71 })
72 }
73
74 React.useImperativeHandle(ref, () => ({
75 trigger: triggerHeartAnimation,
76 }))
77
78 if (!showHeart) {
79 return null
80 }
81
82 return (
83 <Animated.View
84 style={[
85 heartAnimation.style,
86 {
87 left: heartPosition.x - 40, // Center the 80px heart
88 top: heartPosition.y - 40,
89 transform: [
90 { scale: heartScale },
91 { rotate: heartRotation.interpolate({
92 inputRange: [-180, 180],
93 outputRange: ['-180deg', '180deg']
94 })}
95 ],
96 opacity: heartOpacity,
97 }
98 ]}
99 pointerEvents="none"
100 >
101 <Ionicons name="heart" size={80} color="#ff3040" />
102 </Animated.View>
103 )
104 }
105)
106
107HeartAnimation.displayName = 'HeartAnimation'
108
109const heartAnimation = StyleSheet.create({
110 style: {
111 position: 'absolute',
112 justifyContent: 'center',
113 alignItems: 'center',
114 zIndex: 1000,
115 },
116})