【问题标题】:How to do shared element transitions in React Native Web?如何在 React Native Web 中进行共享元素转换?
【发布时间】:2021-11-28 14:55:33
【问题描述】:

我正在使用react-native-web 开发一个网络和移动应用程序。我已经到了想要在屏幕之间引入更好的过渡效果的地步,因此我想做如下共享元素过渡:

因为我的路由从@react-navigation/stack 开始,所以我想使用一个支持我所拥有的相同结构的库。我花了很长时间才让react-navigation-shared-element 库工作,但它终于编译了。不幸的是,动画看起来不像我想要的那样:

如您所见,实际上没有动画。它只是切换页面。我已经在这个问题上花了数周时间尝试不同的配置、库以及通常所有的东西,我几乎已经放弃并开始寻找纯网络库来将代码分成两个不同的版本。

因此,我恳请帮助以使代码与一个库一起使用,或者,如果您认为有更好的解决方案,我可以使用仅网络和仅移动库的替代选择来保持大致相同的代码库(例如,通过将库组件包装到我可以在代码中使用的东西中)。

这是我当前动画的代码:

import React from 'react';
import {NavigationContainer} from '@react-navigation/native';
import {createSharedElementStackNavigator, SharedElement} from 'react-navigation-shared-element';
import {View, TouchableOpacity, Text} from 'react-native';

const Stack = createSharedElementStackNavigator();

function A({navigation}) {
  return (
    <View>
      <TouchableOpacity onPress={() => navigation.navigate('B')}>GO</TouchableOpacity>
      <SharedElement id={'shared'}>
        <Text style={{position: 'absolute', top: 50, background: 'yellow'}}>Start screen</Text>
      </SharedElement>
    </View>
  );
}

function B({navigation}) {
  return (
    <View>
      <TouchableOpacity onPress={() => navigation.navigate('A')}>GO</TouchableOpacity>
      <SharedElement id={'shared'}>
        <Text style={{position: 'absolute', top: 100, background: 'yellow'}}>Start screen</Text>
      </SharedElement>
    </View>
  );
}

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name={'A'} component={A} />
        <Stack.Screen
          name={'B'}
          component={B}
          sharedElements={(route, otherRoute, showing) => {
            return [
              {
                id: 'shared',
                animation: 'move',
              },
            ];
          }}
        />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

以下是我正在使用的相关库:

"@react-navigation/native": "^6.0.2",
"@react-navigation/stack": "^6.0.7",
"react-native-shared-element": "^0.8.2",
"react-navigation-shared-element": "^3.1.3"

最后,这是我的webpack 配置,我添加它只是为了支持共享元素库,所以我不知道它好不好。我对 React 很陌生,所以我不知道是否需要加载器或预设之类的东西来支持缺少的共享元素动画。

const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const Dotenv = require('dotenv-webpack');

const appDirectory = path.resolve(__dirname);

const babelLoaderConfiguration = {
  test: /\.js$/,
  include: [
    path.resolve(appDirectory, 'src'),
  ],
  use: {
    loader: 'babel-loader',
    options: {
      presets: ['module:metro-react-native-babel-preset'],
      plugins: ['react-native-web'],
    },
  },
};

const imageLoaderConfiguration = {
  test: /\.(gif|jpe?g|png|svg)$/,
  use: {
    loader: 'url-loader',
    options: {
      name: '[name].[ext]',
      esModule: false,
    },
  },
};

module.exports = {
  entry: path.resolve(appDirectory, 'src', 'index.js'),
  output: {
    filename: 'bundle.web.js',
    path: path.resolve(appDirectory, 'build'),
  },
  devtool: 'source-map',
  module: {
    rules: [babelLoaderConfiguration, imageLoaderConfiguration],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(appDirectory, 'public', 'index.html'),
      filename: 'index.html',
    }),
    new Dotenv({
      path: path.resolve(appDirectory, '.env'),
      safe: true,
    }),
  ],
  resolve: {
    alias: {
      'react-native$': 'react-native-web',
    },
    extensions: ['.web.js', '.js'],
  },
};

【问题讨论】:

    标签: react-native webpack react-navigation shared-element-transition react-native-shared-element


    【解决方案1】:

    尝试在一个组件中使用带有 useState 的负边距:

    实时示例 - https://snack.expo.dev/8ZCuner6B

    import React, { Component } from 'react';
    import { Alert, Button, Text, TouchableOpacity, TextInput, View, StyleSheet } from 'react-native';
    
    
     const App = () => {
    
       const [animationStyle , setAnimationStyle] = React.useState({
        width: 200,
        fontFamily: 'Baskerville',
        fontSize: 20,
        height: 44,
        padding: 10,
        borderWidth: 1,
        borderColor: 'white',
        marginTop:30,
        marginVertical: 10,
        zIndex:2,
      })
    
      const frogotPasswordHandler = () => {
      let margin = 30;
      let step = 3.5
      const timerId = setInterval(() =>{
        if (margin > -50){
          margin = margin - step
          updateMarginStyle(margin);
        }
        if (margin < -50) {
          console.log("cleared")
          clearInterval(timerId)
        }
      } , 2);
    
      }
    
      const updateMarginStyle = (num) =>{
        setAnimationStyle({
        ...animationStyle,
        marginTop:num,
      })
      }
    
        return (
          <View style={styles.container}>
          <Text style={styles.titleText}>Hi, Welcome To</Text>
            <Text style={styles.titleText}>Momento</Text>
            <View    style={styles.textWrapper}>
            <TextInput
              keyboardType = 'email-address'
              onChangeText={(email) => this.setState({ email })}
              placeholder='email'
              placeholderTextColor = 'white'
              style={styles.input}
            />
            </View>
            <TextInput
              onChangeText={(password) => this.setState({ password })}
              placeholder={'password'}
              secureTextEntry={true}
              placeholderTextColor = 'white'
              style={animationStyle}
            />
    
            <TouchableOpacity
              style={styles.forgotButton}
              onPress={frogotPasswordHandler}
           >
            <Text style={styles.defaultText}>Forgot password</Text>
           </TouchableOpacity>
            
    
         
            <TouchableOpacity
              style={styles.button}
           >
             <Text style={styles.buttonText}> Sign Up / Login </Text>
           </TouchableOpacity>
            
          </View>
        );
      
    }
    
    
    export default App;
    
    
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
        backgroundColor: 'salmon',
      },
      textWrapper:{
        backgroundColor:'green',
            zIndex:10,
    
      },
      titleText:{
        fontFamily: 'Baskerville',
        fontSize: 50,
        alignItems: 'center',
        justifyContent: 'center',
      },
      button: {
        alignItems: 'center',
        backgroundColor: 'powderblue',
        width: 200,
        height: 44,
        padding: 10,
        borderWidth: 1,
        borderColor: 'white',
        borderRadius: 25,
        marginBottom: 10,
      },
      buttonText:{
        fontFamily: 'Baskerville',
        fontSize: 20,
        alignItems: 'center',
        justifyContent: 'center',
      },
      input: {
        width: 200,
        fontFamily: 'Baskerville',
        fontSize: 20,
        height: 44,
        padding: 10,
        marginTop:20,
        borderWidth: 1,
        borderColor: 'white',
        marginVertical: 10,
      },
      defaultText:{
      },
      forgotButton:{
        backgroundColor:'gray',
        marginBottom:20,
        marginTop:10,
      }
    });
    

    【讨论】:

    • 这是非常手动的,我必须为每个动画编写代码,我希望将其自动化。此外,将它与 react-navigation 连接起来并非易事,而且不会像这样工作(例如,useState 将无法工作)。
    • 我对react导航没有很匹配的经验,但是你可以使用自定义的hook来分享动画逻辑
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-06-25
    • 2021-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-06
    相关资源
    最近更新 更多