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

基于React与SVG的动态文本自动换行问题求助(使用mechanic.design框架)

Solution for Dynamic Text Wrapping in SVG with React

Since SVG doesn’t have native support for automatic text wrapping, we can build a React-specific workaround to split dynamic text into lines that fit within your SVG’s bounds. Here’s a practical implementation tailored to your code:

Step 1: Build a Text Wrapping Utility Function

First, we’ll create a function that measures text width and splits it into lines that fit your desired maximum width. We’ll use a hidden SVG <text> element to get accurate dimension measurements (this matches your SVG’s font styling better than canvas):

const wrapText = (text, maxWidth, fontSize, fontFamily, fontWeight) => {
  const lines = [];
  if (!text) return lines;
  
  const words = text.split(' ');
  let currentLine = words[0];

  // Create a hidden text element to measure width
  const tempText = document.createElementNS('http://www.w3.org/2000/svg', 'text');
  tempText.style.visibility = 'hidden';
  tempText.style.fontSize = `${fontSize}px`;
  tempText.style.fontFamily = fontFamily;
  tempText.style.fontWeight = fontWeight;
  document.body.appendChild(tempText);

  for (let i = 1; i < words.length; i++) {
    const word = words[i];
    tempText.textContent = `${currentLine} ${word}`;
    const lineWidth = tempText.getBBox().width;

    if (lineWidth <= maxWidth) {
      currentLine += ` ${word}`;
    } else {
      lines.push(currentLine);
      currentLine = word;
    }
  }
  lines.push(currentLine);

  document.body.removeChild(tempText);
  return lines;
};

Step 2: Integrate the Wrapper into Your Component

Modify your handler component to use this function, rendering each wrapped line as a <tspan> inside your existing <text> elements. We’ll use useMemo to avoid recalculating lines unnecessarily:

import React, { useEffect, useMemo } from "react";
import ImgBanner from './assets/banner.svg';
import "./styles.css";

// Add the wrapText function here

export const handler = ({ inputs, mechanic }) => {
  const { width, height, schriftfarbe, subtitel, titel, titel2, untertitel, href } = inputs;

  // Calculate max width for text (subtract 50px for left/right padding)
  const textMaxWidth = width - 50;

  // Wrap dynamic text lines
  const wrappedSubtitel = useMemo(() => 
    wrapText(subtitel, textMaxWidth, 30, 'Garnett Regular', 'regular'),
    [subtitel, textMaxWidth]
  );
  const wrappedTitel = useMemo(() => 
    wrapText(titel, textMaxWidth, 72, 'Garnett Regular', 'regular'),
    [titel, textMaxWidth]
  );
  const wrappedTitel2 = useMemo(() => 
    wrapText(titel2, textMaxWidth, 72, 'Garnett Regular', 'regular'),
    [titel2, textMaxWidth]
  );
  const wrappedUntertitel = useMemo(() => 
    wrapText(untertitel, textMaxWidth, 30, 'Garnett Regular', 'regular'),
    [untertitel, textMaxWidth]
  );

  useEffect(() => {
    mechanic.done();
  }, []);

  return (
    <svg width={width} height={height}>
      <rect width={width} height={height} />
      <image href={ImgBanner} width={width} height={height} />
      <image x={950} y={-10} width={200} height={200} href={href}></image>
      
      {/* Wrapped Subtitel */}
      <text x={25} y={75} fill={schriftfarbe} textAnchor="start" fontWeight="regular" fontFamily="Garnett Regular" fontSize={30}>
        {wrappedSubtitel.map((line, idx) => (
          <tspan key={idx} x={25} dy={idx === 0 ? 0 : 35}>{line}</tspan>
        ))}
      </text>
      
      {/* Wrapped Titel */}
      <text x={25} y={height / 2} fill={schriftfarbe} textAnchor="start" fontWeight="regular" fontFamily="Garnett Regular" fontSize={72}>
        {wrappedTitel.map((line, idx) => (
          <tspan key={idx} x={25} dy={idx === 0 ? 0 : 80}>{line}</tspan>
        ))}
      </text>
      
      {/* Wrapped Titel2 */}
      <text x={25} y={height / 2 + 90} fill={schriftfarbe} textAnchor="start" fontWeight="regular" fontFamily="Garnett Regular" fontSize={72}>
        {wrappedTitel2.map((line, idx) => (
          <tspan key={idx} x={25} dy={idx === 0 ? 0 : 80}>{line}</tspan>
        ))}
      </text>
      
      {/* Wrapped Untertitel */}
      <text x={25} y={575} fill={schriftfarbe} textAnchor="start" fontWeight="regular" fontFamily="Garnett Regular" fontSize={30}>
        {wrappedUntertitel.map((line, idx) => (
          <tspan key={idx} x={25} dy={idx === 0 ? 0 : 35}>{line}</tspan>
        ))}
      </text>
    </svg>
  );
};

Key Details to Adjust:

  • Line Spacing: The dy value on <tspan> controls vertical space between lines. Tweak 35 (for 30px text) or 80 (for 72px text) to match your font’s natural line height.
  • Padding: The textMaxWidth subtracts 50px from your SVG width to match your 25px left padding—adjust this if you need different spacing.
  • Long Words: If you have single words that exceed the max width, you can add extra logic to split them mid-word (e.g., insert hyphens) if needed.

This setup will automatically wrap your dynamic text within the SVG bounds, no manual line breaks required!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 20:17:33