Bạn có thể kiểm soát chiều rộng hành trình của SVG được vẽ không?


204

Hiện đang xây dựng một ứng dụng SVG dựa trên trình duyệt. Trong ứng dụng này, nhiều hình dạng khác nhau có thể được tạo kiểu và định vị bởi người dùng, bao gồm cả hình chữ nhật.

Khi tôi áp dụng một yếu tố nói stroke-widthSVG , đột quỵ được áp dụng cho phần bù và chèn theo các cách khác nhau bởi các trình duyệt khác nhau. Điều này đang chứng tỏ là rắc rối, đặc biệt là khi tôi cố gắng tính chiều rộng bên ngoài và vị trí trực quan của một hình chữ nhật và đặt nó bên cạnh các yếu tố khác.rect1pxrect

Ví dụ:

  • Firefox thêm hình ảnh 1px (dưới cùng và bên trái) và bù 1px (trên cùng và bên phải)
  • Chrome thêm hình ảnh 1px (trên cùng và bên trái) và bù 1px (dưới cùng và bên phải)

Giải pháp duy nhất của tôi cho đến nay là tự vẽ các đường viền thực tế (có thể bằng pathcông cụ) và định vị các đường viền phía sau phần tử được vuốt. Nhưng giải pháp này là một cách giải quyết khó chịu và tôi không muốn đi theo con đường này nếu có thể.

Vì vậy, câu hỏi của tôi là, bạn có thể kiểm soát cách một SVG stroke-widthđược vẽ trên các yếu tố không?


có những cách hack bộ lọc mà bạn có thể sử dụng để đạt được điều này - nhưng đó không phải là một giải pháp tuyệt vời
Michael Mullany

2
Có một paint-ordertham số, nơi bạn có thể chỉ định, rằng phần điền sẽ được hiển thị trên đỉnh của nét, vì vậy bạn sẽ nhận được "căn chỉnh bên ngoài", xem jsfiddle.net/hne0kyLg/1
Ivan Kuckir

Tìm thấy một cách để làm điều này bằng cách sử dụng các thuộc tính css 'phác thảo- ': codepen.io/badcat/pen/YVzmYY . Không chắc chắn những gì hỗ trợ cho điều này trên các trình duyệt, nhưng có thể hữu ích.
bplmp

Câu trả lời:


375

Không, bạn không thể chỉ định liệu nét vẽ được vẽ bên trong hay bên ngoài một phần tử. Tôi đã đưa ra một đề xuất cho nhóm làm việc SVG cho chức năng này vào năm 2003, nhưng nó không nhận được sự hỗ trợ (hoặc thảo luận).

SVG đề xuất ví dụ về vị trí đột quỵ, từ phrogz.net/SVG/stroke-location.svg

Như tôi đã lưu ý trong đề xuất,

  • bạn có thể đạt được kết quả hình ảnh tương tự như "bên trong" bằng cách nhân đôi chiều rộng nét của mình và sau đó sử dụng đường cắt để cắt đối tượng với chính nó và
  • bạn có thể đạt được kết quả hình ảnh tương tự như 'bên ngoài' bằng cách nhân đôi chiều rộng của nét vẽ và sau đó phủ lên một bản sao không có nét của đối tượng trên chính nó.

Chỉnh sửa : Câu trả lời này có thể sai trong tương lai. Có thể đạt được các kết quả này bằng cách sử dụng Hiệu ứng vectơ SVG , bằng cách kết hợp veStrokePathvới veIntersect(cho 'bên trong') hoặc với veExclude(cho 'bên ngoài). Tuy nhiên, Vector Effects vẫn là một mô-đun dự thảo đang hoạt động mà không có triển khai nào mà tôi chưa thể tìm thấy.

Chỉnh sửa 2 : Đặc tả dự thảo SVG 2 bao gồm một thuộc stroke-alignmenttính (có tâm | bên trong | bên ngoài các giá trị có thể). Khách sạn này cuối cùng có thể vào UAs.

Chỉnh sửa 3 : Thật thú vị và thất vọng, nhóm làm việc SVG đã xóa stroke-alignmentkhỏi SVG 2. Bạn có thể thấy một số mối quan tâm được mô tả sau văn xuôi ở đây .


2
Nghĩ rằng đó có thể là trường hợp. Để giải quyết điều này, tôi đã viết một hàm bao bọc cho getBBox () được gọi là getStrokedBBox (). Trình bao bọc này trả về BBox theo cách trình duyệt áp dụng các nét vào hình nhỏ và phần bù của hình. Nó không hoàn hảo (cần tiếp tục kiểm tra các phiên bản trình duyệt mới nhất) nhưng hiện tại nó cung cấp chính xác hình dạng chiều rộng bên ngoài.
Steve

6
@Phrogz có lẽ chúng ta sẽ thấy nó trước khi 10 năm trôi qua. svgwg.org/svg2-draft/painting.html#SpecifyingStrokePaint chú thích
tiếng Pháp

1
Cuối cùng cũng đến rồi! Tôi, cho một, đã thực sự thúc giục cho tài sản này đến.
mnsth

Tôi đã tạo một kịch bản đường viền svg Svg để theo dõi đường viền của bất kỳ SVGGeometryE bổ sung nào có thể được sử dụng như một cách giải quyết của căn chỉnh nét, cho bất kỳ ai quan tâm bạn có thể tìm thấy một mô tả ở đây
maioman

2
Có trang nào mà tôi có thể nâng cấp cho đề xuất không? Cảm ơn. Có vẻ vô lý nó không được hỗ trợ.
mahish

57

UPDATE: Các stroke-alignmentthuộc tính là vào ngày 01 tháng 4 năm 2015 chuyển đến một spec hoàn toàn mới gọi là SVG Strokes .

Kể từ Dự thảo của Biên tập viên SVG 2.0 ngày 26 tháng 2 năm 2015 (và có thể kể từ ngày 13 tháng 2 ),các stroke-alignmenttài sản là hiện tạivới các giá trị inner, center (mặc định)outer.

Nó dường như hoạt động giống như stroke-locationtài sản được đề xuất bởi @Phrogz và đề xuất saustroke-position . Khách sạn này đã được lên kế hoạch từ ít nhất là năm 2011, nhưng ngoài một chú thích cho biết

SVG 2 sẽ bao gồm một cách để xác định vị trí đột quỵ

, nó chưa bao giờ được chi tiết trong thông số kỹ thuật như nó là hoãn lại - cho đến bây giờ, có vẻ như vậy.

Không có trình duyệt nào hỗ trợ thuộc tính này, hoặc, theo như tôi biết, bất kỳ tính năng SVG 2 mới nào, nhưng hy vọng chúng sẽ sớm xuất hiện khi thông số kỹ thuật đáo hạn. Đây là một tài sản mà cá nhân tôi đã mong muốn có, và tôi thực sự rất vui vì cuối cùng nó cũng có trong thông số kỹ thuật.

Dường như có một số vấn đề như làm thế nào để tài sản nên hành xử trên các đường dẫn mở cũng như các vòng lặp. Những vấn đề này, rất có thể, sẽ kéo dài việc triển khai trên các trình duyệt. Tuy nhiên, tôi sẽ cập nhật câu trả lời này với thông tin mới khi các trình duyệt bắt đầu hỗ trợ thuộc tính này.


1
stroke-alignmentđược chỉ định trong SVG Strokes, Dự thảo làm việc W3C. Trong khi đó, Dự thảo của Biên tập viên SVG 2 W3C nói rằng một thuộc tính định vị đột quỵ sẽ nằm trong thông số SVG tại svgwg.org/svg2-draft/painting.html#SpecifyingStrokePaint , nhưng thông số đó đã đạt đến trạng thái Đề xuất của Ứng viên W3C và không có thuộc tính đó. trong thông số kỹ thuật ngoài liên kết đến stroke-positionđề xuất, có vẻ như điều đó sẽ không xảy ra.
Patrick Dark

47

Tôi tìm thấy một cách dễ dàng, có một vài hạn chế, nhưng hiệu quả với tôi:

  • xác định hình dạng trong defs
  • xác định đường dẫn clip tham chiếu hình
  • sử dụng nó và nhân đôi đột quỵ khi bên ngoài bị cắt bớt

Dưới đây là một ví dụ hoạt động:

<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
Câu trả lời hay nhất tôi tìm thấy
Ed Kolosovsky

1
Giải pháp thông minh. Cảm ơn
Vince Yuan

Điều này nên được chấp nhận như là câu trả lời. Sử dụng <defs> và <use> là giải pháp thanh lịch nhất hiện có.
Nyerguds

Giải pháp tốt đẹp. Làm thế nào bạn sẽ đảo ngược nó để thực hiện một đột quỵ bên ngoài?
Lucent

Tại sao cấp cao nhất <use>? Tại sao không chỉ đặt đường dẫn và clip đường dẫn trực tiếp? Điều này hoạt động tốt : <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>. (Vâng, điều này hoàn toàn hợp lý và đúng SVG và sẽ hiển thị chính xác ở mọi nơi. Không sợ đệ quy.)
Chris Morgan

30

Bạn có thể sử dụng CSS để định kiểu thứ tự nét và điền. Đó là, đột quỵ trước và sau đó điền vào thứ hai, và có được hiệu quả mong muốn.

MDN trên paint-order: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribution/paint-order

Mã CSS:

paint-order: stroke;

Đây là hoàn hảo! Cảm ơn bạn đã chia sẻ
BrunoFenzl

1
Các tài liệu được liên kết dường như không chỉ ra liệu đột quỵ là bên trong, bên ngoài hay trung tâm? Bạn có thể làm rõ làm thế nào để kiểm soát nơi đột quỵ được vẽ? Cảm ơn.
Crashalot

2
Nét được đặt ở giữa, giống như nó luôn luôn là nét nhưng nếu bạn có một khối tô đặc, thì tác dụng của việc điều chỉnh thứ tự sơn để vẽ nét đầu tiên là nửa bên trong của nét được vẽ, có tác dụng tương tự như vẽ đột quỵ nửa dày bên ngoài.
Chris Morgan

7

Đây là một hàm sẽ tính toán số lượng pixel bạn cần thêm - sử dụng nét đã cho - lên trên cùng, phải, dưới và trái, tất cả dựa trên trình duyệt:

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

Như những người ở trên đã lưu ý, bạn sẽ phải tính toán lại phần bù cho tọa độ đường dẫn của nét hoặc tăng gấp đôi chiều rộng của nó và sau đó che dấu bên này hoặc bên kia, bởi vì không chỉ SVG không hỗ trợ căn chỉnh nét của Illustrator, mà PostScript cũng không .

Các đặc điểm kỹ thuật cho đột quỵ trong PostScript tay bang 2nd edition của Adobe: " 4.5.1 vuốt ve: Các đột quỵ hành vẽ một dòng của một số độ dày dọc theo con đường hiện tại Đối với mỗi phân khúc thẳng hoặc cong trong đường dẫn,. Đột quỵ vẽ một dòng được làm trung tâm trên đoạn song song với phân khúc. " (nhấn mạnh của họ)

Phần còn lại của đặc tả không có thuộc tính để bù đắp vị trí của dòng. Khi Illustrator cho phép bạn căn chỉnh bên trong hoặc bên ngoài, nó sẽ tính toán lại phần bù của đường dẫn thực tế (vì nó vẫn rẻ hơn về mặt tính toán so với in đè sau đó che dấu). Các tọa độ đường dẫn trong tài liệu .ai là tham chiếu, không phải là những gì được rastered hoặc xuất sang định dạng cuối cùng.

Vì định dạng gốc của Inkscape là đặc tả SVG, nên nó không thể cung cấp một tính năng mà thông số kỹ thuật thiếu.


4

Đây là một công việc xung quanh cho biên giới bên trong rect bằng cách sử dụng symboluse.

Ví dụ : 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>

Lưu ý: Hãy chắc chắn rằng để thay thế ?trong usevới giá trị thực.

Bối cảnh : Lý do tại sao điều này hoạt động là vì biểu tượng thiết lập chế độ xem mới bằng cách thay thế symbolbằng svgvà tạo một phần tử trong DOM bóng. Cái svgbóng DOM này sau đó được liên kết thành SVGphần tử hiện tại của bạn . Lưu ý rằng svgs có thể được lồng nhau và mỗi lần svgtạo một khung nhìn mới, sẽ cắt mọi thứ chồng lấp, bao gồm cả đường viền chồng chéo. Để biết tổng quan chi tiết hơn nhiều về những gì đang diễn ra, hãy đọc bài viết tuyệt vời này của Sara Soueidan.


2

Tôi không biết điều đó sẽ hữu ích như thế nào nhưng trong trường hợp của tôi, tôi chỉ tạo một vòng tròn khác chỉ có đường viền và đặt nó vào bên trong hình dạng khác.


0

Một giải pháp (bẩn) có thể là bằng cách sử dụng các mẫu,

đây là một ví dụ với hình tam giác được vuốt bên trong:

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

Giải pháp từ Xavier Ho là nhân đôi chiều rộng của nét vẽ và thay đổi thứ tự sơn là tuyệt vời, mặc dù chỉ hoạt động nếu phần tô có màu đặc, không có độ trong suốt.

Tôi đã phát triển cách tiếp cận khác, phức tạp hơn nhưng hoạt động cho bất kỳ điền. Nó cũng hoạt động theo hình elip hoặc đường dẫn (với phần sau có một số trường hợp góc có hành vi lạ, ví dụ như các đường dẫn mở đi qua chính chúng, nhưng không nhiều).

Bí quyết là hiển thị hình dạng trong hai lớp. Một cái không có nét (chỉ tô) và một cái khác chỉ có nét ở chiều rộng gấp đôi (trong suốt) và đi qua mặt nạ cho thấy toàn bộ hình dạng, nhưng ẩn hình dạng ban đầu mà không có nét.

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