您的当前位置:首页正文

在react-native中写红包动画

来源:图艺博知识网

平时没有写技术文章的习惯,但是这次写红包的动画还是挺有意思的,记录一下,如果有需要,可以参考一下。

UI给的红包效果如下:

看到红包效果后,本来想直接让设计师导出json格式的动画数据,然后用lottie加载的,发现不行;想想算了,直接用JS写吧,于是就开始了漫长的写动画调UI的过程。

动画大致分为两个部分,点击拆红包后向服务端发请求,开始抖动红包动画,拿到请求响应后停止抖动,并开始拆红包动画。

UI部分的代码如下,数据部分和style部分都没给出。


/* global Promise */

// @flow

import React from 'react';
import { connect } from 'dva';
import {
    Text,
    View,
    StyleSheet,
    Animated,
    TouchableOpacity,
    TouchableWithoutFeedback,
} from 'react-native';
import { Modal } from 'antd-mobile';
import icon_close from '../assets/other/icon_close.png';
import icon_redPackage from '../assets/other/icon_redPackage.png';
import icon_redPackage_open from '../assets/other/icon_redPackage_open.png';
import icon_redPackage_close from '../assets/other/icon_redPackage_close.png';

type RedPackage = {
    id: String;
    name: String;
    integral: number;
};

type Props = {
    visible: boolean;
    isOpen: boolean;
    totalIntegral: number,
    redPackages: RedPackage[];
    currentRedPackage: ?RedPackage;
    isOrderResult: boolean;             // 是否是提交订单给的红包
    dispatch: () => Promise<any>;
};

@connect(state => state.redPackage)
export default class RedPackageModal extends  {

    render() {
        const { visible } = this.props;
        return (
            <Modal
                transparent
                style={styles.modal}
                visible={visible}
                maskClosable={false}
                animationType={'fade'}
            >
                {this._renderContent()}
            </Modal>
        );
    }

    _renderContent = () => {

        const {
            visible,
            redPackages,
            totalIntegral,
            isOpen,
            isOrderResult,
            currentRedPackage,
            shake,
            progress,
            dispatch
        } = this.props;

        if (!visible || !redPackages || !redPackages.length) return null;
        const { integral } = currentRedPackage || {};
        const total = totalIntegral + (isOrderResult ? 0 : integral);
        return (
            <View style={styles.container}>
                <TouchableWithoutFeedback onPress={() => {
                    if (isOpen) return;
                    // 开启红包抖动动效
                    const shakeAnimation = Animated.loop(Animated.timing(shake, {
                        toValue: 2,
                        duration: 100,
                    }));
                    shakeAnimation.start();
                    // 请求拆红包接口
                    dispatch({
                        type: 'redPackage/openRedPackage'
                    }).then(() => {
                        // 结束抖动动效
                        shakeAnimation.stop();
                        // 动画结束后,防止图片倾斜,将倾斜度置为0
                        Animated.timing(shake, {
                            toValue: 0,
                            duration: 10,
                        }).start();
                        // 开始拆红包动效
                        Animated.timing(progress, {
                            toValue: 2,
                            duration: 600,
                        }).start();
                    });
                }}>
                    <Animated.View style={{
                        // 抖动红包
                        transform: [{
                            rotateZ: shake.interpolate({
                                inputRange: [0, 0.5, 1, 1.5, 2],
                                outputRange: ['0deg', '5deg', '0deg', '-5deg', '0deg'],
                            }),
                        }],
                    }}>
                        <Animated.Image source={icon_redPackage} style={styles.redPackage} />
                        <Animated.Image source={icon_redPackage_close} style={[styles.packageClose, {
                            // 开启红包时红包关闭图片渐渐消失
                            opacity: progress.interpolate({
                                inputRange: [0, 0.25, 0.5, 0.75, 1, 2],
                                outputRange: [1, 0.98, 0.9, 0.75, 0, 0],
                            })
                        }]} />
                        <Animated.View style={[styles.top, {
                            // 开启红包时开启图片向上移动,造成翻转错觉
                            top: progress.interpolate({
                                inputRange: [0, 1, 3],
                                outputRange: [0, -38, -38],
                            })
                        }]}>
                            <Animated.Image source={icon_redPackage_open} style={[styles.packageOpen, {
                                // 开启红包时开启图片渐渐出现
                                opacity: progress.interpolate({
                                    inputRange: [0, 0.25, 0.5, 0.75, 1, 2],
                                    outputRange: [0, 0.02, 0.1, 0.25, 1, 1],
                                })
                            }]} />
                        </Animated.View>
                        <Animated.View style={[styles.content, {
                            // 开启红包后白纸探出,造成白纸跳动动效
                            top: progress.interpolate({
                                inputRange: [0, 0.75, 1, 1.5, 2],
                                outputRange: [46, 46, 36, 0, 25],
                            })
                        }]}>
                            <Animated.View style={{
                                // 开启红包后跳转白纸高度
                                height: progress.interpolate({
                                    inputRange: [0, 0.75, 1.5, 2],
                                    outputRange: [0, 0, 255, 255],
                                })
                            }}>
                                <Animated.View style={[styles.wrap, {
                                    // 白纸出现后UI渐出
                                    opacity: progress.interpolate({
                                        inputRange: [0, 1, 1.5, 2],
                                        outputRange: [0, 0, 0.2, 1],
                                    })
                                }]}>
                                    <Text style={styles.title}>恭喜发财 大吉大利</Text>
                                    <Text style={styles.integralText}>
                                        本次获得
                                        <Text style={styles.integral}>{integral}</Text>
                                        积分
                                    </Text>
                                    <Text style={styles.desc}>{isOrderResult ? '(发货后到账)' : ''}</Text>
                                    <Text style={styles.total}>您当前共有{total}积分</Text>
                                    {
                                        // 如果有多个红包,在最后一个红包显示积分商城按钮,防止流程被打断
                                        redPackages.length <= 1 ? (
                                            <TouchableOpacity style={styles.touchable} onPress={() => {
                                                // 跳转到积分商城
                                                dispatch({
                                                    type: 'redPackage/onGoToStore'
                                                });
                                            }}>
                                                <Text style={styles.touchableText}>积分商城</Text>
                                            </TouchableOpacity>
                                        ) : null
                                    }
                                </Animated.View>
                            </Animated.View>
                        </Animated.View>
                    </Animated.View>
                </TouchableWithoutFeedback>
                <Text style={styles.packageNumber}>{`您有${redPackages.length}个红包待领取`}</Text>
                {
                    // 在红包开启后显示关闭按钮
                    isOpen && currentRedPackage ? (
                        <TouchableOpacity style={styles.closeable} onPress={() => {
                            dispatch({
                                type: 'redPackage/onClose',
                            });
                        }}>
                            <Animated.Image source={icon_close} style={styles.close} />
                        </TouchableOpacity>
                    ) : null
                }
            </View >
        );
    };
}
Top