Bisakah Anda mengontrol bagaimana langkah-lebar SVG ditarik?

204

Saat ini sedang membangun aplikasi SVG berbasis browser. Dalam aplikasi ini, berbagai bentuk dapat ditata dan diposisikan oleh pengguna, termasuk persegi panjang.

Ketika saya menerapkan elemen stroke-widthSVG rectkatakan 1px, stroke diterapkan ke rectoffset dan inset dengan cara yang berbeda oleh browser yang berbeda. Ini terbukti merepotkan, terutama ketika saya mencoba menghitung lebar luar dan posisi visual persegi panjang dan menempatkannya di sebelah elemen lain.

Sebagai contoh:

  • Firefox menambahkan inset 1px (bawah dan kiri), dan offset 1px (atas dan kanan)
  • Chrome menambahkan inset 1px (atas dan kiri), dan offset 1px (bawah dan kanan)

Satu-satunya solusi saya sejauh ini adalah dengan menggambar batas aktual sendiri (mungkin dengan pathalat) dan memposisikan perbatasan di belakang elemen yang dibelai. Tetapi solusi ini merupakan solusi yang tidak menyenangkan, dan saya lebih suka untuk tidak turun jalan ini jika mungkin.

Jadi pertanyaan saya adalah, dapatkah Anda mengontrol bagaimana SVG stroke-widthdigambarkan pada elemen?

Steve
sumber
ada filter hack yang dapat Anda gunakan untuk mencapai hal ini - tetapi ini bukan solusi yang bagus
Michael Mullany
2
Ada paint-orderparameter, di mana Anda dapat menentukan, bahwa isian harus diberikan di atas stroke, sehingga Anda akan mendapatkan "penyelarasan luar", lihat jsfiddle.net/hne0kyLg/1
Ivan Kuckir
Menemukan cara untuk melakukan ini menggunakan atribut 'outline-' css: codepen.io/badcat/pen/YVzmYY . Tidak yakin apa dukungan untuk ini di seluruh browser, tetapi mungkin bermanfaat.
bplmp

Jawaban:

375

Tidak, Anda tidak bisa menentukan apakah goresannya digambar di dalam atau di luar elemen. Saya membuat proposal ke kelompok kerja SVG untuk fungsi ini pada tahun 2003, tetapi tidak menerima dukungan (atau diskusi).

SVG mengusulkan contoh lokasi-stroke, dari phrogz.net/SVG/stroke-location.svg

Seperti yang saya catat dalam proposal,

  • Anda dapat mencapai hasil visual yang sama dengan "di dalam" dengan menggandakan lebar goresan Anda dan kemudian menggunakan jalur kliping untuk klip objek ke dirinya sendiri, dan
  • Anda dapat mencapai hasil visual yang sama dengan 'luar' dengan menggandakan lebar goresan dan kemudian menirukan salinan tanpa goresan objek di atasnya.

Sunting : Jawaban ini mungkin salah di masa mendatang. Seharusnya dimungkinkan untuk mencapai hasil ini menggunakan Efek Vektor SVG , dengan menggabungkan veStrokePathdengan veIntersect(untuk 'dalam') atau dengan veExclude(untuk 'luar'). Namun, Vector Effects masih merupakan modul konsep kerja tanpa implementasi yang belum dapat saya temukan.

Sunting 2 : Spesifikasi konsep SVG 2 mencakup stroke-alignmentproperti (dengan nilai tengah | di dalam | di luar kemungkinan). Properti ini akhirnya bisa menjadi UA.

Sunting 3 : Yang mengherankan dan mengecewakan, kelompok kerja SVG telah dihapus stroke-alignmentdari SVG 2. Anda dapat melihat beberapa masalah yang dijelaskan setelah prosa di sini .

Phrogz
sumber
2
Pikir itu mungkin terjadi. Untuk mengatasi ini saya telah menulis fungsi wrapper untuk getBBox () yang disebut getStrokedBBox (). Wrapper ini mengembalikan BBox sesuai dengan cara browser menerapkan stroke ke inset dan offset bentuk. Itu tidak sempurna (perlu terus memeriksa versi browser terbaru) tetapi itu secara akurat memberikan bentuk lebar luar untuk saat ini.
Steve
6
@ Phrogz mungkin kita akan melihatnya sebelum 10 tahun berlalu. svgwg.org/svg2-draft/painting.html#SpesifikasiStrokePaint annotation
frenchone
1
Akhirnya di sini! Saya, misalnya, benar-benar mendesak agar properti ini tiba.
mnsth
Saya membuat skrip kontur svg untuk melacak kontur setiap SVGGeometryElement yang dapat digunakan sebagai solusi penyelesaian perataan-stroke, bagi siapa pun yang tertarik, Anda dapat menemukan deskripsi di sini
maioman
2
Apakah ada halaman di mana saya dapat mengambil suara untuk proposal? Terima kasih. Sepertinya konyol itu tidak didukung.
Mahish
57

UPDATE: The stroke-alignmentAtribut pada 1 April 2015 pindah ke spec yang disebut Strokes SVG benar-benar baru .

Pada Draft Editor SVG 2.0 tanggal 26 Februari 2015 (dan mungkin sejak 13 Februari) ),itu stroke-alignment properti hadirdengan nilai inner, center (default) danouter .

Tampaknya bekerja dengan cara yang sama dengan stroke-locationproperti yang diusulkan oleh @Phrogz dan stroke-positionsaran selanjutnya . Properti ini telah direncanakan sejak setidaknya 2011, tetapi terlepas dari anotasi yang mengatakan

SVG 2 harus mencakup cara untuk menentukan posisi stroke

, tidak pernah dirinci dalam spesifikasi karena ditangguhkan - sampai sekarang, tampaknya.

Tidak ada browser yang mendukung properti ini, atau, sejauh yang saya tahu, salah satu fitur SVG 2 baru, namun mudah-mudahan mereka akan segera setelah spesifikasi matang. Ini adalah properti yang secara pribadi saya ingin miliki, dan saya sangat senang akhirnya ada di spec.

Tampaknya ada beberapa masalah tentang bagaimana properti harus berperilaku di jalur terbuka serta loop. Masalah-masalah ini, kemungkinan besar, akan memperpanjang implementasi lintas browser. Namun, saya akan memperbarui jawaban ini dengan informasi baru ketika browser mulai mendukung properti ini.

mnsth
sumber
1
stroke-alignmentdispesifikasikan dalam SVG Strokes, W3C Working Draft. Sementara itu, Draft Editor SVG 2 W3C mengatakan bahwa properti penentuan posisi stroke harus dalam spesifikasi SVG di svgwg.org/svg2-draft/painting.html#SpecifyingStrokePaint , tetapi spec tersebut telah mencapai Status Rekomendasi Calon W3C dan tidak ada properti seperti itu. di spec selain dari tautan ke stroke-positionproposal, membuatnya tampak seperti itu tidak akan terjadi.
Patrick Dark
47

Saya menemukan cara mudah, yang memiliki beberapa batasan, tetapi berhasil bagi saya:

  • mendefinisikan bentuk dalam defs
  • menentukan jalur klip yang mereferensikan bentuk
  • gunakan dan gandakan goresan dengan saat bagian luar terpotong

Berikut ini contoh kerjanya:

<svg width="240" height="240" viewBox="0 0 1024 1024">
<defs>
	<path id="ld" d="M256,0 L0,512 L384,512 L128,1024 L1024,384 L640,384 L896,0 L256,0 Z"/>
	<clipPath id="clip">
		<use xlink:href="#ld"/>
	</clipPath>
</defs>
<g>
	<use xlink:href="#ld" stroke="#0081C6" stroke-width="160" fill="#00D2B8" clip-path="url(#clip)"/>
</g>
</svg>

Jorg Janke
sumber
2
Jawaban terbaik yang saya temukan
Ed Kolosovsky
1
Solusi cerdas. Terima kasih
Vince Yuan
Ini harus diterima sebagai jawaban. Menggunakan <defs> dan <use> adalah solusi paling elegan yang tersedia saat ini.
Nyerguds
Solusi bagus Bagaimana Anda membalikkannya untuk membuat pukulan luar?
Lucent
Mengapa tingkat atas <use>? Mengapa tidak langsung menempatkan path dan clip path? Ini bekerja dengan baik: <svg width="240" height="240" viewBox="0 0 1024 1024"> <path id="ld" d="M256,0 L0,512 L384,512 L128,1024 L1024,384 L640,384 L896,0 L256,0 Z" clip-path="url(#clip)" stroke="#0081C6" stroke-width="160" fill="#00d2b8"/> <clipPath id="clip"> <use xlink:href="#ld"/> </clipPath> </svg>. (Ya, ini sepenuhnya masuk akal dan benar SVG dan akan membuat dengan benar di mana-mana. Jangan takut rekursi.)
Chris Morgan
30

Anda dapat menggunakan CSS untuk memberi gaya urutan goresan dan isi. Artinya, stroke dulu lalu isi kedua, dan dapatkan efek yang diinginkan.

MDN di paint-order: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/paint-order

Kode CSS:

paint-order: stroke;
Xavier Ho
sumber
Ini sempurna! Terima kasih telah berbagi
BrunoFenzl
1
Dokumentasi yang ditautkan tampaknya tidak menunjukkan apakah stroke ada di dalam, di luar, atau di tengah? Bisakah Anda mengklarifikasi cara mengontrol di mana stroke ditarik? Terima kasih.
Crashalot
2
Stroke terpusat, seperti biasanya — tetapi jika Anda memiliki isian padat, efek menyesuaikan urutan cat untuk mengecat goresan pertama adalah bahwa separuh bagian dalam coretan dilukis, yang memiliki efek yang sama seperti menggambar sebuah Stroke setengah tebal di luar.
Chris Morgan
7

Berikut adalah fungsi yang akan menghitung berapa banyak piksel yang perlu Anda tambahkan - menggunakan goresan yang diberikan - ke atas, kanan, bawah dan kiri, semuanya berdasarkan browser:

var getStrokeOffsets = function(stroke){

        var strokeFloor =       Math.floor(stroke / 2),                                                                 // max offset
            strokeCeil =        Math.ceil(stroke / 2);                                                                  // min offset

        if($.browser.mozilla){                                                                                          // Mozilla offsets

            return {
                bottom:     strokeFloor,
                left:       strokeFloor,
                top:        strokeCeil,
                right:      strokeCeil
            };

        }else if($.browser.webkit){                                                                                     // WebKit offsets

            return {
                bottom:     strokeCeil,
                left:       strokeFloor,
                top:        strokeFloor,
                right:      strokeCeil
            };

        }else{                                                                                                          // default offsets

            return {
                bottom:     strokeCeil,
                left:       strokeCeil,
                top:        strokeCeil,
                right:      strokeCeil
            };

        }

    };
Steve
sumber
6

Seperti yang dicatat oleh orang-orang di atas, Anda harus menghitung ulang offset ke koordinat path stroke atau menggandakan lebar dan kemudian menutupi satu sisi atau yang lain, karena SVG tidak hanya secara alami mendukung penyelarasan stroke Illustrator, tetapi PostScript tidak juga .

Spesifikasi untuk stroke dalam Manual Adobe PostScript negara edisi ke-2: " 4.5.1 Sambil membelai: The Stroke Operator menarik garis dari beberapa ketebalan sepanjang jalan saat Untuk setiap segmen lurus atau melengkung di jalan,. Stroke menarik garis yang berpusat pada segmen dengan sisi yang sejajar dengan segmen. " (Penekanan mereka)

Spesifikasi lainnya tidak memiliki atribut untuk mengimbangi posisi garis. Ketika Illustrator memungkinkan Anda menyelaraskan di dalam atau di luar, itu menghitung ulang offset jalur yang sebenarnya (karena itu masih lebih murah secara komputasi daripada mencetak lebih dari lalu menutupi). Koordinat jalur dalam dokumen .ai adalah referensi, bukan apa yang di-raster atau diekspor ke format final.

Karena format asli Inkscape adalah spec SVG, ia tidak dapat menawarkan fitur yang tidak dimiliki spec.

pengguna1574945
sumber
4

Berikut ini adalah pekerjaan untuk menggunakan batas dalam dan .rectsymboluse

Contoh : https://jsbin.com/yopemiwame/edit?html,output

SVG :

<svg>
  <symbol id="inner-border-rect">
    <rect class="inner-border" width="100%" height="100%" style="fill:rgb(0,255,255);stroke-width:10;stroke:rgb(0,0,0)">
  </symbol>
  ...
  <use xlink:href="#inner-border-rect" x="?" y="?" width="?" height="?">
</svg>

Catatan: Pastikan untuk mengganti ?in usedengan nilai real.

Latar Belakang : Alasan mengapa ini bekerja adalah karena simbol menetapkan viewport baru dengan mengganti symboldengan svgdan menciptakan unsur dalam DOM bayangan. Ini svgdari bayangan DOM kemudian dihubungkan ke SVGelemen Anda saat ini . Perhatikan bahwa svgs dapat disarangkan dan setiap svgmembuat viewport baru, yang memotong semua yang tumpang tindih, termasuk perbatasan yang tumpang tindih. Untuk ikhtisar yang lebih terperinci tentang apa yang terjadi, baca artikel fantastis ini oleh Sara Soueidan.

F Lekscha
sumber
2

Saya tidak tahu bagaimana itu akan membantu tetapi dalam kasus saya, saya hanya membuat lingkaran lain dengan perbatasan saja dan meletakkannya "di dalam" bentuk lainnya.

Saksi
sumber
0

Solusi (kotor) yang mungkin adalah dengan menggunakan pola,

di sini adalah contoh dengan segitiga di dalam:

https://jsfiddle.net/qr3p7php/5/

<style>
#triangle1{
  fill: #0F0;
  fill-opacity: 0.3;
  stroke: #000;
  stroke-opacity: 0.5;
  stroke-width: 20;
}
#triangle2{
  stroke: #f00;
  stroke-opacity: 1;
  stroke-width: 1;
}    
</style>

<svg height="210" width="400" >
    <pattern id="fagl" patternUnits="objectBoundingBox" width="2" height="1" x="-50%">
        <path id="triangle1" d="M150 0 L75 200 L225 200 Z">
    </pattern>    
    <path id="triangle2" d="M150 0 L75 200 L225 200 Z" fill="url(#fagl)"/>
</svg>
maks-lt
sumber
0

Solusi dari Xavier Ho untuk menggandakan lebar goresan dan mengubah urutan cat adalah brilian, meskipun hanya berfungsi jika isiannya berwarna solid, tanpa transparansi.

Saya telah mengembangkan pendekatan lain, lebih rumit tetapi bekerja untuk mengisi apa pun. Ini juga bekerja di elips atau jalur (dengan yang kemudian ada beberapa kasus sudut dengan perilaku aneh, misalnya jalur terbuka yang melintasi diri mereka sendiri, tetapi tidak banyak).

Caranya adalah dengan menampilkan bentuk dalam dua lapisan. Satu tanpa goresan (hanya isian), dan satunya lagi dengan goresan dengan lebar ganda (isian transparan) dan melewati topeng yang menunjukkan seluruh bentuk, tetapi menyembunyikan bentuk asli tanpa goresan.

  <svg width="240" height="240" viewBox="0 0 1024 1024">
  <defs>
    <path id="ld" d="M256,0 L0,512 L384,512 L128,1024 L1024,384 L640,384 L896,0 L256,0 Z"/>
    <mask id="mask">
      <use xlink:href="#ld" stroke="#FFFFFF" stroke-width="160" fill="#FFFFFF"/>
      <use xlink:href="#ld" fill="#000000"/>
    </mask>
  </defs>
  <g>
    <use xlink:href="#ld" fill="#00D2B8"/>
    <use xlink:href="#ld" stroke="#0081C6" stroke-width="160" fill="red" mask="url(#mask)"/>
  </g>
  </svg>
hirunatan
sumber