Cara menempatkan dan menempatkan teks di tengah persegi panjang SVG

179

Saya memiliki persegi panjang berikut ...

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

Saya ingin memusatkan kata "Fiksi" di dalamnya. Untuk persegi panjang lain, apakah bungkus kata svg tetap di dalamnya? Saya tidak dapat menemukan apa pun secara khusus tentang menyisipkan teks ke dalam bentuk yang dipusatkan baik secara horizontal maupun vertikal dan bungkus kata. Juga, teks tidak bisa meninggalkan persegi panjang.

Melihat http://www.w3.org/TR/SVG/text.html#TextElement contoh tidak membantu karena elemen teks x dan y berbeda dari x dan y persegi panjang. Tampaknya tidak ada lebar dan tinggi untuk elemen teks. Saya tidak yakin dengan matematika di sini.

(Tabel html saya tidak akan berfungsi.)

Nyonya Aleena
sumber
1
bisakah Anda beralih ke jawaban menggunakan anchor teks dan dominan-baseline? Terima kasih!
neaumusic
1
neaumusic, selesai. 8)
Lady Aleena

Jawaban:

310

Solusi mudah untuk memusatkan teks secara horizontal dan vertikal di SVG:

  1. Setel posisi teks ke pusat absolut elemen tempat Anda ingin memusatkannya:

    • Jika itu orangtua, Anda bisa melakukannya x="50%" y ="50%".
    • Jika itu adalah elemen lain, xakan menjadi xelemen + setengah lebarnya (dan serupa ydengan tetapi dengan ketinggian).
  2. Gunakan text-anchorproperti untuk memusatkan teks secara horizontal dengan nilai middle:

    tengah

    Karakter yang diberikan disejajarkan sedemikian rupa sehingga bagian tengah geometris dari teks yang dihasilkan berada pada posisi teks awal saat ini.

  3. Gunakan dominant-baselineproperti untuk memusatkan teks secara vertikal dengan nilai middle(atau tergantung pada bagaimana Anda ingin itu terlihat, Anda mungkin ingin melakukannya central)

Ini demo sederhana:

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

Alvaro Montoro
sumber
1
Saya telah mencari di mana-mana untuk solusi sesederhana ini - tidak ada yang bekerja untuk saya. Saya menggunakan ini untuk memusatkan angka di dalam bilah kemajuan radial
anthonytherockjohnson
7
Dalam kasus saya, itu adalah dominant-baselineproperti yang melakukan pekerjaan, bukan alignment-baseline.
pintoch
1
Jika Anda akan melakukan ini secara teratur, Anda juga dapat menambahkan berikut: <style type="text/css"><![CDATA[ text { alignment-baseline: middle; text-anchor:middle; } ]]></style>. Terima kasih atas solusinya.
Manngo
1
Jalankan cuplikan kode Anda sendiri. Itu tidak bekerja. Lihat ini: Bahkan tidak berfungsi di pratinjau yang disediakan oleh stackoverflow itu sendiri. Seperti yang dinyatakan sebelumnya dalam komentar dominant-baselinememang perlu digunakan. Maaf, tapi jawaban ini entah bagaimana menyesatkan. Karena sepertinya tidak berfungsi di Firefox atau Chromium dan selain itu Anda harus mempelajari komentar untuk menemukan solusi yang benar pada Anda sendiri, saya mengambil kebebasan untuk menambahkan jawaban lain dengan kode yang berfungsi seperti yang diharapkan. (Sebagai catatan: Diuji dengan FF dan Chromium di Ubuntu Linux.)
Regis
2
@RegisMay terima kasih telah menunjukkannya. Saya di Chrome di Mac dan bekerja dengan baik dengan alignment-baseline, tapi tampaknya itu mungkin menjadi hal yang aneh karena meninjau spesifikasi, harus dominant-baselineuntuk text, dan alignment-baselineuntuk tspan, tref, altGlyph, dan textPath. Saya memperbarui jawabannya.
Alvaro Montoro
38

SVG 1.2 Tiny menambahkan pembungkus teks, tetapi sebagian besar implementasi SVG yang akan Anda temukan di browser (dengan pengecualian Opera) belum menerapkan fitur ini. Biasanya terserah Anda, pengembang, untuk memposisikan teks secara manual.

Spesifikasi SVG 1.1 memberikan tinjauan yang baik tentang batasan ini, dan kemungkinan solusi untuk mengatasinya:

Setiap elemen 'teks' menyebabkan satu string teks dirender. SVG tidak melakukan pemecahan garis otomatis atau pembungkus kata. Untuk mencapai efek beberapa baris teks, gunakan salah satu metode berikut:

  • Penulis atau paket pembuat perlu melakukan pra-komputasi jeda baris dan menggunakan beberapa elemen 'teks' (satu untuk setiap baris teks).
  • Penulis atau paket pembuat perlu melakukan pra-komputasi jeda baris dan menggunakan elemen 'teks' tunggal dengan satu atau lebih elemen anak 'tspan' dengan nilai yang sesuai untuk atribut 'x', 'y', 'dx' dan 'dy' untuk mengatur posisi awal baru untuk karakter-karakter yang memulai baris baru. (Pendekatan ini memungkinkan pemilihan teks pengguna di beberapa baris teks - lihat Pemilihan teks dan operasi clipboard.)
  • Ekspresikan teks yang akan dirender dalam namespace XML lain seperti XHTML [XHTML] yang disematkan sebaris di dalam elemen 'foreignObject'. (Catatan: semantik yang tepat dari pendekatan ini tidak sepenuhnya ditentukan saat ini.)

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

Sebagai primitif, pembungkus teks dapat disimulasikan dengan menggunakan dyatribut dan tspanelemen, dan seperti yang disebutkan dalam spesifikasi, beberapa alat dapat mengotomatisasi ini. Misalnya, di Inkscape, pilih bentuk yang Anda inginkan, dan teks yang Anda inginkan, dan gunakan Teks -> Alirkan ke Bingkai. Ini akan memungkinkan Anda untuk menulis teks Anda, dengan pembungkus, yang akan membungkus berdasarkan batas-batas bentuk. Juga, pastikan Anda mengikuti instruksi ini untuk memberi tahu Inkscape untuk menjaga kompatibilitas dengan SVG 1.1: http://wiki.inkscape.org/wiki/index.php/FAQ#What_about_flowed_text.3F

Selain itu, ada beberapa perpustakaan JavaScript yang dapat digunakan untuk mengotomatiskan pembungkusan teks secara dinamis: http://www.carto.net/papers/svg/textFlow/

Sangat menarik untuk mencatat solusi CSVG untuk membungkus bentuk ke elemen teks (mis. Lihat contoh "tombol" mereka), meskipun penting untuk menyebutkan bahwa implementasi mereka tidak dapat digunakan dalam browser: http://www.csse.monash.edu .au / ~ clm / csvg / about.html

Saya menyebutkan ini karena saya telah mengembangkan perpustakaan yang diilhami CSVG yang memungkinkan Anda untuk melakukan hal serupa dan berfungsi di browser web, meskipun saya belum merilisnya.

jbeard4
sumber
Terima kasih atas jawabannya ... tapi ACK! Tampaknya saya harus membuang svg. Salah satu hal pertama yang harus dipikirkan pengembang adalah melampirkan teks (diformat sesuai keinginan pengguna) ke bentuk! Saya akan menunggu sampai mereka melakukan aksi bersama sebelum saya melakukannya lagi. Saya akan mencoba menggambar ulang di Windows Paint dan melihat apakah itu akan melakukannya. (Kalau saja saya bisa membuat browser untuk menampilkan tabel dengan benar.)
Lady Aleena
Tentang teks yang dapat diedit di svg, Opera mendukung atribut yang dapat diedit dari SVG 1.2 Tiny, yang memungkinkan untuk mendapatkan teks yang dapat diedit dengan hanya menambahkan atribut editable="true"ke teks atau elemen textArea.
Erik Dahlström
2
@Erik, sekali lagi Opera berada di depan kurva.
jbeard4
@ Nyonya Aleena, mengapa tidak menggunakan saja solusi Inkscape yang saya sebutkan? Jika itu hanya gambar statis (mis. Tidak ada perilaku dinamis, tidak ada pembaruan konten teks secara dinamis), maka ini akan berfungsi dengan baik. Plus, itu akan scalable, yang Paint tidak akan, karena Paint menghasilkan grafik raster. Akhirnya, saya tidak percaya bahwa Paint mendukung pembungkusan teks pada level apa pun.
jbeard4
@ echo-flow Saya kehilangan kepercayaan pada program penghasil kode sejak lama sejak saya menggunakan (dan kemudian membuang) Halaman Depan Microsoft untuk menghasilkan html. Saya selalu waspada terhadap program yang menambahkan kode hak milik mereka sendiri untuk apa yang saya tulis. Halaman depan benar-benar mengacaukan html saya. Jika Anda adalah pengguna Inkscape, dapatkah Anda memberi tahu saya jika Inkscape menempatkan kode kepemilikannya sendiri di svg yang harus saya buka dan hapus setelah pembuatan svg?
Lady Aleena
23

Jawaban sebelumnya memberikan hasil yang buruk ketika menggunakan sudut bulat atau stroke-widthitu> 1. Misalnya, Anda akan mengharapkan kode berikut untuk menghasilkan persegi panjang bulat, tetapi sudut-sudutnya terpotong oleh svgkomponen induk :

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

Sebagai gantinya, saya sarankan membungkus di textdalam svgdan kemudian bersarang yang baru svgdan rectbersama - sama di dalam gelemen, seperti dalam contoh berikut:

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

Ini memperbaiki masalah kliping yang terjadi pada jawaban di atas. Saya juga menerjemahkan kelompok teks / rect menggunakan transform="translate(x,y)"atribut untuk menunjukkan bahwa ini memberikan pendekatan yang lebih intuitif untuk memposisikan teks / rect di layar.

Austin D
sumber
16

Jika Anda membuat SVG secara terprogram Anda dapat menyederhanakannya dan melakukan sesuatu seperti ini:

  <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>
Brennan Cheung
sumber
3
Shouldsn't itu dominant-baselinedan text-anchor, tidak dominantBaselinedan textAnchor?
Nathaniel M. Beaver
1
@ NathanielM.Beaver Ya, seharusnya begitu. Tangkapan yang bagus. Kode di atas adalah dari React yang sayangnya memerlukan atribut untuk diganti namanya menggunakan camelCase.
Brennan Cheung
13

Anda bisa langsung menggunakan text-anchor = "middle"properti. Saya menyarankan untuk membuat elemen svg wrapper di atas persegi panjang dan teks Anda. Dengan begitu Anda bisa menggunakan seluruh elemen menggunakan satu pemilih css. Pastikan Anda menempatkan properti teks 'x' dan 'y' sebagai 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>

zookastos
sumber
7

Blog Lengkap Lengkap: 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>

Sahil Gupta
sumber
Tidak berpusat pada firefox sementara solusi lainnya. codepen.io/anon/pen/oEByYr
tgf
Saya diuji pada Firefox 58.0.1 (64-bit), itu berfungsi. Bisakah Anda menyebutkan versi yang tidak berhasil untuk Anda?
Sahil Gupta
Firefox 58.0.2 64bit. Teksnya sedikit lebih tinggi dari yang seharusnya. Awalnya mungkin tidak mudah terlihat, tetapi jika kotak Anda sangat cocok, itu lebih jelas; coba kurangi ketinggiannya.
tgf
6

alignment-baselinebukan atribut yang tepat untuk digunakan di sini. Jawaban yang benar adalah dengan menggunakan kombinasi dominant-baseline="central"dan 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>

Regis May
sumber
2

Salah satu cara untuk memasukkan teks ke dalam persegi panjang adalah dengan memasukkan objek asing, yang merupakan DIV, di dalam objek persegi.

Dengan cara ini, teks akan menghormati batas 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>

Bruno Bastos
sumber
1

Saya punya bugger waktu mendapatkan sesuatu yang terpusat menggunakan SVG, jadi saya memutar fungsi kecil saya sendiri. semoga ini bisa membantu Anda. Perhatikan bahwa ini hanya berfungsi untuk elemen 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);
}
john ktejik
sumber
1

Untuk perataan teks secara horizontal dan vertikal, Anda mungkin ingin menggunakan gaya CSS berikut. Secara khusus, perhatikan bahwa dominant-baseline:middlemungkin salah, karena ini (biasanya) setengah jalan antara bagian atas dan garis dasar, bukan setengah jalan antara bagian atas dan bagian bawah. Juga, beberapa sumber (mis. Mozilla ) menggunakan dominant-baseline:hangingalih-alih dominant-baseline:text-before-edge. Ini juga mungkin salah, karena hangingdirancang untuk skrip Indic. Tentu saja, jika Anda menggunakan campuran bahasa Latin, India, ideograf atau apa pun, Anda mungkin perlu membaca dokumentasi .

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

Sunting: Saya baru saja memperhatikan bahwa Mozilla juga menggunakan dominant-baseline:baselineyang pasti salah: itu bahkan bukan nilai yang diakui! Saya menganggap itu default ke font default, yaitu alphabetic, jadi mereka beruntung.

Lebih banyak edit: Safari (11.1.2) mengerti text-before-edgetetapi tidak text-after-edge. Itu juga gagal ideographic. Barang luar biasa, Apple. Jadi Anda mungkin terpaksa menggunakan alphabeticdan membiarkan keturunan. Maaf.

Adam Chalcraft
sumber