基于百分比控制的React文字填充效果实现问题
解决文字同步填充与单个字符颜色区分问题
问题分析
- 填充不同步:原代码中所有文本块共用同一个
percentage值,但每个文本块的宽度不同,导致各自的填充进度无法统一,整体看起来不是从左到右连贯填充。 - 单个字符颜色无法区分:未对第一行最后一个字母"S"做单独的颜色处理逻辑,无法实现填充时的颜色差异。
解决方案
- 统一整体填充进度:通过获取所有文本元素的总宽度,将百分比进度转换为实际像素宽度,再为每个文本块计算对应的填充百分比,确保整体从左到右连贯填充。
- 单独处理字符颜色:将第一行的"S"拆分为两层——底层保持基础描边,上层使用不同颜色的渐变填充,当进度覆盖到该字符时显示对应颜色。
完整代码实现
'use client' import React, { useState, useRef, useEffect } from 'react' const Intro = () => { const [percentage, setPercentage] = useState(0) const [totalWidth, setTotalWidth] = useState(0) const textRefs = useRef({ po: null, s: null, performance: null }) // 计算所有文本元素的总宽度 useEffect(() => { const calcTotalWidth = () => { const poWidth = textRefs.current.po?.offsetWidth || 0 const sWidth = textRefs.current.s?.offsetWidth || 0 const performanceWidth = textRefs.current.performance?.offsetWidth || 0 setTotalWidth(poWidth + sWidth + performanceWidth) } calcTotalWidth() window.addEventListener('resize', calcTotalWidth) return () => window.removeEventListener('resize', calcTotalWidth) }, []) // 为每个文本块计算对应的填充进度 const getElementFillPercentage = (elementRef) => { if (!elementRef || totalWidth === 0) return 0 const elementStart = elementRef.offsetLeft const elementEnd = elementStart + elementRef.offsetWidth const fillPixel = (percentage / 100) * totalWidth if (fillPixel <= elementStart) return 0 if (fillPixel >= elementEnd) return 100 return ((fillPixel - elementStart) / elementRef.offsetWidth) * 100 } // 生成基础填充渐变 const getBaseGradient = (fillPercent) => { return `linear-gradient(to right, white ${fillPercent}%, transparent ${fillPercent}%)` } // 生成"S"的特殊颜色渐变 const getSGradient = (fillPercent) => { return `linear-gradient(to right, #4299e1 ${fillPercent}%, transparent ${fillPercent}%)` } const poFill = getElementFillPercentage(textRefs.current.po) const sFill = getElementFillPercentage(textRefs.current.s) const performanceFill = getElementFillPercentage(textRefs.current.performance) return ( <div className="w-full h-[100vh] flex items-center justify-center"> <div className="flex flex-col relative"> <div ref={(el) => textRefs.current.po = el} className="inline-block"> <span className="text-transparent italic text-[148px] text-stroke" style={{ lineHeight: 0.8, backgroundImage: getBaseGradient(poFill), WebkitBackgroundClip: 'text', backgroundClip: 'text', }} > PO </span> </div> <div className="inline-block relative" ref={(el) => textRefs.current.s = el}> {/* 底层保持描边,上层用蓝色填充 */} <span className="text-transparent italic pr-2 text-[148px] text-stroke"> S </span> <span className="text-transparent italic pr-2 text-[148px] absolute inset-0" style={{ lineHeight: 0.8, backgroundImage: getSGradient(sFill), WebkitBackgroundClip: 'text', backgroundClip: 'text', }} > S </span> </div> <span ref={(el) => textRefs.current.performance = el} className="text-transparent text-[30px] italic text-stroke-less" style={{ lineHeight: 1, letterSpacing: 5.7, marginLeft: 6, backgroundImage: getBaseGradient(performanceFill), WebkitBackgroundClip: 'text', backgroundClip: 'text', }} > PERFORMANCE </span> </div> <input type="range" min="0" max="100" value={percentage} onChange={(e) => setPercentage(e.target.value)} className="mt-4" /> </div> ) } export default Intro
关键说明
- 总宽度计算:通过
useRef获取每个文本元素的宽度,结合resize事件确保窗口大小变化时仍能准确计算总宽度。 - 单个元素填充进度:根据当前百分比对应的像素填充位置,计算每个元素的填充比例,保证整体填充连贯同步。
- 字符颜色区分:将"S"设置为双层结构,底层是默认描边样式,上层使用不同颜色的渐变填充,实现该字符填充时的颜色差异。
内容的提问来源于stack exchange,提问作者Nilton Schumacher F
相关产品推荐
相关产品推荐

