คุณสามารถควบคุมวิธีการลากเส้นความกว้างของ SVG ได้หรือไม่?


204

ขณะนี้กำลังสร้างแอปพลิเคชัน SVG บนเบราว์เซอร์ ภายในแอพนี้ผู้ใช้สามารถกำหนดรูปแบบและกำหนดตำแหน่งรูปร่างต่าง ๆ รวมถึงรูปสี่เหลี่ยมผืนผ้า

เมื่อฉันนำ a ไปใช้stroke-widthกับrectองค์ประกอบการพูดของSVG 1pxจังหวะจะถูกนำไปใช้กับrectออฟเซ็ตและสิ่งที่ใส่เข้าไปในเบราว์เซอร์ต่างๆ นี่เป็นการพิสูจน์ว่าลำบากโดยเฉพาะอย่างยิ่งเมื่อฉันพยายามคำนวณความกว้างด้านนอกและตำแหน่งที่มองเห็นของรูปสี่เหลี่ยมผืนผ้าและจัดวางตำแหน่งถัดจากองค์ประกอบอื่น ๆ

ตัวอย่างเช่น:

  • Firefox เพิ่ม 1px inset (ล่างและซ้าย) และ 1px offset (บนและขวา)
  • Chrome เพิ่มสิ่งที่ใส่ 1px (บนและซ้าย) และ 1px ชดเชย (ด้านล่างและขวา)

ทางออกเดียวของฉันคือการวาดเส้นขอบที่แท้จริงด้วยตนเอง (อาจใช้pathเครื่องมือ) และวางตำแหน่งเส้นขอบด้านหลังองค์ประกอบที่ลูบ แต่วิธีนี้เป็นวิธีแก้ปัญหาที่ไม่พึงประสงค์และฉันไม่ต้องการลงไปที่ถนนเส้นนี้ถ้าเป็นไปได้

ดังนั้นคำถามของฉันคือคุณสามารถควบคุมวิธีการstroke-widthวาดSVG บนองค์ประกอบได้หรือไม่


มีแฮ็กตัวกรองที่คุณสามารถใช้เพื่อให้บรรลุสิ่งนี้ - แต่มันไม่ใช่วิธีที่ยอดเยี่ยม
Michael Mullany

2
มีpaint-orderพารามิเตอร์ที่คุณสามารถระบุได้ว่าการเติมควรจะแสดงอยู่ด้านบนของจังหวะดังนั้นคุณจะได้รับ "การจัดตำแหน่งภายนอก" ดูjsfiddle.net/hne0kyLg/1
Ivan Kuckir

พบวิธีที่จะทำเช่นนี้ใช้ CSS แบบ 'outline-' แอตทริบิวต์: codepen.io/badcat/pen/YVzmYY ไม่แน่ใจว่าสิ่งใดที่สนับสนุนสิ่งนี้ในเบราว์เซอร์ แต่อาจมีประโยชน์
bplmp

คำตอบ:


375

ไม่ได้คุณไม่สามารถระบุได้ว่าการลากเส้นนั้นถูกดึงเข้าด้านในหรือด้านนอกองค์ประกอบ ฉันทำข้อเสนอไปยังคณะทำงาน SVG สำหรับฟังก์ชั่นนี้ในปี 2003 แต่ไม่ได้รับการสนับสนุน (หรือการสนทนา)

SVG เสนอตัวอย่างแบบสโตรคตำแหน่งจาก phrogz.net/SVG/stroke-location.svg

ตามที่ระบุไว้ในข้อเสนอ

  • คุณสามารถบรรลุผลลัพธ์ภาพเดียวกันกับ "ข้างใน" โดยเพิ่มความกว้างของเส้นขีดของคุณเป็นสองเท่าจากนั้นใช้เส้นทางรูปวาดเพื่อคลิปวัตถุด้วยตัวเองและ
  • คุณสามารถบรรลุผลลัพธ์ภาพเดียวกันกับ 'ข้างนอก' โดยเพิ่มความกว้างของเส้นขีดเป็นสองเท่าจากนั้นซ้อนทับสำเนาที่ไม่มีจังหวะของวัตถุที่อยู่ด้านบนของตัวเอง

แก้ไข : คำตอบนี้อาจผิดในอนาคต คุณควรจะได้ผลลัพธ์เหล่านี้โดยใช้เอฟเฟกต์ Vector SVGโดยการรวมveStrokePathกับveIntersect(สำหรับ 'ภายใน') หรือกับveExclude(สำหรับ 'ภายนอก) อย่างไรก็ตาม Vector Effects ยังคงเป็นโมดูลร่างที่ใช้งานได้โดยไม่มีการใช้งานที่ฉันยังหาไม่เจอ

แก้ไข 2 : ข้อกำหนดคุณสมบัติฉบับร่าง SVG 2 มีstroke-alignmentคุณสมบัติ (พร้อม center | inside | นอกค่าที่เป็นไปได้) คุณสมบัตินี้อาจทำให้เป็น UAs ในที่สุด

แก้ไข 3 : ตลกขบขันและ dissapointingly กลุ่มทำงาน SVG ได้ถูกลบออกstroke-alignmentจาก SVG 2. คุณสามารถเห็นบางส่วนของความกังวลที่อธิบายหลังจากร้อยแก้วที่นี่


2
คิดว่าอาจเป็นกรณี เพื่อแก้ปัญหานี้ฉันได้เขียนฟังก์ชัน wrapper สำหรับ getBBox () ที่เรียกว่า getStrokedBBox () wrapper นี้ส่งคืน BBox ตามวิธีที่เบราว์เซอร์นำไปใช้กับการแทรกและการชดเชยรูปร่าง มันไม่สมบูรณ์แบบ (ต้องตรวจสอบกับเบราว์เซอร์เวอร์ชันล่าสุดเสมอ) แต่จะให้ความกว้างของรูปร่างภายนอกอย่างถูกต้องในตอนนี้
Steve

6
@ Phrogz บางทีเราจะเห็นมันก่อน 10 ปีผ่านไป svgwg.org/svg2-draft/painting.html#SpecifyingStrokePaintคำอธิบายประกอบ
frenchone

1
ในที่สุดมันก็มาถึงที่นี่! ฉันหนึ่งได้รับการกระตุ้นอย่างจริงจังสำหรับคุณสมบัตินี้ที่จะมาถึง
mnsth

ฉันสร้างสคริปต์ svg-contourสำหรับติดตามรูปร่างของ SVGGeometryElement ใด ๆ ที่สามารถใช้เป็นวิธีแก้ปัญหาของการจัดแนวเส้นขีดสำหรับผู้ที่สนใจคุณสามารถหาคำอธิบายได้ที่นี่
maioman

2
มีหน้าใดบ้างที่ฉันสามารถโหวตข้อเสนอได้ ขอบคุณ ดูเหมือนไร้สาระมันไม่ได้รับการสนับสนุน
mahish

57

UPDATE:stroke-alignmentแอตทริบิวต์อยู่บน 1 เมษายน 2015 ย้ายไปอยู่ที่สเปคที่เรียกว่าใหม่ที่สมบูรณ์แบบจังหวะ

ในฐานะที่เป็นร่าง SVG 2.0 Editor ของวันที่ 26 กุมภาพันธ์ 2558 (และอาจเป็นตั้งแต่วันที่ 13 กุมภาพันธ์ )stroke-alignmentคุณสมบัติเป็นปัจจุบันด้วยค่าinner, center (เริ่มต้น)outerและ

มันดูเหมือนว่าจะทำงานในลักษณะเช่นเดียวกับstroke-locationสถานที่ให้บริการที่เสนอโดย @Phrogz และต่อมาข้อเสนอแนะstroke-position สถานที่ให้บริการนี้ได้รับการวางแผนมาตั้งแต่อย่างน้อย 2011 แต่นอกเหนือจากบันทึกย่อที่กล่าวว่า

SVG 2 จะต้องมีวิธีการระบุตำแหน่งจังหวะ

มันไม่เคยมีรายละเอียดในสเป็คเหมือนรอการตัดบัญชี - จนถึงตอนนี้ดูเหมือนว่า

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

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


1
stroke-alignmentระบุไว้ใน SVG Strokes, ร่างการทำงานของ W3C ในขณะเดียวกันร่างของบรรณาธิการ SVG 2 W3C กล่าวว่าคุณสมบัติการระบุตำแหน่งแบบสโตรกจะอยู่ในข้อมูลจำเพาะ SVG ที่svgwg.org/svg2-draft/painting.html#SpecifyingStrokePaintแต่สเป็คนั้นได้มาถึงสถานะการแนะนำ W3C แล้ว ในข้อมูลจำเพาะนอกเหนือจากลิงก์ไปยังstroke-positionข้อเสนอทำให้ดูเหมือนว่าจะไม่เป็นเช่นนั้น
Patrick Dark

47

ฉันพบวิธีง่ายๆซึ่งมีข้อ จำกัด เล็กน้อย แต่ได้ผลกับฉัน:

  • กำหนดรูปร่างใน defs
  • กำหนดเส้นทางคลิปอ้างอิงรูปร่าง
  • ใช้มันแล้วลากเส้นเป็นสองเท่าด้วยด้านนอกถูกตัด

นี่คือตัวอย่างการทำงาน:

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


2
คำตอบที่ดีที่สุดที่ฉันพบ
Ed Kolosovsky

1
ทางออกที่ฉลาด ขอบคุณ
วินซ์หยวน

สิ่งนี้ควรได้รับการยอมรับว่าเป็นคำตอบ การใช้ <defs> และ <use> เป็นโซลูชันที่หรูหราที่สุดที่มีอยู่ในปัจจุบัน
Nyerguds

ทางออกที่ดี คุณจะสลับมันอย่างไรเพื่อให้เป็นจังหวะนอก?
Lucent

ทำไมระดับสูงสุด<use>? ทำไมไม่เพียงแค่ใส่พา ธ และพา ธ คลิปเข้าไปโดยตรง <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>นี้ทำงานได้ดี: (ใช่นี่คือเหตุผลที่ถูกต้องสมบูรณ์และถูกต้อง SVG และจะแสดงผลอย่างถูกต้องทุกที่ไม่ต้องกลัวการเรียกซ้ำ)
Chris Morgan

30

คุณสามารถใช้ CSS เพื่อจัดลำดับของสโตรกและเติม นั่นคือจังหวะก่อนแล้วจึงเติมวินาทีและรับเอฟเฟกต์ที่ต้องการ

MDN เมื่อpaint-order: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/paint-order

รหัส CSS:

paint-order: stroke;

มันสมบูรณ์แบบ! ขอบคุณสำหรับการแบ่งปัน
BrunoFenzl

1
ดูเหมือนว่าเอกสารที่เชื่อมโยงไม่ได้ระบุว่าจังหวะอยู่ข้างในข้างนอกหรืออยู่กึ่งกลาง? คุณช่วยอธิบายวิธีการควบคุมจังหวะการชักได้ไหม? ขอบคุณ
Crashalot

2
จังหวะนั้นอยู่ที่กึ่งกลางเช่นเดียวกับที่เคยเป็นมา - แต่ถ้าคุณมีการเติมที่เป็นของแข็งผลของการปรับลำดับสีเพื่อทาสีจังหวะแรกคือการที่ครึ่งด้านในของเส้นขีดถูกทาสีทับซึ่งมีผลเหมือนกับการวาด จังหวะครึ่งนอกเหมือนกับหนา
Chris Morgan

7

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

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

        }

    };

6

ในขณะที่คนข้างต้นตั้งข้อสังเกตว่าคุณจะต้องคำนวณการชดเชยให้กับพิกัดเส้นทางของจังหวะหรือความกว้างเป็นสองเท่าจากนั้นปิดบังด้านใดด้านหนึ่งหรืออื่น ๆ เพราะ SVG ไม่เพียง แต่ไม่สนับสนุนการจัดแนวจังหวะของ Illustrator เท่านั้น .

ข้อกำหนดสำหรับจังหวะในของ Adobe PostScript คู่มือการใช้งานรัฐฉบับที่ 2: " 4.5.1 Stroking: จังหวะประกอบการวาดเส้นของความหนาบางตามเส้นทางปัจจุบันสำหรับแต่ละส่วนตรงหรือโค้งในเส้นทาง. จังหวะวาดเส้นที่เป็นศูนย์กลางใน ส่วนที่มีด้านข้างขนานกับส่วน " (เน้นพวกเขา)

สเปคที่เหลือไม่มีคุณสมบัติในการชดเชยตำแหน่งของสาย เมื่อ Illustrator ช่วยให้คุณสามารถจัดตำแหน่งภายในหรือภายนอกก็จะทำการคำนวณออฟเซ็ตของเส้นทางที่แท้จริง พิกัดเส้นทางในเอกสาร. ai เป็นข้อมูลอ้างอิงไม่ใช่สิ่งที่ได้รับการแปลงหรือส่งออกเป็นรูปแบบสุดท้าย

เนื่องจากรูปแบบดั้งเดิมของ Inkscape คือ spec SVG จึงไม่สามารถให้ฟีเจอร์ที่ spec ขาดได้


4

นี่คือรอบการทำงานภายในเป้น rectการใช้และsymboluse

ตัวอย่าง : 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>

หมายเหตุ: ให้แน่ใจว่าจะเข้ามาแทนที่?ในuseที่มีค่าจริง

ความเป็นมา : เหตุผลที่ทำให้งานนี้เป็นเพราะสัญลักษณ์สร้างวิวพอร์ตใหม่โดยแทนที่symbolด้วยsvgและสร้างองค์ประกอบใน DOM เงา นี้svgของ DOM เงามีการเชื่อมโยงแล้วลงในปัจจุบันของคุณSVGองค์ประกอบ โปรดทราบว่าsvgs สามารถซ้อนกันได้และทุก ๆ รายการsvgสร้างวิวพอร์ตใหม่ซึ่งตัดทุกอย่างที่ทับซ้อนรวมถึงเส้นขอบที่ทับซ้อนกัน สำหรับภาพรวมรายละเอียดเพิ่มเติมเกี่ยวกับสิ่งที่เกิดขึ้นอ่านบทความที่น่าอัศจรรย์นี้โดย Sara Soueidan


2

ฉันไม่ทราบว่าจะมีประโยชน์เพียงใด แต่ในกรณีของฉันฉันเพิ่งสร้างวงกลมอื่นที่มีเส้นขอบเท่านั้นและวางไว้ที่ "ภายใน" รูปร่างอื่น


0

วิธีแก้ปัญหาที่เป็นไปได้ (สกปรก) คือการใช้รูปแบบ

นี่คือตัวอย่างที่มีรูปสามเหลี่ยมด้านในลูบ:

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>

0

การแก้ปัญหาจากXavier Hoของการเพิ่มความกว้างของจังหวะและการเปลี่ยนลำดับสีนั้นยอดเยี่ยมเป็นสองเท่าแม้จะใช้ได้เฉพาะในกรณีที่การเติมเป็นสีทึบ แต่ไม่มีความโปร่งใส

ฉันได้พัฒนาวิธีการอื่น ๆ ที่ซับซ้อนกว่า แต่ใช้ได้กับทุกสิ่ง มันยังทำงานในรูปไข่หรือเส้นทาง (ในภายหลังมีบางกรณีมุมที่มีพฤติกรรมแปลก ๆ เช่นเส้นทางเปิดที่ข้ามตัวเอง แต่ไม่มาก)

เคล็ดลับคือการแสดงรูปร่างในสองชั้น หนึ่งอันที่ไม่มีสโตรก (เติมเต็มเท่านั้น) และอีกอันที่มีสโตรกที่ความกว้างสองเท่า (เติมแบบใส) และผ่านหน้ากากที่แสดงทั้งรูปร่าง แต่ซ่อนรูปร่างดั้งเดิมโดยไม่ต้องสโตรก

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