【搭建react-native项目框架】4.自定义TabBar中间按钮,实现播放时旋转动画

关于如何集成TabBar,请看上一节《【搭建react-native项目框架】3.集成第三方路由和tab页》

本节只讲解如何自定义TabBar的中间按钮,以及播放时旋转动画的实现。

关于动画与播放器的集成可以参考https://github.com/pheromone/react-native-videoDemo,但这个项目目前安卓存在bug,所以我只借鉴了动画部分。

还是先来看效果图


其实思路很简单,首先要使TabBar把中间按钮的位置空出来,然后摆上一个悬浮的按钮,就能实现中间按钮了。

1.设置CustomTabBar的placeMiddle属性为true,这个属性表示是否把中间按钮的位置留出来。
                    <CustomTabBar
                        tabNames={tabNames} //tab名称
                        placeMiddle={true} //中间是否占位,即中间是否需要用特殊按钮样式等
                    />

2.悬浮效果可以用绝对定位来实现,见下图


在components下新建一个playButton.js文件。先写一个外层View,绝对定位到页面底部中间位置;再做个带边框的圆形View;然后用长方形View将圆形View下半部的边框覆盖;最后写个旋转图和按钮。

                <View style={[styles.playBox]}>
                    <View style={[styles.playBoxCircle]} />
                    <View style={[styles.playBoxBackground]} />
                    <TouchableOpacity onPress={() => this.play()} underlayColor="transparent" style={[styles.playInner]}>
                        <View style={[styles.playInnerBox]}>
                            <Animated.Image
                                source={require('./resources/images/src/miss.jpg')}
                                style={[styles.playBackImage, {
                                    transform: [
                                        //使用interpolate插值函数,实现了从数值单位的映射转换,上面角度从0到1,这里把它变成0-360的变化
                                        {rotateZ: this.state.rotateValue.interpolate({
                                                inputRange: [0,1],
                                                outputRange: ['0deg', '360deg'],
                                            })},
                                    ]
                                }]}
                            />
                            <Image
                                source={this.state.playImage}
                                style={[{width: Common.autoScaleSize(32), height: Common.autoScaleSize(32)}]}
                            />
                        </View>
                    </TouchableOpacity>
                </View>

const styles = StyleSheet.create({
    playBox: {
        width: Common.autoScaleSize(128),
        height: Common.autoScaleSize(136),
        position: 'absolute',
        bottom: 0,
        left: Common.autoScaleSize(311),
        flexDirection: 'column',
        justifyContent: 'flex-start',
        alignItems: 'center',
    },
    playBoxCircle: {
        backgroundColor: '#ffffff',
        width: Common.autoScaleSize(128),
        height: Common.autoScaleSize(128),
        borderRadius: Common.autoScaleSize(128),
        position: 'absolute',
        bottom: Common.autoScaleSize(8),
        borderWidth: Common.autoScaleSize(1),
        borderColor: '#cdcdcd',
    },
    playBoxBackground: {
        backgroundColor: '#ffffff',
        width: Common.autoScaleSize(125),
        height: Common.autoScaleSize(72),
        position: 'absolute',
        bottom: 0,
        left: Common.autoScaleSize(1),
    },
    playInner: {
        width: Common.autoScaleSize(101),
        height: Common.autoScaleSize(101),
        borderRadius: Common.autoScaleSize(101),
        position: 'absolute',
        bottom: Common.autoScaleSize(20),
        flexDirection: 'column',
        justifyContent: 'center',
        alignItems: 'center',
    },
    playInnerBox: {
        backgroundColor: '#cdcdcd',
        width: Common.autoScaleSize(101),
        height: Common.autoScaleSize(101),
        borderRadius: Common.autoScaleSize(101),
        flexDirection: 'column',
        justifyContent: 'center',
        alignItems: 'center',
    },
    playBackImage: {
        width: Common.autoScaleSize(101),
        height: Common.autoScaleSize(101),
        borderRadius: Common.autoScaleSize(101),
        position: 'absolute',
    },
});
3.实现旋转动画。

先构造初始旋转角度、播放状态和旋转动画

        this.state = {
            playImage: require('./resources/images/play.png'),
            rotateValue: new Animated.Value(0), //旋转角度的初始值
        };
        this.isPlaying = false;
        this.playerAnimated = Animated.timing(this.state.rotateValue, {
            toValue: 1, //角度从0变1
            duration: 15000, //从0到1的时间
            easing: Easing.inOut(Easing.linear), //线性变化,匀速旋转
        });

根据播放状态切换播放按钮的图标,并开始/暂停播放

    play() {
        this.isPlaying = !this.isPlaying;
        if (this.isPlaying === true) {
            this.setState({
                playImage: require('./resources/images/pause.png'),
            });
            this.startPlay();
        } else {
            this.setState({
                playImage: require('./resources/images/play.png'),
            });
            this.stopPlay();
        }
    }

开始播放

    startPlay() {
        this.playerAnimated.start(() => {
            this.playerAnimated = Animated.timing(this.state.rotateValue, {
                toValue: 1, //角度从0变1
                duration: 15000, //从0到1的时间
                easing: Easing.inOut(Easing.linear), //线性变化,匀速旋转
            });
            this.rotating();
        });
    }

暂停播放

    stopPlay() {
        this.state.rotateValue.stopAnimation((oneTimeRotate) => {
            //计算角度比例
            this.playerAnimated = Animated.timing(this.state.rotateValue, {
                toValue: 1,
                duration: (1-oneTimeRotate) * 15000,
                easing: Easing.inOut(Easing.linear),
            });
        });
    }

开始旋转动画

    rotating() {
        if (this.isPlaying) {
            this.state.rotateValue.setValue(0);
            this.playerAnimated.start(() => {
                this.rotating()
            })
        }
    };
4.在App.js文件中引入playButton.js
import PlayButton from "./components/playButton";

在最外层View组件底部渲染PlayButton

            <View style={[{flex: 1}]}>
                //Router......
                <PlayButton />
            </View>


最后上完整的playButton.js代码

import React, { Component } from 'react';
import {
    Animated,
    Easing,
    StyleSheet,
    View,
    TouchableOpacity,
    Image,
} from "react-native";
//自定义组件
import Common from "./common";
//页面
import PlayScreen from '../views/play'; //播放页

export default class PlayButton extends Component {
    constructor(props) {
        super(props);
        //使用Animated.Value设定初始化值(角度)
        this.state = {
            playImage: require('../resources/images/play.png'),
            rotateValue: new Animated.Value(0), //旋转角度的初始值
        };
        this.isPlaying = false;
        this.playerAnimated = Animated.timing(this.state.rotateValue, {
            toValue: 1, //角度从0变1
            duration: 15000, //从0到1的时间
            easing: Easing.inOut(Easing.linear), //线性变化,匀速旋转
        });
    }

    play() {
        this.isPlaying = !this.isPlaying;
        if (this.isPlaying === true) {
            this.setState({
                playImage: require('../resources/images/pause.png'),
            });
            this.startPlay();
        } else {
            this.setState({
                playImage: require('../resources/images/play.png'),
            });
            this.stopPlay();
        }
    }

    rotating() {
        if (this.isPlaying) {
            this.state.rotateValue.setValue(0);
            this.playerAnimated.start(() => {
                this.rotating()
            })
        }
    };

    startPlay() {
        this.playerAnimated.start(() => {
            this.playerAnimated = Animated.timing(this.state.rotateValue, {
                toValue: 1, //角度从0变1
                duration: 15000, //从0到1的时间
                easing: Easing.inOut(Easing.linear), //线性变化,匀速旋转
            });
            this.rotating();
        });
    }

    stopPlay() {
        this.state.rotateValue.stopAnimation((oneTimeRotate) => {
            //计算角度比例
            this.playerAnimated = Animated.timing(this.state.rotateValue, {
                toValue: 1,
                duration: (1-oneTimeRotate) * 15000,
                easing: Easing.inOut(Easing.linear),
            });
        });
    }

    render() {
        return (
            <View style={[styles.playBox]}>
                <View style={[styles.playBoxCircle]} />
                <View style={[styles.playBoxBackground]} />
                <TouchableOpacity onPress={() => this.play()} underlayColor="transparent" style={[styles.playInner]}>
                    <View style={[styles.playInnerBox]}>
                        <Animated.Image
                            source={require('../resources/images/src/miss.jpg')}
                            style={[styles.playBackImage, {
                                transform: [
                                    //使用interpolate插值函数,实现了从数值单位的映射转换,上面角度从0到1,这里把它变成0-360的变化
                                    {rotateZ: this.state.rotateValue.interpolate({
                                            inputRange: [0,1],
                                            outputRange: ['0deg', '360deg'],
                                        })},
                                ]
                            }]}
                        />
                        <Image
                            source={this.state.playImage}
                            style={[{width: Common.autoScaleSize(32), height: Common.autoScaleSize(32)}]}
                        />
                    </View>
                </TouchableOpacity>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    playBox: {
        width: Common.autoScaleSize(128),
        height: Common.autoScaleSize(136),
        position: 'absolute',
        bottom: 0,
        left: Common.autoScaleSize(311),
        flexDirection: 'column',
        justifyContent: 'flex-start',
        alignItems: 'center',
    },
    playBoxCircle: {
        backgroundColor: '#ffffff',
        width: Common.autoScaleSize(128),
        height: Common.autoScaleSize(128),
        borderRadius: Common.autoScaleSize(128),
        position: 'absolute',
        bottom: Common.autoScaleSize(8),
        borderWidth: Common.autoScaleSize(1),
        borderColor: '#cdcdcd',
    },
    playBoxBackground: {
        backgroundColor: '#ffffff',
        width: Common.autoScaleSize(128),
        height: Common.autoScaleSize(101),
        position: 'absolute',
        bottom: 0,
    },
    playInner: {
        width: Common.autoScaleSize(101),
        height: Common.autoScaleSize(101),
        borderRadius: Common.autoScaleSize(101),
        position: 'absolute',
        bottom: Common.autoScaleSize(20),
        flexDirection: 'column',
        justifyContent: 'center',
        alignItems: 'center',
    },
    playInnerBox: {
        backgroundColor: '#cdcdcd',
        width: Common.autoScaleSize(101),
        height: Common.autoScaleSize(101),
        borderRadius: Common.autoScaleSize(101),
        flexDirection: 'column',
        justifyContent: 'center',
        alignItems: 'center',
    },
    playBackImage: {
        width: Common.autoScaleSize(101),
        height: Common.autoScaleSize(101),
        borderRadius: Common.autoScaleSize(101),
        position: 'absolute',
    },
});

猜你喜欢

转载自blog.csdn.net/danding_ge/article/details/80480952