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

如何将MUI Tooltip定位在进度条的数值标记位置?

MUI进度条Tooltip动态定位解决方案

问题描述

开发MUI进度条组件时,想要让Tooltip跟随进度条的数值标记位置动态显示,但目前Tooltip始终固定在进度条中间位置,无法实现预期效果:

  • 预期效果:Tooltip显示在进度条当前数值对应的顶部位置
  • 当前效果:Tooltip固定在进度条容器的中间位置

问题根源

当前代码把Tooltip直接包裹了整个LinearProgress组件,Tooltip的定位基准是整个进度条容器,因此无论进度值如何变化,Tooltip都会基于容器居中显示,无法跟随进度位置。

解决方案

通过添加一个动态定位的触发元素作为Tooltip的绑定目标,让这个元素的位置随进度值变化,从而带动Tooltip同步移动:

  1. 给进度条外层容器设置相对定位,作为触发元素的定位参考
  2. 创建一个绝对定位的触发元素,通过进度值计算其left偏移量(left: ${value}%)
  3. 将Tooltip绑定到这个触发元素上,调整Tooltip的定位属性确保对齐正确

修改后的完整代码

import { useEffect, useState } from 'react'
import MuiLinearProgress, { LinearProgressProps } from '@mui/material/LinearProgress'
import { Box, Tooltip, Typography } from '@mui/material'
import { makeStyles } from '@mui/styles'

type OmitLinear = Omit<LinearProgressProps, 'variant' | 'color'>

interface LinearProps extends OmitLinear {
  variant?: 'circular' | 'linear' | undefined
  color?: string | undefined
  label?: 'end' | 'under' | 'tooltip' | 'arrowTooltip' | undefined
}

type Props = LinearProps

const useStyles = makeStyles((theme) => ({
  boxLinear: {
    position: 'relative',
    width: '100%',
  },
  boxMuiLinear: {
    position: 'relative',
  },
  rootLinearProgress: {
    height: 8,
    borderRadius: 4,
  },
  tooltipTrigger: {
    position: 'absolute',
    top: 0,
    left: 0,
    height: '100%',
    width: 2,
    transform: 'translateX(-50%)',
    backgroundColor: 'transparent',
  },
  labelBox: {
    marginTop: theme.spacing(1),
    textAlign: 'right',
  },
}))

export const Progress = (props: Props) => {
  const [progress, setProgress] = useState(0)
  const {
    variant = 'linear',
    value = progress,
    color,
    label
  } = props
  const classes = useStyles()

  useEffect(() => {
    const timer = setInterval(() => {
      setProgress((prevProgress) => 
        prevProgress >= 100 ? 0 : prevProgress + 1
      )
    }, 800)
    return () => clearInterval(timer)
  }, [])

  return (
    <>
      {variant === 'circular' ? (
        <div>环形进度条</div>
      ) : (
        <Box className={classes.boxLinear}>
          <Box className={classes.boxMuiLinear}>
            <MuiLinearProgress
              {...props}
              variant='determinate'
              color={undefined}
              value={value}
              className={classes.rootLinearProgress}
            />
            {(label === 'tooltip' || label === 'arrowTooltip') && (
              <Tooltip
                title={`${Math.round(value)}%`}
                open={true}
                placement="top"
                arrow={label === 'arrowTooltip'}
                PopperProps={{
                  modifiers: [
                    {
                      name: 'offset',
                      options: {
                        offset: [0, 8],
                      },
                    },
                  ],
                }}
              >
                <Box
                  className={classes.tooltipTrigger}
                  style={{ left: `${value}%` }}
                />
              </Tooltip>
            )}
          </Box>
          {(label === 'end' || label === 'under') && (
            <Box className={classes.labelBox}>
              <Typography variant="body2" color="text.secondary">
                {`${Math.round(value)}%`}
              </Typography>
            </Box>
          )}
        </Box>
      )}
    </>
  )
}

关键修改点说明

  • 新增tooltipTrigger样式:绝对定位,通过style={{ left: ${value}% }}动态计算位置,transform: 'translateX(-50%)'让触发点精准对齐进度的终点位置
  • 将Tooltip从包裹进度条改为绑定到触发元素,确保Tooltip的定位基准是进度对应的位置
  • 调整PopperProps的offset,让Tooltip和进度条之间保持合适间距,避免遮挡
  • 修正了原代码中useEffect, useState导入的逗号缺失问题,补充了useStyles的定义

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 22:47:57