11import { useSearchParams } from '@solidjs/router' ;
2- import { animate , inView , stagger } from 'motion' ;
2+ import { type AnimationPlaybackControls , animate , inView , spring , stagger } from 'motion' ;
33import { For , createSignal } from 'solid-js' ;
44import { TransitionGroup } from 'solid-transition-group' ;
55
@@ -45,9 +45,17 @@ export const categories = [
4545 a . localeCompare ( b ) ,
4646 ) ,
4747] as const ;
48+
49+ interface Card extends HTMLDivElement {
50+ flip ?: AnimationPlaybackControls ;
51+ }
52+ interface AnimatedList extends HTMLUListElement {
53+ flip ?: AnimationPlaybackControls ;
54+ }
4855export function Recipes ( ) {
4956 const [ searchParams , setSearchParams ] = useSearchParams ( ) ;
5057 const [ showAnimation , setShowAnimation ] = createSignal ( true ) ;
58+ let list : AnimatedList | undefined ;
5159
5260 if ( typeof window !== 'undefined' && ! window . matchMedia ( '(min-width: 640px)' ) . matches ) {
5361 setSearchParams ( { recipe : 'all' } , { replace : true } ) ;
@@ -57,9 +65,10 @@ export function Recipes() {
5765 return (
5866 < ul
5967 class = "flex flex-wrap gap-4"
60- ref = { ( el ) => {
68+ ref = { ( el : AnimatedList ) => {
69+ list = el ;
6170 const isMobile = ! window . matchMedia ( '(min-width: 640px)' ) . matches ;
62- const cards = el . querySelectorAll < HTMLDivElement > ( '.card-flip' ) ;
71+ const cards = el . querySelectorAll < Card > ( '.card-flip' ) ;
6372
6473 if ( isMobile ) {
6574 inView (
@@ -79,16 +88,31 @@ export function Recipes() {
7988 } else {
8089 inView (
8190 el ,
82- ( _card , _entry ) => {
91+ ( _list , _entry ) => {
92+ // Prevent running altogether if they hovered
8393 if ( ! showAnimation ( ) ) return ;
8494
85- animate ( cards , { rotateY : 180 } , { duration : 0.5 , delay : stagger ( 0.2 ) } ) . then ( async ( ) => {
86- await animate (
95+ el . flip = animate (
96+ cards ,
97+ { rotateY : 180 } ,
98+ {
99+ duration : 0.5 ,
100+ delay : stagger ( 0.2 ) ,
101+ type : spring ,
102+ } ,
103+ ) ;
104+ el . flip . then ( async ( ) => {
105+ // Prevent running altogether if they hovered
106+ if ( ! showAnimation ( ) ) return ;
107+
108+ el . flip = animate (
87109 cards ,
88110 { rotateY : 0 } ,
89- { duration : 0.5 , delay : stagger ( 0.2 , { startDelay : 2 } ) } ,
111+ { duration : 0.5 , delay : stagger ( 0.2 , { startDelay : 2 } ) , type : spring } ,
90112 ) ;
91- cards . forEach ( ( card ) => ( card . style . transform = '' ) ) ;
113+
114+ await el . flip ;
115+ cards . forEach ( ( card ) => card . style . removeProperty ( 'transform' ) ) ;
92116 } ) ;
93117 } ,
94118 { amount : 0.5 } ,
@@ -108,7 +132,23 @@ export function Recipes() {
108132 >
109133 < For each = { recipe ( ) } >
110134 { ( Recipe ) => (
111- < li class = "scale-100 z-10 w-full sm:w-auto" onPointerEnter = { ( ) => setShowAnimation ( false ) } >
135+ < li
136+ class = "scale-100 z-10 w-full sm:w-auto"
137+ onPointerEnter = { ( ) => {
138+ setShowAnimation ( false ) ;
139+
140+ if ( ! list ) return ;
141+
142+ list . flip ?. cancel ( ) ;
143+ const cards = list . querySelectorAll < Card > ( '.card-flip' ) ;
144+ for ( const card of cards ) {
145+ //! Leaves it in a "broken" state if we don't wait until the animation finishes
146+ requestAnimationFrame ( ( ) => {
147+ card . style . removeProperty ( 'transform' ) ;
148+ } ) ;
149+ }
150+ } }
151+ >
112152 < Recipe />
113153 </ li >
114154 ) }
0 commit comments