react-native-image-picker自定义过渡动画:使用react-native-reanimated 3实现

【免费下载链接】react-native-image-picker :sunrise_over_mountains: A React Native module that allows you to use native UI to select media from the device library or directly from the camera. 【免费下载链接】react-native-image-picker 项目地址: https://gitcode.com/gh_mirrors/reac/react-native-image-picker

你还在为React Native应用中的图片选择交互生硬而烦恼吗?本文将展示如何通过react-native-reanimated 3为react-native-image-picker实现流畅的过渡动画,解决原生模块调用时界面切换突兀的问题。读完本文你将掌握:动画状态管理、原生模块与JS桥接通信、复杂动画曲线实现。

核心实现思路

react-native-image-picker的原生模块调用会导致界面无过渡切换,需通过以下方案解决:

  1. 状态拦截:通过包装launchCameralaunchImageLibrary方法,在调用原生模块前后插入动画控制
  2. 动画驱动:使用react-native-reanimated 3创建透明度、缩放、位移组合动画
  3. 通信桥接:通过Promise链式调用确保动画与原生模块生命周期同步

核心原理流程图: mermaid

实现步骤

1. 安装依赖

npm install react-native-reanimated@3.x react-native-gesture-handler

2. 创建动画包装组件

// src/components/AnimatedImagePicker.tsx
import React, { useRef } from 'react';
import { View, StyleSheet } from 'react-native';
import Animated, { 
  useAnimatedStyle, 
  withTiming, 
  Easing,
  runOnJS 
} from 'react-native-reanimated';
import { launchCamera, launchImageLibrary } from 'react-native-image-picker';
import type { CameraOptions, ImageLibraryOptions, ImagePickerResponse } from '../types';

// 动画配置常量
const ANIMATION_CONFIG = {
  duration: 350,
  easing: Easing.out(Easing.back(1.2)),
};

export const AnimatedImagePicker = () => {
  const animationState = useRef({
    isAnimating: false,
    progress: 0,
  }).current;

  // 退场动画样式 (原生模块启动前)
  const exitAnimationStyle = useAnimatedStyle(() => ({
    opacity: withTiming(animationState.progress, ANIMATION_CONFIG),
    transform: [
      { scale: withTiming(animationState.progress * 0.9 + 0.1, ANIMATION_CONFIG) },
    ],
  }));

  // 入场动画样式 (原生模块返回后)
  const enterAnimationStyle = useAnimatedStyle(() => ({
    opacity: withTiming(animationState.progress, ANIMATION_CONFIG),
    transform: [
      { translateY: withTiming((1 - animationState.progress) * 50, ANIMATION_CONFIG) },
    ],
  }));

  // 包装相机启动方法
  const animatedLaunchCamera = async (
    options: CameraOptions
  ): Promise<ImagePickerResponse> => {
    if (animationState.isAnimating) return { didCancel: true };
    
    animationState.isAnimating = true;
    // 执行退场动画
    animationState.progress = 0;
    
    return new Promise(resolve => {
      // 等待动画完成后调用原生模块
      setTimeout(async () => {
        const result = await launchCamera(options);
        
        // 执行入场动画
        animationState.progress = 1;
        setTimeout(() => {
          animationState.isAnimating = false;
          runOnJS(resolve)(result);
        }, ANIMATION_CONFIG.duration);
      }, ANIMATION_CONFIG.duration);
    });
  };

  return (
    <Animated.View style={[styles.container, animationState.progress < 0.5 ? exitAnimationStyle : enterAnimationStyle]}>
      {/* 你的相机/相册触发按钮 */}
    </Animated.View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
  },
});

关键代码解析

动画状态管理

通过animationState对象维护动画生命周期:

const animationState = useRef({
  isAnimating: false, // 防止并发动画
  progress: 0, // 0-1动画进度值
}).current;

方法拦截实现

src/index.ts中,原方法直接调用平台相关实现:

export function launchCamera(options: CameraOptions, callback?: Callback) {
  return Platform.OS === 'web'
    ? webCamera(options, callback)
    : nativeCamera(options, callback);
}

我们通过高阶函数模式包装原生调用:

// 高阶动画包装函数
const withAnimation = <T extends (...args: any[]) => Promise<any>>(
  fn: T,
  animationType: 'enter' | 'exit' = 'exit'
): T => {
  return ((...args: Parameters<T>) => {
    // 动画逻辑实现...
    return fn(...args);
  }) as T;
};

// 使用方式
const animatedLaunchImageLibrary = withAnimation(launchImageLibrary);

动画曲线优化

利用react-native-reanimated 3的Easing函数创建自然动画曲线:

// 弹性退场效果
Easing.out(Easing.back(1.2)) // 回弹效果
// 阻尼入场效果
Easing.bezier(0.25, 0.1, 0.25, 1.0) // 平滑减速

完整集成示例

以下是包含图片选择和预览的完整页面组件,结合了本文实现的动画效果:

// src/screens/ImageCaptureScreen.tsx
import React, { useState } from 'react';
import { View, Button, Image, StyleSheet } from 'react-native';
import { AnimatedImagePicker } from '../components/AnimatedImagePicker';
import type { Asset } from '../types';

export const ImageCaptureScreen = () => {
  const [selectedAsset, setSelectedAsset] = useState<Asset | null>(null);
  const { animatedLaunchCamera, animatedLaunchImageLibrary } = AnimatedImagePicker();

  const handleCameraPress = async () => {
    const result = await animatedLaunchCamera({
      mediaType: 'photo',
      quality: 0.8,
      presentationStyle: 'pageSheet', // 配合iOS 13+的模态样式
    });
    
    if (!result.didCancel && result.assets?.[0]) {
      setSelectedAsset(result.assets[0]);
    }
  };

  return (
    <View style={styles.screen}>
      {selectedAsset && (
        <Animated.Image 
          source={{ uri: selectedAsset.uri }} 
          style={styles.previewImage}
        />
      )}
      
      <View style={styles.buttonsContainer}>
        <Button title="拍摄照片" onPress={handleCameraPress} />
        <Button 
          title="从相册选择" 
          onPress={() => animatedLaunchImageLibrary({ mediaType: 'photo' })} 
        />
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  screen: {
    flex: 1,
    padding: 20,
  },
  previewImage: {
    width: '100%',
    height: 300,
    borderRadius: 12,
    marginBottom: 20,
  },
  buttonsContainer: {
    flexDirection: 'row',
    gap: 10,
  },
});

性能优化与注意事项

  1. 动画中断处理:在AnimatedImagePicker组件中添加手势取消逻辑,防止动画过程中用户重复操作

  2. 内存管理:对于频繁调用的场景,使用useCallback缓存动画函数:

const animatedLaunchCamera = useCallback(async (options) => {
  // 实现逻辑
}, []);
  1. 平台适配:根据src/platforms/native.ts中的平台差异,调整动画参数:
const PLATFORM_ANIMATION_CONFIG = Platform.select({
  ios: { duration: 300, easing: Easing.out(Easing.back(1.1)) },
  android: { duration: 250, easing: Easing.bezier(0.34, 1.56, 0.64, 1) },
});
  1. 错误边界:包装原生模块调用防止动画状态异常:
try {
  const result = await launchCamera(options);
} catch (error) {
  // 重置动画状态
  animationState.isAnimating = false;
  animationState.progress = 1;
}

总结

通过本文方法,我们成功为react-native-image-picker添加了自定义过渡动画,主要关键点:

  • 使用状态拦截模式包装原生方法,实现动画与原生模块生命周期同步
  • 结合多种动画参数(透明度、缩放、位移)创建丰富视觉效果
  • 通过Easing函数和动画曲线优化提升交互质感

完整实现代码可参考项目example/src/components/DemoButton.tsx中的交互逻辑,建议配合react-native-reanimated的useAnimatedScrollHandler实现更复杂的滚动关联动画。

掌握这些技巧后,你可以进一步扩展实现:图片选择后的裁剪动画、多图选择的瀑布流加载动画、视频录制的进度动画等高级交互效果。

【免费下载链接】react-native-image-picker :sunrise_over_mountains: A React Native module that allows you to use native UI to select media from the device library or directly from the camera. 【免费下载链接】react-native-image-picker 项目地址: https://gitcode.com/gh_mirrors/reac/react-native-image-picker

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐