atlanteh / react-native-slot-machine

Text slot machine for react-native
MIT License
156 stars 39 forks source link

can this be configured to be progressive jackpot? #11

Closed craigcosmo closed 1 year ago

craigcosmo commented 5 years ago

instead of random jackpot. Can this be configured to be progessive?

Like I got the number 1,234 the next number will be 1,235 the next one will be 1,235

possible?

atlanteh commented 5 years ago

You can set initialAnimation to false to prevent the first animation, and only subsequent animations will run which I believe is what you want.

craigcosmo commented 5 years ago

I set to false. then I got this. watch the bottom of the screen

https://youtu.be/_FvJJ91SXqM

it's a bit weird

atlanteh commented 5 years ago

Do you continuously update the component? If so, can you try slowing down the update rates and see if it still persists?

craigcosmo commented 5 years ago

in progressive jackpot, it got updated continuously

atlanteh commented 5 years ago

I think this is might happen if the next update happens before the previous update has finished. Can you try slowing down the update rate, so that each update only occurs after previous animation has completed, and see if this issue still persists?

craigcosmo commented 5 years ago

i slowed it down. quite closed.

https://youtu.be/hMvxkKp_oYA

But doesn't look right when it transition from something like 19 to 20. The last digit (9) had to go all the way from 9-8-7-6-5-4-2-1 to become 0. the last digit should just be 9 then 0.

atlanteh commented 5 years ago

This is the current implementation. Unfortunately, I'm not actively working on this repo, but If you want, you can create PR with a fix and I'll be happy to merge it :)

craigcosmo commented 5 years ago

I faster the animation, the effect of 9 to 0 is unnoticeable. Now I need to put a thousand separator, any suggestion on how to do that?

atlanteh commented 5 years ago

Yes, you can add comma to the range prop ("9876543210,") and pass the text formatted ("1,234").

craigcosmo commented 5 years ago

somehow it doesnt show the comma

atlanteh commented 5 years ago

Can u post your code?

craigcosmo commented 5 years ago

yes, can

export default class SlotMachine extends Component {

    static get defaultProps() {
        return {
            text: 0,
            width: 37,
            height: 50,
            padding: 4,
            duration: 110,
            delay: 0,
            slotInterval: 500,
            defaultChar: '0',
            range: '9876543210,',
            initialAnimation: false,
            styles: {},
            renderTextContent: (currentChar) => currentChar,
            useNativeDriver: false,
        };
    }

    constructor(props) {
        super(props);
        this.renderSlot = this.renderSlot.bind(this);
        this.startInitialAnimation = this.startInitialAnimation.bind(this);
        this.renderContent = this.renderContent.bind(this);

        this.text = props.text;
        let values;
        if (props.initialAnimation) {
            values = this.getInitialSlotsValues(props);
        } else {
            values = this.getAlignedValues(props).map(val => new Animated.Value(val));
        }
        this.state = {values, initialAnimation: false};
    }

    componentDidMount() {
        const {delay, initialAnimation} = this.props;
        if (!initialAnimation) {
            return;
        }
        setTimeout(this.startInitialAnimation, delay);
    }

    componentWillReceiveProps(newProps) {
        if (newProps.text === this.text) {
            return;
        }
        this.text = newProps.text;
        const {range, duration, useNativeDriver} = newProps;
        const easing = Easing.inOut(Easing.ease);
        const paddedStr = this.getPaddedString(newProps);
        const newValues = this.getAdjustedAnimationValues(newProps);

        this.setState({values: newValues}, () => {
            const newAnimations = paddedStr.split('').map((char, i) => {
                const index = range.indexOf(char);
                const animationValue = -1 * (index) * newProps.height;
                return Animated.timing(this.state.values[i], {toValue: animationValue, duration, easing, useNativeDriver: useNativeDriver});
            });
            Animated.parallel(newAnimations).start();
        });
    }

    getAdjustedAnimationValues(props) {
        const {values} = this.state;
        const paddedStr = this.getPaddedString(props);
        let neededValues = paddedStr.length - values.length;

        if (neededValues <= 0) {
            return values;
        }

        const defaultIndex = props.range.indexOf(props.defaultChar);
        const defaultPosition = this.getPosition(defaultIndex, props);
        const newValues = [...values];

        while (neededValues--) {
            newValues.unshift(new Animated.Value(defaultPosition));
        }

        return newValues;
    }

    getPosition(index, props = this.props) {
        const position = -1 * (index) * props.height;
        return position;
    }

    getAlignedValues(props) {
        const paddedStr = this.getPaddedString();
        const {range} = props;

        const values = paddedStr.split('').map((char) => {
            const index = range.indexOf(char);
            const animationValue = this.getPosition(index, props);
            return animationValue;
        });

        return values;
    }

    getInitialSlotsValues(props) {
        const values = [];
        const strNum = String(this.text);
        const animationValue = this.getPosition(props.range.length - 1, props);

        let slotCount = Math.max(props.padding, strNum.length);
        while (slotCount--) {
            values.push(new Animated.Value(animationValue));
        }

        return values;
    }

    getPaddedString(props = this.props) {
        const {padding, defaultChar} = props;

        let paddedText = String(this.text);
        let neededPadding = Math.max(0, padding - paddedText.length);
        while ((neededPadding--) > 0) {
            paddedText = `${defaultChar}${paddedText}`;
        }

        return paddedText;
    }

    generateSlots() {
        const paddedStr = this.getPaddedString();
        const elements = paddedStr.split('').map(this.renderSlot);
        return elements;
    }

    startInitialAnimation() {
        const {values} = this.state;
        const {duration, slotInterval, useNativeDriver} = this.props;
        const easing = Easing.inOut(Easing.ease);

        const animations = values.map((value, i) => {
            const animationDuration = duration - (values.length - 1 - i) * slotInterval;
            return Animated.timing(value, {toValue: 0, duration: animationDuration, easing, useNativeDriver: useNativeDriver});
        });

        Animated.parallel(animations).start(() => {
            const newValues = this.getAlignedValues(this.props);
            newValues.forEach((value, i) => values[i].setValue(value));
            this.setState({initialAnimation: false});
        });

        this.setState({initialAnimation: true});
    }

    spinTo(value) {
        this.text = value;
        const values = this.getInitialSlotsValues(this.props);
        this.setState({values}, () => this.startInitialAnimation());
    }

    renderContent(currentChar, i, range) {
        const {styles: overrideStyles, renderTextContent} = this.props;        
        const textContent = renderTextContent(currentChar, i, range);
        return (<Text style={[styles.text, overrideStyles.text]}>{textContent}</Text>);
    }

    renderSlot(charToShow, position) {
        const {range, styles: overrideStyles, height, width, renderContent = this.renderContent} = this.props;
        const {initialAnimation, values} = this.state;
        const charToShowIndex = range.indexOf(charToShow);

        const slots = range.split('').map((num, i) => {
            let currentChar = num;
            if (initialAnimation) {
                const currentIndex = (i + charToShowIndex) % range.length;
                currentChar = range[currentIndex];
            }

            const content = renderContent(currentChar, i, range);
            return (
                <Animated.View
                    key={i}
                    style={[styles.slotInner, {height}, overrideStyles.slotInner, {transform: [{translateY: values[position]}]} ]}
                >
                    {content}
                </Animated.View>
            );
        });

        return (
            <View key={position} style={[styles.slotWrapper, {height, width}, overrideStyles.slotWrapper]}>
                {slots}
                <View style={[styles.innerBorder, overrideStyles.innerBorder]} />
                <View style={[styles.outerBorder, overrideStyles.outerBorder]} />

            </View>
        );
    }

    render() {
        const slots = this.generateSlots();
        return (
            <View style={[styles.container, this.props.styles.container]}>
                {slots}
            </View>
        );
    }
}
atlanteh commented 5 years ago

Isn't this my code? What I mean is, can you show how you use the component? <SlotMachine range="9876543210," text="1,234" /> should do the trick of showing comma

craigcosmo commented 5 years ago

sorry, did as you said, it works perfectly.

Just one more thing, I want to change the width, the default is 50, I want to chage it depend on the number of characters in the text.

how should I access the width property within a function?

setSize(text){
    if (text.length<6) {
            // console.log('text here')
            styles.text = {
                ...styles.text,
                fontSize: 64
            }
            this.width = 88
        }
    }

componentDidMount() {
    const {delay, initialAnimation} = this.props;
    if (!initialAnimation) {
        return;
    }
    setTimeout(this.startInitialAnimation, delay);
}
constructor(props) {
    this.text = props.text;
    this.setSize(this.text)
}

With that code above, I can change the the font size, but not the width. Any suggestion?