web-dev-qa-db-ja.com

SVGのストローク幅の描画方法を制御できますか?

現在、ブラウザベースのSVGアプリケーションを構築しています。このアプリ内では、長方形など、さまざまな形状をユーザーがスタイル設定および配置できます。

たとえばstroke-width1pxをSVG rect要素に適用すると、ストロークはrectのオフセットとインセットに異なるブラウザーで異なる方法で適用されます。これは厄介であることが判明しています特に四角形の外側の幅と視覚的な位置を計算し、他の要素の隣に配置しようとすると。

例えば:

  • Firefoxは1pxインセット(下と左)、および1pxオフセット(上と右)を追加します
  • Chromeは、1ピクセルのインセット(上と左)と1ピクセルのオフセット(下と右)を追加します

これまでの私の唯一の解決策は、実際に境界線を(おそらくpathツールを使用して)描画し、境界線をストローク要素の後ろに配置することです。しかし、この解決策は不快な回避策であり、できればこの道を進んで行きたくないと思います。

私の質問は、SVGのstroke-widthを要素に描画する方法を制御できますか?

182
Steve

いいえ、ストロークを要素の内側と外側のどちらに描画するかを指定することはできません。 2003年にこの機能についてSVGワーキンググループに aプロポーザル を作成しましたが、サポート(または議論)を受けませんでした。

SVG proposed stroke-location example, from phrogz.net/SVG/stroke-location.svg

提案で述べたように、

  • ストローク幅を2倍にし、クリッピングパスを使用してオブジェクトをそれ自体にクリップすることにより、「内側」と同じ視覚的結果を得ることができます。
  • ストロークの幅を2倍にして、オブジェクトのストロークなしのコピーをその上に重ねることで、「外側」と同じ視覚的な結果を得ることができます。

Edit:この答えは将来間違っているかもしれません。 SVG Vector Effects を使用して、veStrokePathveIntersect( 'inside'の場合)またはveExclude( 'outsideの場合)と組み合わせることにより、これらの結果を達成できるはずです。ただし、ベクターエフェクトはまだ機能するドラフトモジュールであり、実装はまだ見つかりません。

Edit 2:SVG 2ドラフト仕様には、 stroke-alignment プロパティ(withwith | inside |可能な値の外側)。このプロパティは、最終的にはUAになります。

Edit 3:面白くて残念なことに、SVGワーキンググループはstroke-alignmentをSVG 2から削除しました。散文 here

342
Phrogz

UPDATE:stroke-alignment属性 4月1日にSVG Strokesと呼ばれる完全に新しい仕様に移動しました

2015年2月26日のSVG 2.0エディターズドラフトの時点で(おそらく 2月13日 以降) stroke-alignmentプロパティが存在する 値innercenter(デフォルト)およびouterを使用します。

@Phrogzおよびそれ以降の stroke-location提案 によって提案されたstroke-positionプロパティと同じように機能するようです。このプロパティは少なくとも2011年から計画されていますが、

SVG 2には、ストローク位置を指定する方法が含まれます。

deferredのように仕様に詳細が記載されたことはありません-今まではそうです。

このプロパティをサポートしているブラウザはありません。また、私が知る限り、SVG 2の新しい機能はまだサポートされていませんが、仕様が完成するとすぐにサポートされることを願っています。これは私が個人的に持っておくよう求めてきた財産であり、最終的に仕様に含まれていることを本当に嬉しく思っています。

ループと同様にオープンパスでもプロパティがどのように動作するかについていくつかの問題があるようです。これらの問題は、ほとんどの場合、ブラウザー間で実装を長引かせます。ただし、ブラウザがこのプロパティのサポートを開始すると、新しい情報でこの回答を更新します。

56
mnsth

私は簡単な方法を見つけましたが、これにはいくつかの制限がありますが、私にとってはうまくいきました:

  • defsで形状を定義する
  • 形状を参照するクリップパスを定義する
  • それを使用して、外側が切り取られるようにストロークを2倍にします

ここに実際の例:

<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>
36
Jorg Janke

CSSを使用して、ストロークと塗りの順序をスタイル設定できます。つまり、最初にストロークを実行し、次に2番目に塗りつぶして、目的の効果を実現します。

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

CSSコード:

Paint-order: stroke;
9
Xavier Ho

これは、ブラウザに基づいて、指定されたストロークを使用して、上、右、下、左に追加する必要があるピクセル数を計算する関数です。

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

        }

    };
7
Steve

上記の人々が指摘したように、ストロークのパス座標へのオフセットを再計算するか、幅を2倍にしてから、一方または他方をマスクする必要があります。 。

AdobeのPostScript Manual第2版のストロークの仕様には、「4.5.1 Stroking:stroke演算子は、現在のパスに沿って太さの線を描画します。各直線またはパス内の曲線セグメント、-ストロークは、辺がparallelのセグメントにcenteredである線を描画しますセグメントに。」 (それらを強調する)

仕様の残りの部分には、行の位置をオフセットするための属性がありません。 Illustratorで内側または外側に位置合わせできる場合、実際のパスのオフセットが再計算されます(オーバープリントしてからマスキングするよりも計算コストが低いため)。 .aiドキュメントのパス座標は参照であり、最終的な形式にラスタ化またはエクスポートされるものではありません。

Inkscapeのネイティブ形式は仕様SVGであるため、仕様に欠けている機能を提供することはできません。

6
user1574945

以下は、内部境界rectsymbolおよびuseを使用した回避策です。

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?を実際の値に置き換えてください。

Background:これが機能する理由は、シンボルがsymbolsvgに置き換えて要素を作成することで新しいビューポートを確立するためですシャドウDOM。シャドウDOMのこのsvgは、現在のSVG要素にリンクされます。 svgsはネストでき、すべてのsvgは新しいビューポートを作成し、重複する境界線を含む重複するすべてのものをクリップすることに注意してください。何が起こっているかについてのより詳細な概要については、Sara Soueidanによる この素晴らしい記事 を読んでください。

3
F Lekschas

それがどれほど役立つかはわかりませんが、私の場合は、境界線だけで別の円を作成し、他の図形の「内側」に配置しました。

1
The Witness

(汚い)可能な解決策は、パターンを使用することです。

これは、内側のストロークの三角形の例です。

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