Cách đặt và căn giữa văn bản trong hình chữ nhật SVG


179

Tôi có hình chữ nhật sau ...

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

Tôi muốn tập trung vào từ "Tiểu thuyết" bên trong nó. Đối với các hình chữ nhật khác, liệu Svg có bao bọc trong chúng không? Tôi dường như không thể tìm thấy bất cứ điều gì cụ thể về việc chèn văn bản trong các hình dạng được tập trung cả theo chiều ngang và chiều dọc và gói từ. Ngoài ra, văn bản không thể rời khỏi hình chữ nhật.

Nhìn vào ví dụ http://www.w3.org/TR/SVG/text.html#TextEuity không giúp ích gì vì x và y của phần tử văn bản khác với x và y của hình chữ nhật. Dường như không có chiều rộng và chiều cao cho các yếu tố văn bản. Tôi không chắc chắn về toán học ở đây.

(Bảng html của tôi không hoạt động.)


1
bạn có thể vui lòng chuyển sang câu trả lời bằng cách sử dụng text-anchor và domin-baseline không? cảm ơn!
Neaumusic

1
neaumusic, thực hiện. 8)
Phu nhân Aleena

Câu trả lời:


309

Một giải pháp dễ dàng để căn giữa văn bản theo chiều ngang và chiều dọc trong SVG:

  1. Đặt vị trí của văn bản thành trung tâm tuyệt đối của thành phần mà bạn muốn căn giữa nó:

    • Nếu đó là cha mẹ, bạn có thể làm x="50%" y ="50%".
    • Nếu đó là một phần tử khác, xsẽ là xphần tử đó + một nửa chiều rộng của nó (và tương tự ynhưng với chiều cao).
  2. Sử dụng thuộc text-anchortính để căn giữa văn bản theo chiều ngang với giá trị middle:

    ở giữa

    Các ký tự được kết xuất được căn chỉnh sao cho giữa hình học của văn bản kết xuất được đặt ở vị trí văn bản hiện tại ban đầu.

  3. Sử dụng thuộc dominant-baselinetính để căn giữa văn bản theo chiều dọc với giá trị middle(hoặc tùy thuộc vào cách bạn muốn nó trông như thế nào, bạn có thể muốn làm central)

Đây là một bản demo đơn giản:

<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
Tôi đã tìm kiếm mọi giải pháp đơn giản ở mọi nơi - không có gì khác hiệu quả với tôi. Tôi đã sử dụng giải pháp này để căn giữa một số trong thanh tiến trình xuyên tâm
anthonytherockjohnson

7
Trong trường hợp của tôi, nó là dominant-baselinetài sản thực hiện công việc, không phải là alignment-baseline.
pintoch

1
Nếu bạn sẽ làm điều này thường xuyên, bạn cũng có thể thêm vào như sau : <style type="text/css"><![CDATA[ text { alignment-baseline: middle; text-anchor:middle; } ]]></style>. Cảm ơn giải pháp.
Manngo

1
Chạy đoạn mã của riêng bạn. Nó không hoạt động. Kiểm tra xem: Nó thậm chí không hoạt động trong bản xem trước được cung cấp bởi chính stackoverflow. Như đã nêu trước đây trong bình luận dominant-baselinethực sự cần phải được sử dụng. Tôi xin lỗi, nhưng câu trả lời này bằng cách nào đó gây hiểu lầm. Vì nó dường như không hoạt động trong Firefox cũng như Chromium và ngoài ra, bạn phải nghiên cứu các nhận xét để tự mình tìm ra giải pháp chính xác, tôi đã tự do thêm một câu trả lời khác với mã hoạt động như mong đợi. (Đối với các bản ghi: Đã thử nghiệm với FF và Chromium trên Ubuntu Linux.)
Regis ngày

2
@RegisMay cảm ơn bạn đã chỉ ra. Tôi đang trên Chrome trên máy Mac và nó làm việc tốt với alignment-baseline, nhưng có vẻ như nó có thể là một điều kỳ quặc vì xem xét các thông số kỹ thuật, nó phải là dominant-baselinecho text, và alignment-baselinecho tspan, tref, altGlyph, và textPath. Tôi cập nhật câu trả lời cho phù hợp.
Alvaro Montoro

38

SVG 1.2 Gói thêm văn bản nhỏ, nhưng hầu hết các triển khai SVG mà bạn sẽ tìm thấy trong trình duyệt (ngoại trừ Opera) đã không triển khai tính năng này. Thông thường, tùy thuộc vào bạn, nhà phát triển, định vị văn bản theo cách thủ công.

Đặc tả SVG 1.1 cung cấp một cái nhìn tổng quan về giới hạn này và các giải pháp khả thi để khắc phục nó:

Mỗi phần tử 'văn bản' làm cho một chuỗi văn bản được hiển thị. SVG thực hiện không ngắt dòng tự động hoặc gói từ. Để đạt được hiệu quả của nhiều dòng văn bản, hãy sử dụng một trong các phương pháp sau:

  • Tác giả hoặc gói tác giả cần tính toán trước các ngắt dòng và sử dụng nhiều phần tử 'văn bản' (một cho mỗi dòng văn bản).
  • Tác giả hoặc gói tác giả cần tính toán trước các ngắt dòng và sử dụng một phần tử 'văn bản' duy nhất với một hoặc nhiều phần tử con 'tspan' với các giá trị phù hợp cho các thuộc tính 'x', 'y', 'dx' và 'dy' để đặt vị trí bắt đầu mới cho những nhân vật bắt đầu dòng mới. (Cách tiếp cận này cho phép lựa chọn văn bản của người dùng trên nhiều dòng văn bản - xem phần Chọn văn bản và các thao tác trong bảng tạm.)
  • Thể hiện văn bản sẽ được hiển thị trong một không gian tên XML khác, chẳng hạn như nội tuyến được nhúng XHTML [XHTML] trong phần tử 'ForeignObject'. (Lưu ý: ngữ nghĩa chính xác của phương pháp này không hoàn toàn được xác định tại thời điểm này.)

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

Là một nguyên thủy, gói văn bản có thể được mô phỏng bằng cách sử dụng dythuộc tính và tspancác thành phần, và như được đề cập trong thông số kỹ thuật, một số công cụ có thể tự động hóa việc này. Ví dụ: trong Inkscape, chọn hình dạng bạn muốn và văn bản bạn muốn và sử dụng Văn bản -> Flow vào Khung. Điều này sẽ cho phép bạn viết văn bản của bạn, với gói, sẽ bao gồm dựa trên giới hạn của hình dạng. Ngoài ra, hãy đảm bảo bạn làm theo các hướng dẫn này để nói với Inkscape để duy trì khả năng tương thích với SVG 1.1: http://wiki.inkscape.org/wiki/index.php/FAQ#What_about_flowed tựa.3F

Hơn nữa, có một số thư viện JavaScript có thể được sử dụng để tự động hóa gói văn bản một cách linh hoạt: http://www.carto.net/ con / sgg / textFlow /

Thật thú vị khi lưu ý giải pháp của CSVG trong việc gói hình dạng cho một thành phần văn bản (ví dụ: xem ví dụ "nút" của họ), mặc dù điều quan trọng cần đề cập là việc triển khai của họ không thể sử dụng được trong trình duyệt: http://www.csse.monash.edu .au / ~ clm / csvg / about.html

Tôi đang đề cập đến điều này bởi vì tôi đã phát triển một thư viện lấy cảm hứng từ CSVG cho phép bạn làm những việc tương tự và hoạt động trong các trình duyệt web, mặc dù tôi chưa phát hành nó.


Cảm ơn đã trả lời ... nhưng ACK! Có vẻ như tôi sẽ phải đổ Svg rồi. Một trong những điều đầu tiên các nhà phát triển nên nghĩ đến là đính kèm văn bản (được định dạng theo ý muốn của người dùng) vào hình dạng! Tôi sẽ đợi cho đến khi họ có được hành động của họ với nhau trước khi tôi làm với nó nữa. Tôi sẽ cố gắng vẽ lại nó trong Windows Paint và xem nếu điều đó sẽ làm điều đó. (Giá như tôi có thể khiến các trình duyệt hiển thị bảng chính xác.)
Lady Aleena

Về văn bản có thể chỉnh sửa trong svg, Opera hỗ trợ thuộc tính có thể chỉnh sửa từ SVG 1.2 Tiny, cho phép lấy văn bản có thể chỉnh sửa bằng cách thêm một thuộc tính editable="true"vào phần tử văn bản hoặc textArea.
Erik Dahlström

2
@Erik, một lần nữa Opera lại đi trước đường cong.
jbeard4

@Lady Aleena, tại sao không sử dụng giải pháp Inkscape mà tôi đã đề cập? Nếu đó chỉ là một hình ảnh tĩnh (ví dụ: không có hành vi động, không cập nhật nội dung văn bản một cách linh hoạt), thì điều này sẽ hoạt động tốt. Thêm vào đó, nó sẽ có khả năng mở rộng, điều mà Paint sẽ không làm được, vì Paint tạo ra đồ họa raster. Cuối cùng, tôi không tin rằng Paint hỗ trợ gói văn bản ở mọi cấp độ.
jbeard4

@ echo-Flow Tôi đã mất tất cả niềm tin vào các chương trình sản xuất mã từ lâu vì tôi đã sử dụng (và sau đó đã bỏ) Trang trước của Microsoft để tạo html. Tôi luôn cảnh giác với các chương trình thêm mã độc quyền của riêng họ vào những gì tôi đang viết. Trang trước thực sự làm rối tung html của tôi. Nếu bạn là người dùng Inkscape, bạn có thể cho tôi biết nếu Inkscape đặt mã độc quyền của riêng mình vào svg mà tôi sẽ phải truy cập và loại bỏ sau khi tạo svg không?
Quý bà Aleena

23

Các câu trả lời trước cho kết quả kém khi sử dụng các góc tròn hoặc stroke-widthđó là> 1. Ví dụ: bạn sẽ mong đợi đoạn mã sau tạo ra một hình chữ nhật tròn, nhưng các góc được cắt bởi svgthành phần cha mẹ :

<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>

Thay vào đó, tôi khuyên bạn nên gói cái texttrong svgvà sau đó lồng cái mới svgvà cái rectbên trong một gphần tử, như trong ví dụ sau:

<!--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>

Điều này khắc phục vấn đề cắt xén xảy ra trong các câu trả lời ở trên. Tôi cũng đã dịch nhóm chỉnh lưu / văn bản bằng cách sử dụng transform="translate(x,y)"thuộc tính để chứng minh rằng điều này cung cấp một cách tiếp cận trực quan hơn để định vị trực tràng / văn bản trên màn hình.


16

Nếu bạn đang tạo SVG theo chương trình, bạn có thể đơn giản hóa nó và làm một cái gì đó như thế này:

  <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
Nó Shouldsn't được dominant-baselinetext-anchor, không dominantBaselinetextAnchor?
Nathaniel M. Beaver

1
@ NathanielM.Beaver Có, nó nên như vậy. Nắm bắt tốt. Đoạn mã trên là từ React, không may yêu cầu đổi tên các thuộc tính bằng camelCase.
Brennan Cheung

13

Bạn có thể trực tiếp sử dụng text-anchor = "middle"tài sản. Tôi khuyên bạn nên tạo một phần tử svg bao bọc trên hình chữ nhật và văn bản của bạn. Bằng cách đó bạn có thể sử dụng toàn bộ phần tử bằng một bộ chọn css. Đảm bảo bạn đặt thuộc tính 'x' và 'y' của văn bản là 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

Blog chi tiết đầy đủ: 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>


Không tập trung trong firefox trong khi các giải pháp khác là. codepen.io/anon/pen/oEByYr
TGF

Tôi đã thử nghiệm trên Firefox 58.0.1 (64-bit), nó hoạt động. Bạn có thể đề cập đến phiên bản mà nó không hoạt động cho bạn không?
Sahil Gupta

Firefox 58.0.2 64 bit. Các văn bản là hơi cao hơn nó nên được. Ban đầu nó có thể không dễ nhận thấy, nhưng nếu hộp của bạn rất vừa vặn thì rõ ràng hơn; cố gắng giảm chiều cao.
tgf

6

alignment-baselinekhông phải là thuộc tính phù hợp để sử dụng ở đây. Câu trả lời đúng là sử dụng kết hợp 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

Một cách để chèn văn bản bên trong một hình chữ nhật là chèn một đối tượng lạ, trong đó là một DIV, bên trong đối tượng chỉnh lưu.

Bằng cách này, văn bản sẽ phản hồi các giới hạn của 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

Tôi đã có một lỗi thời gian để lấy bất cứ thứ gì tập trung vào sử dụng SVG, vì vậy tôi đã thực hiện chức năng nhỏ của riêng mình. Hy vọng nó sẽ giúp bạn. Lưu ý rằng nó chỉ hoạt động cho các yếu tố 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

Để căn chỉnh ngang và dọc của văn bản trong đồ họa, bạn có thể muốn sử dụng các kiểu CSS sau. Cụ thể, lưu ý rằng dominant-baseline:middlecó lẽ sai, vì đây là (thường) một nửa giữa đường trên và đường cơ sở, thay vì nửa đường giữa đỉnh và đáy. Ngoài ra, một số nguồn (ví dụ Mozilla ) sử dụng dominant-baseline:hangingthay vì dominant-baseline:text-before-edge. Điều này cũng có thể sai, vì hangingđược thiết kế cho các tập lệnh Indicator. Tất nhiên, nếu bạn đang sử dụng hỗn hợp tiếng Latin, Indicator, ideograph hoặc bất cứ thứ gì, có lẽ bạn sẽ cần phải đọc tài liệu .

/* 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}

Chỉnh sửa: Tôi vừa nhận thấy rằng Mozilla cũng sử dụng dominant-baseline:baselinemột cách chắc chắn là sai: thậm chí nó không phải là một giá trị được công nhận! Tôi giả sử nó mặc định là mặc định phông chữ, đó làalphabetic , vì vậy họ đã gặp may mắn.

Chỉnh sửa thêm: Safari (11.1.2) hiểu text-before-edgenhưng không text-after-edge. Nó cũng thất bại ideographic. Công cụ tuyệt vời, Apple. Vì vậy, bạn có thể bị buộc phải sử dụng alphabeticvà cho phép con cháu sau khi tất cả. Lấy làm tiếc.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.