React Native 特殊過渡動畫

前序

優(yōu)秀的交互設計師在參考了京東的過渡動畫后也想在我們的產(chǎn)品上增加類型的效果,在經(jīng)過一番的Google和思考后覺得還是可以實現(xiàn)的,難度沒有想象中的那么大,這里只是做一下總結(jié)。

效果圖

GIF畫質(zhì)較渣,在模擬器上會出現(xiàn)閃動一下,但是在真機上不會出現(xiàn),可以忽略這一點。


animation.gif

屏蔽react-navigation默認的過渡動畫

關(guān)于react-navigation的使用,這里不做詳細的說明,如有需要了解的可戳這里
在創(chuàng)建createStackNavigator的時候,我們可以定制其相應的屬性值,其中有一個transitionConfig屬性值,官方的解釋是用于返回一個屏幕過渡對象的函數(shù),該對象中也包含了其他的屬性,如下代碼所示

/**
   * Describes a visual transition from one screen to another.
   */
  declare export type TransitionConfig = {
    // The basics properties of the animation, such as duration and easing
    transitionSpec?: NavigationTransitionSpec,
    // How to animate position and opacity of the screen
    // based on the value generated by the transitionSpec
    screenInterpolator?: (props: NavigationSceneRendererProps) => {},
    // How to animate position and opacity of the header componetns
    // based on the value generated by the transitionSpec
    headerLeftInterpolator?: (props: NavigationSceneRendererProps) => {},
    headerTitleInterpolator?: (props: NavigationSceneRendererProps) => {},
    headerRightInterpolator?: (props: NavigationSceneRendererProps) => {},
    // The style of the container. Useful when a scene doesn't have
    // 100% opacity and the underlying container is visible.
    containerStyle?: ViewStyleProp,
  };

其中我們可以看下screenInterpolator的作用,他是用來配置屏幕過渡動畫的。
因為我們需要實現(xiàn)特殊的過渡動畫,那么我們需要將默認的過渡動畫給取消,代碼如下

const TransitionConfiguration = () => ({
    screenInterpolator: (sceneProps) => {
        const {scene} = sceneProps
        const {route} = scene
        const params = route.params || {}
        const routeName = sceneProps.scene.route.routeName
        const transition = params.transition || 'forHorizontal'
        if (routeName === 'SecondPage'){ \\當進入SecondPage頁面的時候,取消默認的過渡動畫
            return null
        }
        return StackViewStyleInterpolator[transition](sceneProps)
    },
    transitionSpec: {
        duration: 350,
        easing: Easing.out(Easing.poly(4)),
        timing: Animated.timing,
    }
})
const StackNavigatorConfig = {
    initialRouteName:'FirstPage',
    headerMode: 'screen',
    mode:'card',
    transitionConfig: TransitionConfiguration,
}

同時我們需要注意一下StackViewStyleInterpolator 的引用:
在react-navigation的2.11.2之前:

import StackViewStyleInterpolator from 'react-navigation/src/views/StackView/StackViewStyleInterpolator'

在react-navigation的2.11.2之后:

import StackViewStyleInterpolator from 'react-navigation-stack/dist/views/StackView/StackViewStyleInterpolator'

自定義過渡動畫

這一部分要根據(jù)實際的場景去實現(xiàn),一般由設計師來決定。其實我們是沒有辦法自定義這個過渡動畫的,除非去修改源碼,但是那樣做的難度和風險都比較的大,為了實現(xiàn)這樣的“效果”,我只是在第一個頁面過渡之前加載了一段動畫,在動畫結(jié)束之后再進行跳轉(zhuǎn),因為取消了react-navigation默認的過渡的動畫,所以可以快速的展示出第二個頁面。同時為了保證整個過渡過程的流程性,第一個頁面結(jié)束時的樣子和第二個頁面開始時的樣子保持一致就OK。

第一個頁面的動畫是在點擊了某一個Item之后,在頁面的最上面一層添加一個全屏的View進行動畫,跟ListView無任何的關(guān)系。你們會發(fā)現(xiàn)有一些共用的地方(圖片+名稱),這其實是使用RN提供的UIManager.measure方法獲取到點擊事件的位置,然后計算出圖標和名稱的位置,在最上面一層的View渲染另一個圖標和名稱,從而做到以假亂真的目的。

因為該過渡動畫的重用性不太高,這里就不全部展示出來了。

_startAnimated (rowData, event) {
    this.props.navigation.setParams({enable: true})
    UIManager.measure(event.target, (x, y, width, height, pageX, pageY) => {
      this.setState({
        isShowItemModel: true,
        currentDataSource: rowData,
        currentClickY: pageY - 64,
        showReView: false,
      })
    })
  }

在動畫結(jié)束之后,執(zhí)行跳轉(zhuǎn)動作。

this.props.navigation.navigate('SecondPage')

注意點

因為最上層的View只覆蓋了導航欄一下的部分,所以導航欄上面的按鈕還是可以點擊的,所以在執(zhí)行動畫的過程中需要禁止掉按鈕點擊事件。因為導航按鈕是在react-navigation屬性中配置的,所以只能在this.props.navigation.params中添加一個變量來控制按鈕的點擊。

static navigationOptions = ({navigation, screenProps}) => ({
        title: 'FirstPage', // 固定標題
        headerRight: <TouchableOpacity style={{marginRight:20}}
                                       disabled={navigation.state.params ? navigation.state.params.disable : false}
                                       onPress={() => {
                                           const {params = {}} = navigation.state
                                           params.showAlert()
        }}><Text style={{fontSize: 14,color: '#333'}}>新增</Text></TouchableOpacity>
    })
componentDidMount () {
        this.props.navigation.setParams({showAlert: this.showAlert.bind(this),disable: false})
    }
goSecondPage() {
        this.props.navigation.setParams({disable: true})
        this.staggerAnimated.start(()=>{
            this.props.navigation.navigate('SecondPage')
            this.props.navigation.setParams({disable: false})
            this.staggerAnimated.reset()
        })
    }

結(jié)束語

該過渡動畫的實現(xiàn)也不是太過于復雜,希望這篇文章給你有幫助。寫了一個簡單的小Demo,希望可以幫助你理解。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容