จะ linebreak ข้อความ svg ภายใน javascript ได้อย่างไร?


107

นี่คือสิ่งที่ฉันมี:

<path class="..." onmousemove="show_tooltip(event,'very long text 
    \\\n I would like to linebreak')" onmouseout="hide_tooltip()" d="..."/>

<rect class="tooltip_bg" id="tooltip_bg" ... />
<text class="tooltip" id="tooltip" ...>Tooltip</text>

<script>
<![CDATA[
function show_tooltip(e,text) {
    var tt = document.getElementById('tooltip');
    var bg = document.getElementById('tooltip_bg');

    // set position ...

    tt.textContent=text;

    bg.setAttribute('width',tt.getBBox().width+10);
    bg.setAttribute('height',tt.getBBox().height+6);

    // set visibility ...
}
...

ตอนนี้ข้อความ Tooltip ที่ยาวมากของฉันไม่มี linebreak แม้ว่าฉันจะใช้ alert (); มันแสดงให้ฉันเห็นว่าจริงๆแล้วข้อความมีสองบรรทัด (แม้ว่าจะมี "\" อยู่ฉันจะลบออกทีละรายการได้อย่างไร)
ฉันไม่สามารถใช้ CDATA ได้ทุกที่


2
มีโอกาสใดบ้างที่ svgjs.com/textflowนี้อาจช่วยคำแนะนำเครื่องมือของคุณ
Alvin K.

@AlvinK. ลิงค์เสีย ฉันพยายามค้นหาตำแหน่งใหม่ แต่ล้มเหลว
guettli

คำตอบ:


150

นี่ไม่ใช่สิ่งที่ SVG 1.1 รองรับ SVG 1.2 มีtextAreaองค์ประกอบพร้อมการตัดคำอัตโนมัติ แต่ไม่ได้ใช้งานในทุกเบราว์เซอร์ SVG 2 ไม่ได้วางแผนในการดำเนินการtextAreaแต่มันจะมีข้อความอัตโนมัติห่อ

อย่างไรก็ตามเนื่องจากคุณรู้อยู่แล้วว่าควรเกิดการขีดเส้นตรงไหนคุณสามารถแบ่งข้อความของคุณออกเป็นหลาย ๆ ตัว<tspan>โดยแต่ละบรรทัดx="0"และdy="1.4em"เพื่อจำลองบรรทัดของข้อความจริง ตัวอย่างเช่น:

<g transform="translate(123 456)"><!-- replace with your target upper left corner coordinates -->
  <text x="0" y="0">
    <tspan x="0" dy="1.2em">very long text</tspan>
    <tspan x="0" dy="1.2em">I would like to linebreak</tspan>
  </text>
</g>

แน่นอนเนื่องจากคุณต้องการทำจาก JavaScript คุณจะต้องสร้างและแทรกแต่ละองค์ประกอบลงใน DOM ด้วยตนเอง


2
และฉันจะรู้ได้อย่างไรว่าจะวางที่<tspan>sใด? แทนที่? แยก?
sollniss

2
พยายามแล้ว var tspan = document.createElement('tspan') tspan.setAttribute('x','0'); tspan.setAttribute('dy','1.2em'); tspan.textContent = text; tt.appendChild(tspan); ไม่แสดงข้อความใด ๆ เลย
sollniss

2
คุณจะสนใจที่จะทำอย่างละเอียดในเหตุผลที่x = '0' DY = '1.2em'เป็นสิ่งจำเป็น? มันได้ผลจริงอย่างที่คุณพูด อย่างไรก็ตามฉันคาดหวังว่ามันจะใช้งานได้แม้ว่าจะไม่มีคุณสมบัติเหล่านั้นก็ตาม แต่ไม่มีอะไรปรากฏ ... นอกจากนี้ฉันยังไม่ชัดเจนว่าทำไม linebreak จึงเกิดขึ้นเลย มันไม่เหมือนกับที่เราตั้งค่าความกว้างของคอนเทนเนอร์เพื่อแก้ไขอะไรบางอย่างเพื่อที่มันจะกำหนดเส้นแบ่งได้ใช่ไหม?
Konrad Viltersten

4
x=0เป็นที่แน่นอนประสานงาน: ย้ายชิ้นส่วนข้อความที่ต้นกำเนิดของปัจจุบันระบบพิกัด transformแอตทริบิวต์ในgองค์ประกอบที่กำหนดในปัจจุบันใหม่ระบบพิกัดและสมมติว่าข้อความที่ถูกจัดชิดซ้าย, tspan จะถูกย้ายไปทางซ้าย สิ่งนี้ทำหน้าที่เหมือนคำสั่งกลับรถ dy=1.2emเป็นพิกัดสัมพัทธ์ : ย้ายส่วนข้อความตามจำนวนนี้โดยสัมพันธ์กับส่วนของข้อความปัจจุบัน สิ่งนี้ทำหน้าที่เหมือนคำสั่งป้อนบรรทัด เมื่อรวมกันคุณจะได้รับ CR / LF
Sergiu Dumitriu

ยังไม่ได้ลอง: คุณสามารถทำได้โดยไม่มีกลุ่มหรือไม่? <text x = "100" y = "100"> <tspan x = "100" y = "100"> ข้อความยาวมาก </tspan> <tspan x = "100" y = "115"> ฉันต้องการ linebreak </tspan> </text> ??
Richard

25

ฉันคิดว่าคุณ alredy สามารถแก้ปัญหาได้ แต่ถ้ามีคนกำลังมองหาวิธีแก้ปัญหาที่คล้ายกันสิ่งนี้ก็ใช้ได้กับฉัน:

 g.append('svg:text')
  .attr('x', 0)
  .attr('y', 30)
  .attr('class', 'id')
  .append('svg:tspan')
  .attr('x', 0)
  .attr('dy', 5)
  .text(function(d) { return d.name; })
  .append('svg:tspan')
  .attr('x', 0)
  .attr('dy', 20)
  .text(function(d) { return d.sname; })
  .append('svg:tspan')
  .attr('x', 0)
  .attr('dy', 20)
  .text(function(d) { return d.idcode; })

มี 3 บรรทัดคั่นด้วย linebreak


21
FWIW: ดูเหมือนว่า OP ใช้ JavaScript ล้วนๆ คำตอบนี้ดูเหมือนจะใช้ประโยชน์จากD3
Ben Mosher

ฉันใช้ D3 และแนวทางของคุณได้ผลสำหรับฉัน ขอบคุณที่โพสต์ ฉันพบว่าฉันจำเป็นต้องลบ tspans เก่าก่อนที่จะต่อท้ายอันใหม่เช่นนี้ focus.selectAll ("tspan"). remove ();
Darren Parker

1
ระวังด้วยวิธีนี้ที่รังแท็ก <tspan> เนื่องจากมันเชื่อมโยง. append () ซึ่งอาจทำให้เกิดอาการปวดหัวเล็กน้อยกับ CSS ทั้งนี้ขึ้นอยู่กับสิ่งที่คุณต้องการทำ
seneyr

ดูที่นี่สำหรับวิธีการที่หลีกเลี่ยงการทำรังอธิบายโดย @seneyr
bszom

16

ด้วยโซลูชัน tspan สมมติว่าคุณไม่รู้ล่วงหน้าว่าจะแบ่งบรรทัดไว้ที่ใด: คุณสามารถใช้ฟังก์ชันที่ดีนี้ได้ซึ่งฉันพบที่นี่: http://bl.ocks.org/mbostock/7555321

ซึ่งจะแบ่งบรรทัดโดยอัตโนมัติสำหรับข้อความยาว svg สำหรับความกว้างที่กำหนดเป็นพิกเซล

function wrap(text, width) {
  text.each(function() {
    var text = d3.select(this),
        words = text.text().split(/\s+/).reverse(),
        word,
        line = [],
        lineNumber = 0,
        lineHeight = 1.1, // ems
        y = text.attr("y"),
        dy = parseFloat(text.attr("dy")),
        tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
    while (word = words.pop()) {
      line.push(word);
      tspan.text(line.join(" "));
      if (tspan.node().getComputedTextLength() > width) {
        line.pop();
        tspan.text(line.join(" "));
        line = [word];
        tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
      }
    }
  });
}

9

ฉันคิดว่าสิ่งนี้ทำในสิ่งที่คุณต้องการ:

function ShowTooltip(evt, mouseovertext){
    // Make tooltip text        
    var tooltip_text = tt.childNodes.item(1);
    var words = mouseovertext.split("\\\n");
    var max_length = 0;

    for (var i=0; i<3; i++){
        tooltip_text.childNodes.item(i).firstChild.data = i<words.length ?  words[i] : " ";
        length = tooltip_text.childNodes.item(i).getComputedTextLength();
        if (length > max_length) {max_length = length;}
    }

    var x = evt.clientX + 14 + max_length/2;
    var y = evt.clientY + 29;
    tt.setAttributeNS(null,"transform", "translate(" + x + " " + y + ")")

    // Make tooltip background
    bg.setAttributeNS(null,"width", max_length+15);
    bg.setAttributeNS(null,"height", words.length*15+6);
    bg.setAttributeNS(null,"x",evt.clientX+8);
    bg.setAttributeNS(null,"y",evt.clientY+14);

    // Show everything
    tt.setAttributeNS(null,"visibility","visible");
    bg.setAttributeNS(null,"visibility","visible");
}

มันแยกข้อความ\\\nและสำหรับแต่ละส่วนทำให้แต่ละส่วนใน tspan จากนั้นจะคำนวณขนาดของกล่องที่ต้องการตามความยาวของข้อความที่ยาวที่สุดและจำนวนบรรทัด คุณจะต้องเปลี่ยนองค์ประกอบข้อความคำแนะนำเครื่องมือให้มีสามช้อนชา:

<g id="tooltip" visibility="hidden">
    <text><tspan>x</tspan><tspan x="0" dy="15">x</tspan><tspan x="0" dy="15">x</tspan></text>
</g>

ถือว่าคุณไม่เคยมีเกินสามบรรทัด หากคุณต้องการมากกว่าสามบรรทัดคุณสามารถเพิ่มช้อนชาและเพิ่มความยาวของห่วง


ทำไมถึงเป็น"\\\n"มากกว่า"\n"?
ralien

3

ใช้ HTML แทนจาวาสคริปต์

<html>
  <head><style> * { margin: 0; padding: 0; } </style></head>
  <body>
    <h1>svg foreignObject to embed html</h1>

    <svg
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 300 300"
      x="0" y="0" height="300" width="300"
    >

      <circle
        r="142" cx="150" cy="150"
        fill="none" stroke="#000000" stroke-width="2"
      />

      <foreignObject
        x="50" y="50" width="200" height="200"
      >
        <div
          xmlns="http://www.w3.org/1999/xhtml"
          style="
            width: 196px; height: 196px;
            border: solid 2px #000000;
            font-size: 32px;
            overflow: auto; /* scroll */
          "
        >
          <p>this is html in svg 1</p>
          <p>this is html in svg 2</p>
          <p>this is html in svg 3</p>
          <p>this is html in svg 4</p>
        </div>
      </foreignObject>

    </svg>

</body></html>


ฉันคิดว่าคุณหมายถึง "ใช้ SVG แทน JavaScript"
Valerio Bozz

มันคือ "HTML ใน SVG" ซึ่งเป็นทางออกที่ดีที่สุดสำหรับฉัน!
Kévin Berthommier

2

ฉันได้ปรับวิธีแก้ปัญหาเล็กน้อยโดย @steco เปลี่ยนการอ้างอิงจากd3เป็นjqueryและเพิ่มheightองค์ประกอบข้อความเป็นพารามิเตอร์

function wrap(text, width, height) {
  text.each(function(idx,elem) {
    var text = $(elem);
    text.attr("dy",height);
        var words = text.text().split(/\s+/).reverse(),
        word,
        line = [],
        lineNumber = 0,
        lineHeight = 1.1, // ems
        y = text.attr("y"),
        dy = parseFloat( text.attr("dy") ),
        tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
    while (word = words.pop()) {
      line.push(word);
      tspan.text(line.join(" "));
      if (elem.getComputedTextLength() > width) {
        line.pop();
        tspan.text(line.join(" "));
        line = [word];
        tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
      }
    }
  });
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.