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

iPhone端Chrome首次渲染React Modal时进入动画失效问题

iPhone端Chrome首次打开React Modal时进入动画失效的解决思路

问题描述

基于React开发的Modal组件,使用SCSS实现了进入/退出动画,在iPhone Safari、桌面Safari和Chrome中均正常,但iPhone端Chrome首次打开Modal时进入动画无法生效。尝试将scale改为rotate,问题依然存在。

相关代码

SCSS代码

@keyframes pop {
  0% {
    -webkit-transform: scale(0,0) translate(0,0);
    transform: scale(0,0) translate(0,0);
  }
  70% {
    -webkit-transform: scale(1.05,1.05) ;
    transform: scale(1.05,1.05);
  }
  100% {
    -webkit-transform: scale(1,1) ;
    transform: scale(1,1);
  }
}

&.showing {
  .modal {
    animation: pop 0.3s ease-in-out;
    animation-fill-mode: forwards;
    animation-direction: normal;
  }
}
&.shown {
  .modal {
    transform: scale(1,1);
  }
}
&.hidden {
  .modal {
    transform: scale(0,0);
  }
}
&.hiding {
  .modal {
    animation: pop 0.3s ease-in-out;
    animation-fill-mode: forwards;
    animation-direction: reverse;
  }
}

React组件代码

import React, { useEffect, useState } from 'react'
import classes from './popupModal.module.scss'
import classNames from 'classnames'

interface IProps {
  onClose?: () => void;
  smallCloseButton?: boolean;
}

const PopupModal = ({onClose, smallCloseButton}: IProps) => {
  const [currentState, setCurrentState] = useState('showing')
  
  useEffect(() => {
    setTimeout(() => {
      setCurrentState('shown')
    }, 300)
  }, [])

  const handleClose = () => {
    setCurrentState('hiding')
    setTimeout(() => {
      onClose?.()
    }, 300)
  }
  
  const closeBtnSize = smallCloseButton ? 's' : 'm'
  
  return (
    <div className={classNames(classes.modalWrapper, classes[currentState])} >
      <button
        onClick={handleClose}
      />
      <div className={classes.modal}>Modal内容</div>
    </div>
  )
}

export default PopupModal

解决思路

  • 强制触发浏览器重绘/回流
    iPhone端Chrome首次渲染时,可能因初始状态transform: scale(0,0)与动画第一帧完全一致,导致浏览器跳过动画。可以调整状态切换逻辑,先设置hidden状态,短暂延迟后再切换到showing,强制浏览器识别状态变化:

    useEffect(() => {
      setCurrentState('hidden')
      const timer = setTimeout(() => {
        setCurrentState('showing')
        setTimeout(() => setCurrentState('shown'), 300)
      }, 50)
      return () => clearTimeout(timer)
    }, [])
    
  • 优化动画初始状态与类切换逻辑
    避免初始状态和动画第一帧完全一致,给hidden状态的Modal设置微小差异,比如用scale(0.001, 0.001)替代scale(0,0),同时添加opacity过渡增强状态区分:

    &.hidden {
      .modal {
        transform: scale(0.001, 0.001);
        opacity: 0;
      }
    }
    &.showing {
      .modal {
        opacity: 1;
        animation: pop 0.3s ease-in-out forwards;
      }
    }
    
  • 改用CSS Transition替代关键帧动画
    关键帧动画在部分移动端浏览器可能存在兼容性问题,改用Transition实现更稳定:

    .modal {
      transition: transform 0.3s ease-in-out;
      transform: scale(0.001, 0.001);
    }
    &.showing .modal,
    &.shown .modal {
      transform: scale(1,1);
    }
    &.hiding .modal {
      transform: scale(0.001, 0.001);
    }
    

    调整React逻辑时,可直接切换状态,无需定时器等待动画结束。

  • 补全浏览器前缀
    确保添加-webkit-前缀,适配iPhone端Chrome的WebKit内核:

    &.showing {
      .modal {
        -webkit-animation: pop 0.3s ease-in-out;
        animation: pop 0.3s ease-in-out;
        -webkit-animation-fill-mode: forwards;
        animation-fill-mode: forwards;
      }
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 00:52:40