You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Expo React Native中Pressable切换图片时显示延迟问题咨询

解决React Native条件渲染图片的位置切换延迟问题

问题描述

我是Expo和React Native新手,正在学习条件渲染,测试时发现条件渲染的图片存在显示延迟问题:当Pressable包裹的图片随状态变量变化同时切换源和屏幕位置时,它会先在原位置显示新图片,再根据top属性移动(还会显示整个位移过程)。希望图片位置调整完成后再整体显示,避免每次点击按钮切换图片出现这种延迟。

源代码

import { StatusBar } from 'expo-status-bar';
import { Pressable, StyleSheet, Text, TextInput, View, ScrollView, Image, Dimensions } from 'react-native';
import Constants from 'expo-constants';
import { Card } from 'react-native-paper';
import login from './login-screen.png';
import loginButton from './login-screen-button.png';
import loginButtonPressed from './loginButton2.png';
import ID_selected from './ID_selected.png';
import {useState} from 'react';

//Iphone 12 pro max resolution: 1284 x 2778 pixels

export default function App() {
  const win = Dimensions.get('window');
  const ratio = win.width / 428;
  const [text, onChangeText] = useState();
  const [isShowingImageID, setShowingImageID] = useState(false);
  const [isShowingImagePW, setShowingImagePW] = useState(false);
  const [pressed, onPressFunction] = useState(false);
  console.log("Window.width: ", win.width);
  console.log("Windows.height: ", win.height);

  return (
    <View style={styles.container}>
      <Image
        source={login}
        style={{
          width: 428,
          height: 926,
        }}
      />
      <Pressable 
        onPressIn={() => onPressFunction(true)}
        onPressOut={() => onPressFunction(false)}
        style={{
          position: "absolute"
        }}
      >
        {
          pressed ? 
          (
            <Image
              source={loginButtonPressed}
              style={{
                width: 428,
                height: 926,
                top: 50,
                position: "relative"
              }}
            />
          ) :
          (
            <Image
              source={loginButton}
              style={{
                width: 428,
                height: 926,
                position: "relative"
              }}
            />
          )
        }
      </Pressable>
      
      {
        isShowingImageID ?
        (
          <Image
            source={ID_selected}
            style={{
              width: 1184 / 3, 
              height: 140 / 3, 
              position: "absolute",
              top: 295
            }}
          />
         ) : (null)
      }
      {
        isShowingImagePW ?
        (
          <Image
            source={ID_selected}
            style={{
              width: 1184 / 3, 
              height: 140 / 3, 
              position: "absolute",
              top: 338
            }}
          />
         ) : (null)
      }
      <TextInput
        style={textInputStyleID.container}
        onChangeText={text => onChangeText(text)}
        onPressIn={touchEvt => setShowingImageID(true)}
        onPressOut={touchEvt => setShowingImageID(false)}
        placeholder="아이디"
        placeholderTextColor="#A9A9A9"
        keyboardType="email-address"
      />
      <TextInput
        style={textInputStylePW.container}
        onChangeText={text => onChangeText(text)}
        onPressIn={touchEvt => setShowingImagePW(true)}
        onPressOut={touchEvt => setShowingImagePW(false)}
        placeholder="비밀번호"
        placeholderTextColor="#A9A9A9"
        keyboardType="default"
        secureTextEntry={true}
      />
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
    position: 'absolute'
  },
});
const textInputStyleID = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
    position: 'absolute',
    top: 303,
    left: 42,
    width: 350,
    height: 30
  }
});
const textInputStylePW = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
    position: 'absolute',
    top: 346,
    left: 42,
    width: 350,
    height: 30
  }
});

解决方案

1. 统一定位逻辑,避免组件重排延迟

当前Pressable用absolute定位,内部图片用relative定位,切换时修改图片top会触发重排。将定位逻辑移到Pressable上,内部图片保持静态尺寸:

<Pressable 
  onPressIn={() => onPressFunction(true)}
  onPressOut={() => onPressFunction(false)}
  style={[{
    position: "absolute",
    top: pressed ? 50 : 0,
  }]}
>
  <Image
    source={pressed ? loginButtonPressed : loginButton}
    style={{
      width: 428,
      height: 926,
      position: "absolute"
    }}
  />
</Pressable>

状态切换时仅修改Pressable的top值,图片始终填满容器,不会出现先显示再移动的问题。

2. 预加载图片,消除加载延迟

图片首次加载的时间也会导致延迟,在组件挂载时预加载所有需要切换的图片:

import { useState, useEffect } from 'react';

export default function App() {
  // ...其他状态定义

  useEffect(() => {
    Image.prefetch(loginButtonPressed);
    Image.prefetch(ID_selected);
  }, []);

  // ...其余代码
}

3. 优化条件渲染,避免组件重复卸载/挂载

当前ID和PW的图片通过条件渲染挂载/卸载,会导致组件重建。改用opacity控制显示,保持组件始终存在:

<Image
  source={ID_selected}
  style={{
    width: 1184 / 3, 
    height: 140 / 3, 
    position: "absolute",
    top: 295,
    opacity: isShowingImageID ? 1 : 0,
    pointerEvents: isShowingImageID ? 'auto' : 'none'
  }}
/>
<Image
  source={ID_selected}
  style={{
    width: 1184 / 3, 
    height: 140 / 3, 
    position: "absolute",
    top: 338,
    opacity: isShowingImagePW ? 1 : 0,
    pointerEvents: isShowingImagePW ? 'auto' : 'none'
  }}
/>

4. 用Animated库实现精准过渡(可选)

如果需要平滑动画而非瞬间切换,使用React Native的Animated库控制过渡:

import { Animated } from 'react-native';

export default function App() {
  const pressedAnim = new Animated.Value(0);

  const onPressIn = () => {
    Animated.timing(pressedAnim, {
      toValue: 1,
      duration: 0,
      useNativeDriver: false,
    }).start();
  };

  const onPressOut = () => {
    Animated.timing(pressedAnim, {
      toValue: 0,
      duration: 0,
      useNativeDriver: false,
    }).start();
  };

  const topValue = pressedAnim.interpolate({
    inputRange: [0, 1],
    outputRange: [0, 50],
  });

  // ...其余代码

  return (
    <View style={styles.container}>
      {/* 其他组件 */}
      <Animated.View 
        style={[{
          position: "absolute",
          top: topValue,
        }]}
      >
        <Pressable onPressIn={onPressIn} onPressOut={onPressOut}>
          <Image
            source={pressedAnim._value === 1 ? loginButtonPressed : loginButton}
            style={{ width: 428, height: 926 }}
          />
        </Pressable>
      </Animated.View>
      {/* 其他组件 */}
    </View>
  );
}

内容的提问来源于stack exchange,提问作者rasputin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 11:09:17