如何让react-confetti在Next.js+MUI表单的当前视口生效?
问题描述
我开发了一个基于Next.js和MUI的类Typeform表单应用,每个问题页面占满整个视口。完成最后一题时,我希望渲染react-confetti特效,但目前特效显示在页面起始位置,而非当前的最后一题区域。
页面截图
- 欢迎页:

- 最后一题页面:

问题现象
特效显示在第一题区域而非最后一题:
安装依赖命令
yarn add @mui react-use react-confetti
我的index.tsx代码
import Button from '@mui/material/Button' import Grid from '@mui/material/Grid' import TextField from '@mui/material/TextField' import Typography from '@mui/material/Typography' import type { NextPage } from 'next' import dynamic from 'next/dynamic' import { useState } from 'react' import useWindowSize from 'react-use/lib/useWindowSize' const Confetti = dynamic(() => import('react-confetti'), { ssr: false, }) const Home: NextPage = () => { const { width, height } = useWindowSize() const [run, setRun] = useState(false) return ( <div> <Grid id="welcome" container spacing={0} direction="column" alignItems="center" justifyContent="center" style={{ minHeight: '100vh', textAlign: 'center' }} > <Grid item xs={3}> <Typography variant="h6" gutterBottom> Hello User! </Typography> <Typography variant="body2" gutterBottom> Tell more about your experience with us </Typography> <Button variant="contained" color="primary" sx={{ marginBottom: '1em' }} size="large"> Start </Button> <Typography variant="caption" display="block" gutterBottom> It takes less than 1 minute </Typography> </Grid> </Grid> {run && ( <Confetti height={height} width={width} recycle={false} numberOfPieces={1000} onConfettiComplete={() => setRun(false)} /> )} <Grid id="suggestion" container spacing={0} direction="column" alignItems="center" justifyContent="center" style={{ minHeight: '100vh', textAlign: 'center' }} > <Grid item xs={3}> <Typography variant="h5" gutterBottom> Any suggestions to improve our space? </Typography> <TextField variant="standard" size="medium" label="Leave your suggestion" fullWidth color="primary" margin="dense" /> <Button variant="contained" color="primary" sx={{ marginTop: '20px' }} size="large" disabled={run} onClick={() => setRun(true)} > Finish </Button> </Grid> </Grid> </div> ) } export default Home
解决方案
问题根源是react-confetti默认采用固定定位(position: fixed),会相对于浏览器视口顶部渲染。你的页面是多屏滚动结构,用户滚动到最后一题时,视口对应的页面顶部是第一题位置,所以特效会显示在最上方。
下面两种方法都能解决问题,选其一即可:
方法一:将Confetti嵌套到最后一题容器内
把Confetti组件放到最后一题的Grid容器里,给容器添加position: relative,再给Confetti设置position: absolute,让它相对于最后一题的视口区域渲染:
修改后的关键代码:
<Grid id="suggestion" container spacing={0} direction="column" alignItems="center" justifyContent="center" style={{ minHeight: '100vh', textAlign: 'center', position: 'relative' }} // 新增相对定位 > <Grid item xs={3}> {/* 原有表单内容不变 */} </Grid> {run && ( <Confetti height={height} width={width} recycle={false} numberOfPieces={1000} onConfettiComplete={() => setRun(false)} style={{ position: 'absolute', top: 0, left: 0 }} // 新增绝对定位 /> )} </Grid>
方法二:根据滚动位置动态调整Confetti的top值
用react-use的useScroll钩子获取当前滚动距离,给Confetti设置对应的top偏移,让它跟着当前视口走:
首先导入钩子:
import useScroll from 'react-use/lib/useScroll'
然后在组件中使用:
const { y: scrollY } = useScroll()
修改Confetti的渲染代码:
{run && ( <Confetti height={height} width={width} recycle={false} numberOfPieces={1000} onConfettiComplete={() => setRun(false)} style={{ top: scrollY }} // 同步滚动偏移 /> )}
这样Confetti就会显示在当前用户看到的最后一题区域了。
内容的提问来源于stack exchange,提问作者Natan Deitch
相关产品推荐
相关产品推荐

