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

如何实现适配所有字体的描边文字渐变填充擦除效果

渐进式填充描边文字React组件的适配与精准进度问题

需求与现有实现

需要创建一个React组件,渲染带有基于props控制的渐进式填充颜色的描边文字,且能适配任意字体。由于部分字体存在-webkit-text-stroke的渲染bug,采用双层span叠加方案:外层span负责文字描边,内层span通过背景渐变实现渐进式颜色填充。

现有组件代码:

type StrokeFillWipeTextProps = {
  text: string;
  color: string;
  wipeColor: string;
  progress: number;
  className: string;
};

export const StrokeFillWipeText = ({
  text,
  color,
  wipeColor,
  progress,
  className,
}: StrokeFillWipeTextProps) => {
  const wrapperStyle: React.CSSProperties = {
    position: 'relative',
    WebkitTextStroke: '10px black',
  };

  const innerStyle: React.CSSProperties = {
    position: 'absolute',
    left: 0,
    pointerEvents: 'none',
    backgroundClip: 'text',
    backgroundSize: '200% 100%',
    transition: 'background-position',
    WebkitTextStroke: 0,
    WebkitBackgroundClip: 'text',
    WebkitTextFillColor: 'transparent',
    whiteSpace: 'pre',
    color: color,
    backgroundPositionX: `${100 - progress}%`,
    backgroundImage: `linear-gradient(to right, ${wipeColor}, ${wipeColor} 50%, ${color} 50%)`,
  };

  return (
    <div className={className}>
      <span style={wrapperStyle}>
        {text}
        <span style={innerStyle} aria-hidden>
          {text}
        </span>
      </span>
    </div>
  );
};

预期效果

  • progress=0:文字完全显示为color
  • progress=50:文字前半部分为wipeColor,后半部分为color
  • progress=100:文字完全显示为wipeColor

渲染效果示例:
渲染效果截图

现存问题

在部分字体、字符或字号下,首尾字符出现裁剪,文字实际溢出外层span容器:
文字溢出截图1
文字溢出截图2

核心诉求

  1. 让组件适配所有字体、字符和字号,避免文字裁剪/溢出
  2. 确保progress属性精准生效:0对应无填充擦除,100对应完全填充擦除,中间值准确匹配文字填充比例

解决方案

1. 解决文字裁剪/溢出问题

问题根源:-webkit-text-stroke会让文字向外扩展渲染,但外层span的固有尺寸仅计算文字本身,导致描边部分超出容器,内层绝对定位的span基于容器定位,进而裁剪首尾字符。

解决措施:

  • 外层span设置display: inline-block,让容器自适应包含文字+描边的完整宽度
  • 给外层span添加左右padding,值等于描边宽度,抵消描边的外扩部分
  • 内层span同步外层padding,确保文字和描边完全对齐

2. 确保progress精准生效

现有方案的渐变背景逻辑存在冗余,调整后可提升精准度:

  • 直接使用progress值作为渐变的分界点,无需200%的背景尺寸
  • 移除不必要的transition(如需动画可明确指定时长)
  • 确保内外层文字的排版属性(如white-space)完全一致

修正后的组件代码

type StrokeFillWipeTextProps = {
  text: string;
  color: string;
  wipeColor: string;
  progress: number;
  className: string;
  strokeWidth?: string;
  strokeColor?: string;
};

export const StrokeFillWipeText = ({
  text,
  color,
  wipeColor,
  progress,
  className,
  strokeWidth = '10px',
  strokeColor = 'black',
}: StrokeFillWipeTextProps) => {
  const strokeValue = parseInt(strokeWidth, 10);
  
  const wrapperStyle: React.CSSProperties = {
    position: 'relative',
    display: 'inline-block',
    padding: `0 ${strokeValue}px`,
    WebkitTextStroke: strokeWidth,
    color: strokeColor,
    verticalAlign: 'baseline',
  };

  const innerStyle: React.CSSProperties = {
    position: 'absolute',
    left: 0,
    top: 0,
    pointerEvents: 'none',
    padding: `0 ${strokeValue}px`,
    backgroundClip: 'text',
    WebkitBackgroundClip: 'text',
    WebkitTextFillColor: 'transparent',
    whiteSpace: 'pre',
    verticalAlign: 'baseline',
    backgroundImage: `linear-gradient(to right, ${wipeColor} ${progress}%, ${color} ${progress}%)`,
  };

  return (
    <div className={className}>
      <span style={wrapperStyle} aria-label={text}>
        {text}
        <span style={innerStyle} aria-hidden>
          {text}
        </span>
      </span>
    </div>
  );
};

额外优化说明

  • 新增strokeWidth和strokeColor props,提升组件通用性
  • 添加aria-label确保无障碍访问
  • 设置verticalAlign: baseline解决不同字体的基线对齐问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 10:35:01