1. 程式人生 > >【搭建react-native專案框架】4.自定義TabBar中間按鈕,實現播放時旋轉動畫

【搭建react-native專案框架】4.自定義TabBar中間按鈕,實現播放時旋轉動畫

本節只講解如何自定義TabBar的中間按鈕,以及播放時旋轉動畫的實現。

還是先來看效果圖

其實思路很簡單,首先要使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',
    },
});