UNPKG

react-native-wheel-of-fortune-dp

Version:
442 lines (405 loc) 14.9 kB
import React, { Component } from 'react'; import { View, StyleSheet, Dimensions, Animated, TouchableOpacity, Image, } from 'react-native'; import * as d3Shape from 'd3-shape'; import Svg, { G, Text, TSpan, Path, Pattern, SvgCss } from 'react-native-svg'; import { ImageRender } from './imageReward'; const AnimatedSvg = Animated.createAnimatedComponent(Svg); const { width, height } = Dimensions.get('screen'); class WheelOfFortune extends Component { constructor(props) { super(props); this.state = { enabled: false, started: false, finished: false, winner: null, gameScreen: new Animated.Value(width - 100), wheelOpacity: new Animated.Value(1), imageLeft: new Animated.Value(width / 2 - 30), imageTop: new Animated.Value(height / 2 - 70), }; this.angle = 0; this.prepareWheel(); } prepareWheel = () => { this.Rewards = this.props.options.rewards; this.RewardCount = this.Rewards.length; this.numberOfSegments = this.RewardCount; this.fontSize = this.props.options.fontSize ? this.props.options.fontSize : 20; this.fontFamily = 20; this.oneTurn = 360; this.angleBySegment = this.oneTurn / this.numberOfSegments; this.angleOffset = this.angleBySegment / 2; this.winner = this.props.options.winner != undefined ? this.props.options.winner : Math.floor(Math.random() * this.numberOfSegments); this._wheelPaths = this.makeWheel(); this._angle = new Animated.Value(0); this.props.options.onRef(this); }; resetWheelState = () => { this.setState({ enabled: false, started: false, finished: false, winner: null, gameScreen: new Animated.Value(width - 100), wheelOpacity: new Animated.Value(1), imageLeft: new Animated.Value(width / 2 - 30), imageTop: new Animated.Value(height / 2 - 70), }); }; _tryAgain = () => { this.prepareWheel(); this.resetWheelState(); this.angleListener(); this._onPress(); }; angleListener = () => { this._angle.addListener(event => { if (this.state.enabled) { this.setState({ enabled: false, finished: false, }); } this.angle = event.value; }); }; componentWillUnmount() { this.props.options.onRef(undefined); } componentDidMount() { this.angleListener(); } makeWheel = () => { const data = Array.from({ length: this.numberOfSegments }).fill(1); const arcs = d3Shape.pie()(data); var colors = this.props.options.colors ? this.props.options.colors : [ '#E07026', '#E8C22E', '#ABC937', '#4F991D', '#22AFD3', '#5858D0', '#7B48C8', '#D843B9', '#E23B80', '#D82B2B', ]; return arcs.map((arc, index) => { const instance = d3Shape .arc() .padAngle(0.01) .outerRadius(width / 2) .innerRadius(this.props.options.innerRadius || 100); return { path: instance(arc), color: colors[index % colors.length], value: this.Rewards[index], centroid: instance.centroid(arc), }; }); }; _getWinnerIndex = () => { const deg = Math.abs(Math.round(this.angle % this.oneTurn)); // wheel turning counterclockwise if (this.angle < 0) { return Math.floor(deg / this.angleBySegment); } // wheel turning clockwise return ( (this.numberOfSegments - Math.floor(deg / this.angleBySegment)) % this.numberOfSegments ); }; _onPress = () => { const duration = this.props.options.duration || 10000; this.setState({ started: true, }); // console.log('onPress', (365 - // this.winner * (this.oneTurn / this.numberOfSegments) + // 360 * (duration / 1000))) // console.log('duration', duration) Animated.timing(this._angle, { toValue: 365 - this.winner * (this.oneTurn / this.numberOfSegments) + 360 * (duration / 1000), duration: duration, useNativeDriver: true, }).start(() => { const winnerIndex = this._getWinnerIndex(); this.setState({ finished: true, winner: this._wheelPaths[winnerIndex].value, }); this.props.getWinner(this._wheelPaths[winnerIndex].value, winnerIndex); }); }; _textRender = (x, y, number, i) => ( <Text x={x - number.length * (this.fontSize/6 * number.length) / 2.5} y={y - 80} fill={ this.props.options.textColor ? this.props.options.textColor : '#fff' } style={{ fontFamily : this.props.options.fontFamily ? this.props.options.fontFamily : 'Arial' }} textAnchor="middle" fontSize={this.fontSize}> {Array.from({ length: number.length }).map((_, j) => { // Render reward text vertically if (this.props.options.textAngle === 'vertical') { return ( <TSpan x={x} dy={this.fontSize} key={`arc-${i}-slice-${j}`}> {number.charAt(j)} </TSpan> ); } // Render reward text horizontally else { return ( <TSpan y={y - 40} dx={this.fontSize * 0.07} key={`arc-${i}-slice-${j}`}> {number.charAt(j)} </TSpan> ); } })} <ImageRender x={x} y={y} i={i} options={this.props.options} ></ImageRender> {/* <Text x={x - number.length + 3} y={y - 14} fill={ this.props.options.textColor ? this.props.options.textColor : '#fff' } textAnchor="middle" fontSize={this.fontSize - 4}> {this.props.options.typeRewards ? this.props.options.typeRewards[i] : 'cash'} </Text> */} </Text> ); _textRenderOld = (x, y, number, i) => ( <Text x={x - number.length * 5} y={y - 80} fill={ this.props.options.textColor ? this.props.options.textColor : '#fff' } textAnchor="middle" fontSize={this.fontSize}> {Array.from({ length: number.length }).map((_, j) => { // Render reward text vertically if (this.props.options.textAngle === 'vertical') { return ( <TSpan x={x} dy={this.fontSize} key={`arc-${i}-slice-${j}`}> {number.charAt(j)} </TSpan> ); } // Render reward text horizontally else { return ( <TSpan y={y - 40} dx={this.fontSize * 0.07} key={`arc-${i}-slice-${j}`}> {number.charAt(j)} </TSpan> ); } })} </Text> ); _renderSvgWheel = () => { return ( <View style={styles.container}> {this._renderKnob()} <Animated.View style={{ alignItems: 'center', justifyContent: 'center', transform: [ { rotate: this._angle.interpolate({ inputRange: [-this.oneTurn, 0, this.oneTurn], outputRange: [ `-${this.oneTurn}deg`, `0deg`, `${this.oneTurn}deg`, ], }), }, ], backgroundColor: this.props.options.backgroundColor ? this.props.options.backgroundColor : '#fff', width: width - 90, height: width - 90, borderRadius: (width - 20) / 2, borderWidth: this.props.options.borderWidth ? this.props.options.borderWidth : 2, borderColor: this.props.options.borderColor ? this.props.options.borderColor : '#fff', opacity: this.state.wheelOpacity, }}> <AnimatedSvg width={this.state.gameScreen} height={this.state.gameScreen} viewBox={`0 0 ${width} ${width}`} style={{ transform: [{ rotate: `-${this.angleOffset}deg` }], margin: 10, }}> <G y={width / 2} x={width / 2}> {this._wheelPaths.map((arc, i) => { const [x, y] = arc.centroid; const number = arc.value.toString(); return ( <G key={`arc-${i}`}> <Path d={arc.path} strokeWidth={2} fill={arc.color} /> <G rotation={ (i * this.oneTurn) / this.numberOfSegments + this.angleOffset } origin={`${x}, ${y}`}> {this._textRender(x, y, number, i)} </G> </G> ); })} </G> </AnimatedSvg> </Animated.View> </View> ); }; _renderKnob = () => { const knobSize = this.props.options.knobSize ? this.props.options.knobSize : 20; // [0, this.numberOfSegments] const YOLO = Animated.modulo( Animated.divide( Animated.modulo( Animated.subtract(this._angle, this.angleOffset), this.oneTurn, ), new Animated.Value(this.angleBySegment), ), 1, ); return ( <Animated.View style={{ top: 16, position: 'relative', width: knobSize, height: knobSize * 2, justifyContent: 'flex-end', zIndex: 1, opacity: this.state.wheelOpacity, transform: [ { rotate: YOLO.interpolate({ inputRange: [-1, -0.5, -0.0001, 0.0001, 0.5, 1], outputRange: [ '0deg', '0deg', '35deg', '-35deg', '0deg', '0deg', ], }), }, ], }}> <Svg width={knobSize} height={(knobSize * 100) / 57} viewBox={`0 0 57 100`} style={{ transform: [{ translateY: 8 }], }}> <Image source={ this.props.options.knobSource ? this.props.options.knobSource : require('../assets/images/knob.png') } style={{ width: knobSize, height: (knobSize * 100) / 57 }} /> </Svg> </Animated.View> ); }; _renderTopToPlay() { if (this.state.started == false) { return ( <TouchableOpacity onPress={() => this._onPress()}> {this.props.options.playButton()} </TouchableOpacity> ); } } render() { return ( <View style={styles.container}> <View style={{ position: 'absolute', top: -100, width: width, height: height / 2, justifyContent: 'center', alignItems: 'center', }}> <Animated.View style={[styles.content, { padding: 10 }]}> {this._renderSvgWheel()} </Animated.View> </View> {this.props.options.playButton ? this._renderTopToPlay() : null} </View> ); } } export default WheelOfFortune; const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', }, content: {}, startText: { fontSize: 50, color: '#fff', fontWeight: 'bold', textShadowColor: 'rgba(0, 0, 0, 0.4)', textShadowOffset: { width: -1, height: 1 }, textShadowRadius: 10, }, });