วิธีการวางและจัดกึ่งกลางข้อความในสี่เหลี่ยมผืนผ้า SVG


179

ฉันมีสี่เหลี่ยมผืนผ้าต่อไปนี้ ...

<rect x="0px" y="0px" width="60px" height="20px"/>

ฉันต้องการที่จะจัดกึ่งกลางคำว่า "นิยาย" ไว้ข้างใน สำหรับรูปสี่เหลี่ยมผืนผ้าอื่น ๆ svg word จะห่อหุ้มอยู่ภายในหรือไม่? ฉันไม่สามารถค้นหาสิ่งใดเป็นพิเศษเกี่ยวกับการแทรกข้อความภายในรูปร่างที่อยู่กึ่งกลางทั้งแนวนอนและแนวตั้งและการตัดคำ นอกจากนี้ข้อความไม่สามารถออกจากสี่เหลี่ยมผืนผ้าได้

การดูตัวอย่างhttp://www.w3.org/TR/SVG/text.html#TextElementนั้นไม่ได้ช่วยเนื่องจากองค์ประกอบข้อความของ x และ y แตกต่างจาก x และ y ของสี่เหลี่ยมผืนผ้า ดูเหมือนจะไม่มีความกว้างและความสูงสำหรับองค์ประกอบข้อความ ฉันไม่แน่ใจคณิตศาสตร์ที่นี่

(ตาราง html ของฉันจะไม่ทำงาน)


1
คุณกรุณาเปลี่ยนไปใช้คำตอบโดยใช้ text-anchor และ dominant-baseline ได้หรือไม่? ขอบคุณ!
neaumusic

1
neaumusic เสร็จแล้ว 8)
เลดี้อเลนา

คำตอบ:


310

วิธีง่ายๆในการจัดกึ่งกลางข้อความในแนวนอนและแนวตั้งใน SVG:

  1. กำหนดตำแหน่งของข้อความเป็นศูนย์กลางสัมบูรณ์ขององค์ประกอบที่คุณต้องการจัดกึ่งกลาง:

    • x="50%" y ="50%"ถ้าเป็นแม่คุณก็สามารถทำได้
    • ถ้ามันเป็นอีกองค์ประกอบหนึ่งxจะเป็นxขององค์ประกอบนั้น + ครึ่งหนึ่งของความกว้าง (และคล้ายกับyแต่มีความสูง)
  2. ใช้text-anchorคุณสมบัติเพื่อจัดกึ่งกลางข้อความตามแนวนอนด้วยค่าmiddle:

    กลาง

    อักขระที่แสดงผลจะถูกจัดตำแหน่งให้ตรงกลางเรขาคณิตของข้อความที่แสดงผลลัพธ์ที่ตำแหน่งข้อความปัจจุบันเริ่มต้น

  3. ใช้dominant-baselineคุณสมบัติเพื่อจัดกึ่งกลางข้อความตามแนวตั้งด้วยค่าmiddle(หรือขึ้นอยู่กับว่าคุณต้องการให้มันเป็นอย่างไรคุณอาจต้องการทำcentral)

นี่คือตัวอย่างง่ายๆ:

<svg width="200" height="100">
  <rect x="0" y="0" width="200" height="100" stroke="red" stroke-width="3px" fill="white"/>
  <text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle">TEXT</text>    
</svg>


1
ฉันได้มองหาวิธีแก้ปัญหาที่ง่าย ๆ นี้ - ไม่มีอะไรทำงานได้สำหรับฉันฉันใช้สิ่งนี้เพื่อจัดให้มีตัวเลขอยู่ในแถบความคืบหน้าของรัศมี
anthonytherockjohnson

7
ในกรณีของฉันมันเป็นคุณสมบัติที่จะทำงานไม่ได้dominant-baseline alignment-baseline
pintoch

1
<style type="text/css"><![CDATA[ text { alignment-baseline: middle; text-anchor:middle; } ]]></style>หากคุณกำลังจะทำเช่นนี้เป็นประจำคุณยังสามารถเพิ่มต่อไปนี้: ขอบคุณสำหรับการแก้ปัญหา
Manngo

1
เรียกใช้ข้อมูลโค้ดของคุณเอง มันไม่ทำงาน ลองดูสิ: มันยังไม่ทำงานในหน้าตัวอย่างที่จัดเตรียมโดย stackoverflow เอง ตามที่ระบุไว้ก่อนหน้านี้ในความคิดเห็นที่dominant-baselineแน่นอนจะต้องใช้ ฉันขอโทษ แต่คำตอบนี้ทำให้เข้าใจผิด ดูเหมือนว่าจะไม่ทำงานใน Firefox หรือ Chromium และนอกจากนี้คุณต้องศึกษาความคิดเห็นเพื่อหาวิธีการแก้ปัญหาที่ถูกต้องด้วยตัวคุณเองฉันมีอิสระในการเพิ่มคำตอบอีกด้วยรหัสที่ทำงานตามที่คาดไว้ (สำหรับเรคคอร์ด: ทดสอบกับ FF และ Chromium บน Ubuntu Linux)
Regis พฤษภาคม

2
@RegisMay ขอบคุณที่ชี้ให้เห็น ฉันอยู่ใน Chrome บน Mac และทำงานดีด้วยalignment-baselineแต่ดูเหมือนว่ามันอาจจะเป็นสิ่งที่เล่นโวหารเพราะการตรวจสอบรายละเอียดที่มันควรจะเป็นdominant-baselineสำหรับtextและalignment-baselineสำหรับtspan, tref, และaltGlyph textPathฉันปรับปรุงคำตอบตามนั้น
Alvaro Montoro

38

SVG 1.2 การเพิ่มข้อความเล็ก ๆ แต่การใช้งาน SVG ส่วนใหญ่ที่คุณจะพบในเบราว์เซอร์ (ยกเว้น Opera) ไม่ได้ใช้คุณสมบัตินี้ โดยทั่วไปแล้วขึ้นอยู่กับคุณนักพัฒนาเพื่อจัดตำแหน่งข้อความด้วยตนเอง

ข้อมูลจำเพาะ SVG 1.1 ให้ภาพรวมที่ดีของข้อ จำกัด นี้และวิธีแก้ปัญหาที่เป็นไปได้ในการแก้ไข:

องค์ประกอบ 'ข้อความ' แต่ละข้อความทำให้เกิดการแสดงผลสตริงข้อความเดียว SVG ไม่มีการแบ่งบรรทัดอัตโนมัติหรือการตัดคำ เพื่อให้ได้ผลของข้อความหลายบรรทัดให้ใช้วิธีใดวิธีหนึ่งต่อไปนี้:

  • ผู้แต่งหรือแพ็คเกจการเขียนจำเป็นต้องคำนวณการขึ้นบรรทัดใหม่และใช้องค์ประกอบ 'ข้อความ' หลายรายการ (หนึ่งรายการสำหรับแต่ละบรรทัดข้อความ)
  • ผู้แต่งหรือแพ็คเกจการเขียนจำเป็นต้องคำนวณการขึ้นบรรทัดใหม่และใช้องค์ประกอบ 'text' เดี่ยวกับองค์ประกอบย่อย 'tspan' หนึ่งรายการหรือมากกว่าด้วยค่าที่เหมาะสมสำหรับแอตทริบิวต์ 'x', 'y', 'dx' และ 'dy' เพื่อกำหนดตำแหน่งเริ่มต้นใหม่สำหรับอักขระเหล่านั้นที่เริ่มบรรทัดใหม่ (วิธีนี้ช่วยให้การเลือกข้อความผู้ใช้ข้ามข้อความหลายบรรทัด - ดูการเลือกข้อความและการทำงานของคลิปบอร์ด)
  • แสดงข้อความที่จะเรนเดอร์ใน XML เนมสเปซอื่นเช่น XHTML [XHTML] ฝังแบบอินไลน์ภายในองค์ประกอบ 'foreignObject' (หมายเหตุ: ความหมายที่แท้จริงของวิธีการนี้ไม่ได้กำหนดไว้อย่างสมบูรณ์ในขณะนี้)

http://www.w3.org/TR/SVG11/text.html#Introduction

ในฐานะดั้งเดิมการตัดข้อความสามารถจำลองได้โดยใช้dyคุณลักษณะและtspanองค์ประกอบและตามที่ระบุไว้ในข้อมูลจำเพาะเครื่องมือบางอย่างสามารถทำสิ่งนี้โดยอัตโนมัติ ตัวอย่างเช่นใน Inkscape เลือกรูปร่างที่คุณต้องการและข้อความที่คุณต้องการและใช้ Text -> Flow to Frame สิ่งนี้จะช่วยให้คุณเขียนข้อความของคุณพร้อมการพันซึ่งจะเป็นการพันตามขอบเขตของรูปร่าง นอกจากนี้ตรวจสอบให้แน่ใจว่าคุณทำตามคำแนะนำเหล่านี้เพื่อบอกให้ Inkscape รักษาความเข้ากันได้กับ SVG 1.1: http://wiki.inkscape.org/wiki/index.php/FAQ#What_about_flowed_text.3F

นอกจากนี้ยังมีห้องสมุด JavaScript บางส่วนที่สามารถใช้ในการตัดข้อความอัตโนมัติแบบไดนามิก: http://www.carto.net/papers/svg/textFlow/

เป็นเรื่องที่น่าสนใจที่จะทราบวิธีแก้ปัญหาของ CSVG เพื่อตัดรูปร่างให้กับองค์ประกอบข้อความ (เช่นดูตัวอย่าง "ปุ่ม" ของพวกเขา) แม้ว่าสิ่งสำคัญคือต้องกล่าวว่าการนำไปใช้งานไม่สามารถใช้งานได้ในเบราว์เซอร์: http://www.csse.monash.edu .au / ~ CLM / csvg / about.html

ฉันกำลังพูดถึงเรื่องนี้เพราะฉันได้พัฒนาห้องสมุดที่ได้แรงบันดาลใจจาก CSVG ที่ช่วยให้คุณทำสิ่งที่คล้ายกันและทำงานในเว็บเบราว์เซอร์แม้ว่าฉันจะยังไม่ได้เปิดตัวก็ตาม


ขอบคุณสำหรับการตอบกลับ ... แต่ ACK! ดูเหมือนว่าฉันจะต้องทิ้ง svg แล้ว หนึ่งในสิ่งแรกที่นักพัฒนาควรคำนึงถึงคือการแนบข้อความ (จัดรูปแบบตามที่ผู้ใช้ต้องการ) ให้เป็นรูปร่าง! ฉันจะรอจนกว่าพวกเขาจะได้แสดงร่วมกันก่อนที่ฉันจะทำมันอีกต่อไป ฉันจะลองวาดมันใหม่ใน Windows Paint และดูว่ามันจะทำหรือไม่ (ถ้าเพียง แต่ฉันสามารถให้เบราว์เซอร์แสดงตารางได้อย่างถูกต้อง)
เลดี้อเลนา

เกี่ยวกับข้อความที่แก้ไขได้ใน svg Opera สนับสนุนแอตทริบิวต์ที่สามารถแก้ไขได้จาก SVG 1.2 Tiny ซึ่งทำให้สามารถรับข้อความที่แก้ไขได้โดยเพียงเพิ่มแอททริบิวeditable="true"ให้กับข้อความหรือองค์ประกอบ textArea
Erik Dahlström

2
@Erik อีกครั้ง Opera อยู่เหนือเส้นโค้ง
jbeard4

@ เลดี้อเลนาทำไมไม่ใช้โซลูชัน Inkscape ที่ฉันพูดถึง? หากเป็นเพียงภาพคงที่ (เช่นไม่มีพฤติกรรมแบบไดนามิกไม่ต้องปรับปรุงเนื้อหาข้อความแบบไดนามิก) สิ่งนี้น่าจะใช้ได้ดี นอกจากนี้ยังสามารถปรับขนาดได้ซึ่ง Paint จะไม่สามารถใช้งานได้เนื่องจาก Paint จะสร้างกราฟิกแรสเตอร์ ในที่สุดฉันไม่เชื่อว่า Paint รองรับการตัดข้อความในทุกระดับ
jbeard4

@ echo-flow ฉันหมดความเชื่อมั่นในโปรแกรมสร้างโค้ดมานานแล้วตั้งแต่ฉันใช้ (และทิ้งในภายหลัง) Microsoft Front Page เพื่อสร้าง html ฉันมักจะระมัดระวังโปรแกรมที่จะเพิ่มรหัสกรรมสิทธิ์ของตนเองในสิ่งที่ฉันกำลังเขียน Front Page ทำให้ HTML ของฉันยุ่งมาก หากคุณเป็นผู้ใช้ Inkscape คุณสามารถบอกฉันได้หรือไม่ว่า Inkscape วางรหัสที่เป็นกรรมสิทธิ์ของตัวเองใน svg ที่ฉันจะต้องเข้าไปและดึงออกหลังจากการสร้าง svg?
เลดี้อเลนา

23

คำตอบก่อนหน้านี้ให้ผลลัพธ์ที่ไม่ดีเมื่อใช้มุมมนหรือstroke-widthนั่นคือ> 1 ตัวอย่างเช่นคุณคาดหวังว่ารหัสต่อไปนี้ในการผลิตสี่เหลี่ยมผืนผ้าโค้งมน แต่มุมที่ถูกตัดโดยsvgองค์ประกอบหลัก:

<svg width="200" height="100">
  <!--this rect should have rounded corners-->
  <rect x="0" y="0" rx="5" ry="5" width="200" height="100" stroke="red" stroke-width="10px" fill="white"/>
  <text x="50%" y="50%" alignment-baseline="middle" text-anchor="middle">CLIPPED BORDER</text>    
</svg>

แต่ฉันขอแนะนำให้ห่อtextในsvgและจากนั้นซ้อนใหม่svgและrectรวมเข้าด้วยกันในgองค์ประกอบดังตัวอย่างต่อไปนี้:

<!--the outer svg here-->
<svg width="400px" height="300px">

  <!--the rect/text group-->
  <g transform="translate(50,50)">
    <rect rx="5" ry="5" width="200" height="100" stroke="green" fill="none" stroke-width="10"/>
    <svg width="200px" height="100px">
      <text x="50%" y="50%" alignment-baseline="middle" text-anchor="middle">CORRECT BORDER</text>      
    </svg>
  </g>

  <!--rest of the image's code-->
</svg>

วิธีนี้ช่วยแก้ไขปัญหาการคลิปที่เกิดขึ้นในคำตอบข้างต้น ฉันยังแปลกลุ่ม rect / text โดยใช้แอtransform="translate(x,y)"ททริบิวเพื่อแสดงให้เห็นว่านี่เป็นวิธีการที่ใช้งานง่ายยิ่งขึ้นในการจัดตำแหน่ง rect / text บนหน้าจอ


16

หากคุณกำลังสร้าง SVG โดยทางโปรแกรมคุณสามารถทำให้มันง่ายขึ้นและทำสิ่งนี้:

  <g>
      <rect x={x} y={y} width={width} height={height} />
      <text
          x={x + width / 2}
          y={y + height / 2}
          dominant-baseline="middle"
          text-anchor="middle"
      >
          {label}
      </text>
  </g>

3
ไม่ควรdominant-baselineและtext-anchorไม่dominantBaselineและtextAnchor?
นาธาเนียลเอ็มบีเวอร์

1
@ NathanielM.Beaver ใช่มันควรจะเป็น จับดี. โค้ดด้านบนมาจาก React ซึ่งน่าเสียดายที่ต้องมีการเปลี่ยนชื่อแอททริบิวโดยใช้ camelCase
Brennan Cheung

13

คุณสามารถใช้text-anchor = "middle"คุณสมบัติโดยตรง ผมแนะนำให้สร้างองค์ประกอบ svg wrapper มากกว่าสี่เหลี่ยมผืนผ้าและข้อความของคุณ ด้วยวิธีนี้คุณสามารถใช้องค์ประกอบทั้งหมดโดยใช้หนึ่งตัวเลือก CSS ตรวจสอบให้แน่ใจว่าคุณวางคุณสมบัติ 'x' และ 'y' ของข้อความเป็น 50%

    <svg class="svg-rect" width="50" height="40">
        <rect x="0" y="0" rx="3" ry="3" width="50" height="40" fill="#e7e7e7"></rect>
        <text x="50%" y="50%" text-anchor="middle" stroke="black" stroke-width="1px" dy=".3em">N/A</text>
    </svg>


7

บล็อกรายละเอียดแบบเต็ม: http://blog.techhysahil.com/svg/how-to-center-text-in-svg-shapes/

<svg width="600" height="600">
  <!--   Circle -->
  <g transform="translate(50,40)">
    <circle cx="0" cy="0" r="35" stroke="#aaa" stroke-width="2" fill="#fff"></circle>
    <text x="0" y="0" alignment-baseline="middle" font-size="12" stroke-width="0" stroke="#000" text-anchor="middle">HueLink</text>
  </g>
  
  <!--   In Rectangle text position needs to be given half of width and height of rectangle respectively -->
  <!--   Rectangle -->
  <g transform="translate(150,20)">
    <rect width="150" height="40" stroke="#aaa" stroke-width="2" fill="#fff"></rect>
    <text x="75" y="20" alignment-baseline="middle" font-size="12" stroke-width="0" stroke="#000" text-anchor="middle">HueLink</text>
  </g>
  
  <!--   Rectangle -->
  <g transform="translate(120,140)">
    <ellipse cx="0" cy="0" rx="100" ry="50" stroke="#aaa" stroke-width="2" fill="#fff"></ellipse>
    <text x="0" y="0" alignment-baseline="middle" font-size="12" stroke-width="0" stroke="#000" text-anchor="middle">HueLink</text>
  </g>
  
  
</svg>


ไม่ได้อยู่กึ่งกลางใน Firefox ในขณะที่โซลูชั่นอื่น ๆ codepen.io/anon/pen/oEByYr
tgf

ฉันทดสอบกับ Firefox 58.0.1 (64 บิต) มันใช้งานได้ คุณช่วยพูดถึงเวอร์ชั่นที่มันใช้ไม่ได้กับคุณไหม?
Sahil Gupta

Firefox 58.0.2 64 บิต ข้อความสูงกว่าที่ควรจะเป็นเล็กน้อย มันอาจจะไม่สังเกตเห็นได้ง่ายในตอนแรก แต่ถ้ากล่องของคุณพอดีอย่างใกล้ชิดมันจะชัดเจนมากขึ้น ลองลดความสูง
tgf

6

alignment-baselineไม่ใช่แอตทริบิวต์ที่เหมาะสมที่จะใช้ที่นี่ คำตอบที่ถูกต้องคือการใช้การรวมกันของdominant-baseline="central"และtext-anchor="middle":

<svg width="200" height="100">
    <g>
        <rect x="0" y="0" width="200" height="100" style="stroke:red; stroke-width:3px; fill:white;"/>
        <text x="50%" y="50%" style="dominant-baseline:central; text-anchor:middle; font-size:40px;">TEXT</text>
    </g>
</svg>


2

วิธีหนึ่งในการแทรกข้อความภายในสี่เหลี่ยมคือการแทรกวัตถุแปลกปลอมซึ่งเป็น DIV ภายในวัตถุ rect

วิธีนี้ข้อความจะตอบสนองข้อ จำกัด ของ DIV

var g = d3.select("svg");
					
g.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width","100%")
.attr("height","100%")
.attr("fill","#000");


var fo = g.append("foreignObject")
 .attr("width","100%");

fo.append("xhtml:div")
  .attr("style","width:80%;color:#FFF;margin-right: auto;margin-left: auto;margin-top:40px")
  .text("Mussum Ipsum, cacilds vidis litro abertis Mussum Ipsum, cacilds vidis litro abertis Mussum Ipsum, cacilds vidis litro abertis");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.js"></script>
<svg width="200" height="200"></svg>


1

ฉันมีคนเสพเวจมรรคในเวลาที่รับอะไรก็ตามที่เป็นศูนย์กลางโดยใช้ SVG ดังนั้นฉันจึงใช้งานฟังก์ชั่นเล็ก ๆ น้อย ๆ ของตัวเอง หวังว่ามันจะช่วยคุณได้ โปรดทราบว่าใช้งานได้กับองค์ประกอบ SVG เท่านั้น

function centerinparent(element) { //only works for SVG elements
    var bbox = element.getBBox();
    var parentwidth = element.parentNode.width.baseVal.value;
    var parentheight = element.parentNode.height.baseVal.value;
    var newwidth = ((parentwidth / 2) - (bbox.width / 2)) - 2; //i start everything off by 2 to account for line thickness
    var newheight = ((parentheight / 2) - (bbox.height / 2)) - 2;
    //need to adjust for line thickness??

    if (element.classList.contains("textclass")) { //text is origined from bottom left, whereas everything else origin is top left
        newheight += bbox.height; //move it down by its height
    }

    element.setAttributeNS(null, "transform", "translate(" + newwidth + "," + newheight + ")");
    // console.log("centering BOXES:  between width:"+element.parentNode.width.baseVal.value + "   height:"+parentheight);
    // console.log(bbox);
}

1

สำหรับการจัดแนวแนวนอนและแนวตั้งของข้อความในกราฟิกคุณอาจต้องการใช้สไตล์ CSS ต่อไปนี้ โดยเฉพาะอย่างยิ่งโปรดทราบว่าdominant-baseline:middleอาจเป็นสิ่งที่ผิดเนื่องจากเป็นครึ่งทางระหว่างด้านบนกับเส้นฐานแทนที่จะเป็นครึ่งทางระหว่างด้านบนและด้านล่าง นอกจากนี้บางบางแหล่ง (เช่นMozilla ) ใช้แทนdominant-baseline:hanging dominant-baseline:text-before-edgeสิ่งนี้อาจผิดเนื่องจาก hangingถูกออกแบบมาสำหรับสคริปต์ภาษาอินดิก แน่นอนถ้าคุณกำลังใช้เป็นส่วนผสมของละตินภาษาสันสกฤต ideographs หรือสิ่งที่คุณอาจจะต้องอ่านเอกสาร

/* Horizontal alignment */
text.goesleft{text-anchor:end}
text.equalleftright{text-anchor:middle}
text.goesright{text-anchor:start}
/* Vertical alignment */
text.goesup{dominant-baseline:text-after-edge}
text.equalupdown{dominant-baseline:central}
text.goesdown{dominant-baseline:text-before-edge}
text.ruledpaper{dominant-baseline:alphabetic}

แก้ไข: ฉันเพิ่งสังเกตเห็นว่าMozillaยังใช้dominant-baseline:baselineซึ่งผิดอย่างแน่นอน: มันไม่ได้แม้แต่ค่าที่รู้จัก! ฉันคิดว่ามันเป็นค่าเริ่มต้นของแบบอักษรเริ่มต้นซึ่งก็คือalphabeticพวกเขาโชคดี

แก้ไขเพิ่มเติม: Safari (11.1.2) เข้าใจแต่ไม่text-before-edge นอกจากนี้ยังล้มเหลวในtext-after-edge ideographicสิ่งที่ยอดเยี่ยม Apple ดังนั้นคุณอาจถูกบังคับให้ใช้alphabeticและอนุญาตให้สืบทอดหลังจากทั้งหมด ขอโทษ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.