如何沿SVG路径将弯曲文本按路径上下区域分两种颜色?
实现沿动态路径弯曲文本的上下分色效果
核心思路是通过复制两份沿路径排列的文本,结合动态生成的裁剪区域,分别显示文本的上下部分并设置不同颜色,同时同步响应路径的实时变化:
完整实现代码
<svg> <!-- 基础路径,文本将沿此路径排列 --> <path id="Path" d="M 100 100 q 50 -100 230 0" stroke="#000" stroke-width="1" fill="none"></path> <!-- 上半部分文本的裁剪区域 --> <clipPath id="clipTop"> <path id="clipPathTop" fill="white" /> </clipPath> <!-- 下半部分文本的裁剪区域 --> <clipPath id="clipBottom"> <path id="clipPathBottom" fill="white" /> </clipPath> <!-- 上半部分文本(橙色) --> <text fill="#ff4500" font-size="64px" clip-path="url(#clipTop)"> <textPath id="TextPathTop" alignment-baseline="middle" href="#Path">Example</textPath> </text> <!-- 下半部分文本(蓝色) --> <text fill="#1e90ff" font-size="64px" clip-path="url(#clipBottom)"> <textPath id="TextPathBottom" alignment-baseline="middle" href="#Path">Example</textPath> </text> </svg>
svg { width: 500px; height: 500px; }
const path = document.getElementById("Path"); const textPathTop = document.getElementById("TextPathTop"); const textPathBottom = document.getElementById("TextPathBottom"); const clipPathTop = document.getElementById("clipPathTop"); const clipPathBottom = document.getElementById("clipPathBottom"); const fontSize = 64; const halfFontSize = fontSize / 2; // 生成偏移路径的工具函数:基于原路径生成上下偏移的闭合裁剪区域 function createOffsetClipPath(originalPath, offset) { const pathLength = originalPath.getTotalLength(); const offsetPoints = []; const originalReversePoints = []; // 生成偏移后的路径点(沿路径切线垂直方向偏移) for (let i = 0; i <= pathLength; i += 2) { const currPoint = originalPath.getPointAtLength(i); const nextPoint = originalPath.getPointAtLength(Math.min(i + 1, pathLength)); const dx = nextPoint.x - currPoint.x; const dy = nextPoint.y - currPoint.y; const tangentLength = Math.sqrt(dx * dx + dy * dy) || 1; // 计算垂直切线的单位向量(向上偏移取(dy, -dx),向下取(-dy, dx)) const perpX = dy / tangentLength; const perpY = -dx / tangentLength; offsetPoints.push([ currPoint.x + perpX * offset, currPoint.y + perpY * offset ]); } // 生成原路径的反向点,用于闭合裁剪区域 for (let i = pathLength; i >= 0; i -= 2) { const p = originalPath.getPointAtLength(i); originalReversePoints.push([p.x, p.y]); } // 构建闭合路径的d属性 let d = `M ${offsetPoints[0][0]} ${offsetPoints[0][1]}`; offsetPoints.slice(1).forEach(([x, y]) => d += ` L ${x} ${y}`); originalReversePoints.forEach(([x, y]) => d += ` L ${x} ${y}`); d += " Z"; return d; } // 鼠标移动时同步更新所有元素 document.onmousemove = function(e) { // 更新基础路径 path.setAttribute("d", `M 100 100 q ${e.x - 100} ${e.y - 100} 230 0`); const pathLength = path.getTotalLength(); // 同步文本长度,保证文本完全适配路径 textPathTop.setAttribute("textLength", `${pathLength}px`); textPathBottom.setAttribute("textLength", `${pathLength}px`); // 更新上下裁剪区域路径 clipPathTop.setAttribute("d", createOffsetClipPath(path, halfFontSize)); clipPathBottom.setAttribute("d", createOffsetClipPath(path, -halfFontSize)); };
关键说明
- 两份
<textPath>完全同步,通过clip-path属性分别只显示文本的上半/下半区域 createOffsetClipPath函数通过计算路径切线的垂直方向,生成贴合文本弯曲形态的闭合裁剪路径- 鼠标移动时,同步更新基础路径、文本长度和裁剪路径,确保所有元素实时响应路径变化
内容的提问来源于stack exchange,提问作者Karo
相关产品推荐
相关产品推荐

