Spinner wheel
Discount spinner
<Script runBeforeMount={true} :as="animation">
const offers = [
{ label: '10% OFF', color: 'red' },
{ label: '10% OFF', color: 'red' },
{ label: '10% OFF', color: 'red' },
{ label: '10% OFF', color: 'red' },
{ label: '20% OFF', color: 'maroon' },
{ label: '30% OFF', color: 'blue' },
{ label: '40% OFF', color: 'purple' },
{ label: '50% OFF', color: 'green' },
{ label: 'Try Again', color: 'red' }
]
setStates({
isInteracted: false,
isSpinning: false,
offers,
selectedOffer: null,
finalSegment: null
})
const rotation = new Animated.Value(0)
const segmentCount = offers.length
const segmentAngle = 360 / segmentCount
const style = {
transform: [{
rotate: rotation.interpolate({
inputRange: [0, 360],
outputRange: ['0deg', '360deg']
})
}]
}
</Script>
<Script :dependencies={[animation, state.isSpinning]} :as="actions">
function startWheel() {
if (state.isSpinning) return
setState('isSpinning', true)
setState('isInteracted', true)
const spin = Animated.loop(
Animated.sequence([
Animated.timing(animation.rotation, {
toValue: 360,
duration: 1500,
easing: rn.Easing.linear,
useNativeDriver: true
}),
Animated.timing(animation.rotation, {
toValue: 0,
duration: 0,
useNativeDriver: true
})
]),
{ iterations: Infinity }
)
spin.start()
}
function stopWheel() {
if (!state.isSpinning) return
setState('isSpinning', false)
if (animation.spin) {
animation.spin.stop()
}
animation.rotation.stopAnimation(currentValue => {
const currentRotation = currentValue % 360
const finalIndex = Math.floor(Math.random() * animation.segmentCount)
const desiredRotation = 360 - (finalIndex * animation.segmentAngle + animation.segmentAngle / 2)
let delta = desiredRotation - currentRotation
if (delta < 0) delta += 360
delta += 360 * 3
const targetRotation = currentValue + delta
Animated.timing(animation.rotation, {
toValue: targetRotation,
duration: 5000,
easing: rn.Easing.out(rn.Easing.quad),
useNativeDriver: true
}).start(() => {
setState('selectedOffer', state.offers[finalIndex].label)
setState('finalSegment', finalIndex)
})
})
}
</Script>
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<View style={{ width: 300, height: 300, position: 'relative' }}>
<AnimatedView style={[
{
position: 'absolute',
top: 10,
left: 10,
width: 280,
height: 280,
borderRadius: 140,
backgroundColor: 'orange',
overflow: 'hidden',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.3,
shadowRadius: 3
},
animation.style
]}>
<Fragment :forEach={state.offers} :as="offer">
<View style={{
position: 'absolute',
left: 140,
top: 140 - 10,
width: 140,
height: 20
}}>
<AnimatedView style={{
width: '100%',
height: '100%',
transform: [
{ translateX: -70 },
{ rotate: ((data.offerIndex * animation.segmentAngle + animation.segmentAngle / 2) - 90) + 'deg' },
{ translateX: 70 }
],
backgroundColor: "white",
justifyContent: 'center'
}}>
<Text py={4} style={{
width: '100%',
color: offer.color,
fontWeight: 'bold',
fontSize: 12,
textAlign: 'center'
}}>
{offer.label}
</Text>
</AnimatedView>
</View>
</Fragment>
</AnimatedView>
<Text style={{
position: 'absolute',
top: 0,
left: "50%",
transform: [{ translateX: -15 }],
fontSize: 20,
fontWeight: 'bold'
}}>
<Icon icon="fontisto:caret-down" color="success" />
</Text>
</View>
<View style={{ flexDirection: 'row', marginTop: 30 }}>
<Button
action={actions.startWheel}
disabled={state.isSpinning || state.isInteracted}
style={{
marginHorizontal: 10,
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 24
}}
>
<Text style={{ color: '#fff', fontSize: 16 }}>START</Text>
</Button>
<Button
action={actions.stopWheel}
disabled={!state.isSpinning}
color="danger"
style={{
marginHorizontal: 10,
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 24
}}
>
<Text style={{ color: '#fff', fontSize: 16 }}>STOP</Text>
</Button>
</View>
<Text :if={state.selectedOffer} style={{ marginTop: 20, fontSize: 18, fontWeight: 'bold', color: '#333' }}>
You won: {state.selectedOffer}
</Text>
</View>
{}Loading...