スタートアップ SVG

第3回少し高度なSVG

前回はSVGの基本的な書き方を解説しました。今回は前回触れなかった座標変換・テキスト関連に、グラデーションを解説していきます。

座標とグループ化

SVGにはg要素という複数の要素をまとめるための要素があります。このg要素によってグループ化した要素に同じ色を適用したり、座標変換(後述)をすることなどができます。

グループ化のサンプルコード(SVG)
<g fill="#33ff33">
  <rect x="40" y="40" width="30" height="30" />
  <polygon points="0,0 43.3,25 0,50"/>
</g>
グループ化のサンプルコード(SVGDOM)
// g(group)要素を作成
var g = document.createElementNS(SVG, 'g');
g.style.fill = '#3333ff';

var rect = document.createElementNS(SVG,'rect');
rect.setAttribute('width', 30);
rect.setAttribute('height', 30);
rect.setAttribute('x', 40);
rect.setAttribute('y', 40);

var triangle = document.createElementNS(SVG,'polygon');
triangle.setAttribute('points', '0,0 43.3,25 0,50');

g.appendChild(rect);
g.appendChild(triangle);

Raphaelではsetを使うことでグループ化に近い処理が可能です。ただし、構築されるDOMを確認するとg要素は作られていないため、中身は異なることに注意が必要です。

グループ化のサンプルコード(Raphael)
var st = paper.set();
st.push(
        paper.rect(40, 40, 30, 30),
        paper.path("M0,0 L43.3,25 L0,50 z")
);
st.attr({fill: "#ff3333"});
st.attr({stroke: "none"});
グループ化のサンプル
(SVG)
グループ化のサンプル
(SVGDOM)
グループ化のサンプル
(Raphael)

このg要素にはx, y座標もありませんし、widthやheightなども持っていませんが、その代わりに、transform属性によって位置や形状を操作することができます。

transformのサンプルコード(SVG)
<g fill="#33ff33" transform="translate(60,10) rotate(60)">
  <rect x="40" y="40" width="30" height="30" />
  <polygon points="0,0 43.3,25 0,50"/>
</g>
transformのサンプルコード(SVGDOM)
// g(group)要素を作成
var g = document.createElementNS(SVG,'g');
g.setAttribute('fill', '#3333ff');
g.setAttribute('transform', 'translate(60,10) rotate(60)');

Raphaelでは次のように、rotate、translate、scaleといったメソッドが用意されています。しかし、前述の通り、setはg要素に対応しているわけではないため、描画はSVG版と異なってしまいます。

transformのサンプルコード(Raphael)
var st = paper.set();
st.push(
    paper.rect(40, 40, 30, 30),
    paper.path("M0,0 L43.3,25 L0,50 z")
);
st.attr({fill: "#ff3333"});
st.attr({stroke: "none"});
st.rotate(60);
st.translate(60, 10);
transformのサンプル
(SVG)
transformのサンプル
(SVGDOM)
transformのサンプル
(Raphael)

transform属性にはtranslate(移動⁠⁠、scale(拡大・縮小⁠⁠、skewX(x方向の傾き⁠⁠、skewY(y方向の傾き⁠⁠、rotate(回転)に加えてmatrix(行列)を指定できます。translateはx軸、y軸をそれぞれ指定でき、y軸は省略できます。scaleは引数1つで比率を保ったままの拡大縮小、引数2つでx軸とy軸を個別に伸縮できます。skewX、skewYはそれぞれ引数を1つだけ持てます。matrixは移動・拡縮・傾き・回転を1つの行列で表現できます。matrixの引数は6つあり、

  • matrix(x軸の伸縮, y軸の傾き, x軸の傾き, y軸の伸縮, x軸の移動, y軸の移動)

に対応します。matrixで回転させたい場合は、次のようにsin、cos関数から伸縮と傾きを求める必要があります。

matrixによる回転と移動
var rad = Math.PI*2/360*60;
g.setAttribute('transform', 'matrix('+Math.cos(rad)+' '+Math.sin(rad)+' -'+Math.sin(rad)+' '+Math.cos(rad)+' 60 10)');

なお、transformにおいて注意しなければいけないのが、

 transform="translate(60,10) rotate(60)"

 transform="rotate(60) translate(60,10)"

で結果が異なるという点です。移動後の回転と、回転後の移動では中心点との関係が変わるためです。transformは右側から順に適用されるので、上記のサンプルは次のように書いた場合と同じ描画になっています。可読性を優先してこのように要素を分けてもよいでしょう。

transformのサンプルコード(SVG)
<g fill="#33ff33" transform="rotate(60)">
  <g transform="translate(60,10)">
    <rect x="40" y="40" width="30" height="30" />
    <polygon points="0,0 43.3,25 0,50"/>
  </g>
</g>

SVGとテキスト

SVGではtext要素で簡単に文字を表示することが可能です。ただし、折り返しなどはできないため、文字が切れたり重なったりしないように適切に配置する必要があります。

テキストのサンプルコード(SVG)
<g fill="#009900" font-size="15">
  <text x="10" y="20">
    スタートアップ
    <tspan x="10" y="40">
      SVG
    </tspan>
  </text>
</g>
テキストのサンプルコード(SVGDOM)
var g = document.createElementNS(SVG,'g');
g.setAttribute('fill', '#3333ff');
g.setAttribute('font-size', '15');

var text1 = document.createElementNS(SVG,'text');
text1.setAttribute('x', 10);
text1.setAttribute('y', 20);
text1.textContent = 'スタートアップ';
var text2 = document.createElementNS(SVG,'tspan');
text2.setAttribute('x', 10);
text2.setAttribute('y', 40);
text2.textContent = 'SVG';

text1.appendChild(text2);
g.appendChild(text1);

Raphaelもテキストを扱うことができます。ただし、Raphaelではデフォルトのスタイルがいくつか設定されているため、見た目や位置が少々異なります。

テキストのサンプルコード(Raphael)
var text = paper.text(10, 20, 'スタートアップ\nSVG');
text.attr({
    "fill": "#ff3333",
    "font-size":"15px",
    "text-anchor":"start"
});
テキストのサンプル
(SVG)
テキストのサンプル
(SVGDOM)
テキストのサンプル
(Raphael)

tspan要素を使うことで、text要素内の任意の文字について文字色や大きさ・位置を調整することができます。

また、テキストはtextPath要素を使うことで特定のパスに沿って描画させることもできます。なお、パスの定義はdefs要素という描画しない要素を置くための要素(HTMLでいうhead要素と似ています)の中に記述します。

textPathのサンプルコード(SVG)
<defs>
  <path id="circlePath" transform="translate(20,20)"
        d="M 50 0
           C 75 0  100 25 100 50
           C 100 75 75 100 50 100
           C 25 100 0 75 0 50
           C 0 25 25 0 50 0
           z" />
</defs>
<g fill="#009900" font-size="15">
  <text x="10" y="10" >
    <textPath xlink:href="#circlePath">スタートアップ SVG テキストパスのデモです。</textPath>
  </text>
</g>
<use xlink:href="#circlePath" fill="none" stroke="red" />
textPathのサンプルコード(SVGDOM)
var defs = document.createElementNS(SVG,'defs');
svg.appendChild(defs);
var circlePath = document.createElementNS(SVG,'path');
circlePath.setAttribute('d', 'M 50 0 C 75 0  100 25 100 50 C 100 75 75 100 50 100 C 25 100 0 75 0 50 C 0 25 25 0 50 0 z');
circlePath.setAttribute('id', 'circlePath');
circlePath.setAttribute('transform', 'translate(20,20)');
defs.appendChild(circlePath);

var g = document.createElementNS(SVG,'g');
g.setAttribute('fill', '#3333ff');
g.setAttribute('font-size', '15');

var text = document.createElementNS(SVG,'text');
text.setAttribute('x', 10);
text.setAttribute('y', 10);
var textPath = document.createElementNS(SVG,'textPath');
textPath.setAttributeNS(XLINK, 'href', '#circlePath');
textPath.textContent = 'スタートアップ SVG テキストパスのデモです。';

var circle = document.createElementNS(SVG,'use');
circle.setAttributeNS(XLINK, 'href', '#circlePath');
circle.setAttribute('fill', 'none');
circle.setAttribute('stroke', 'red');

text.appendChild(textPath);
g.appendChild(text);
textPathのサンプル(SVG)
textPathのサンプル(SVGDOM)

SVGのグラデーション

SVGではベタ塗りだけでなく、グラデーションも可能です。linearGradient(線形グラデーション⁠⁠、radialGradient(円形グラデーション)という要素でグラデーションの塗り方を定義して、図形のfill属性にその定義のIDを指定します。

線形グラデーションのサンプルコード(SVG)
<defs>
  <linearGradient id="colorGradient">
    <stop offset="0%" stop-color="#00ff00"/>
    <stop offset="100%" stop-color="#ffff00"/>
  </linearGradient>
  <linearGradient id="alphaGradient" x1="0" y1="0" x2="0" y2="100%">
    <stop offset="0%" stop-color="#00ff00" stop-opacity="1"/>
    <stop offset="100%" stop-color="#00ff00" stop-opacity="0"/>
  </linearGradient>
</defs>
<rect x="10" y="10" width="100" height="50" fill="url(#colorGradient)"/>
<rect x="10" y="60" width="100" height="50" fill="url(#alphaGradient)"/>
線形グラデーションのサンプルコード(SVGDOM)
// linearGradient要素の作成
var colorGradient = document.createElementNS(SVG, 'linearGradient');
colorGradient.setAttribute('id', 'colorGradient');
// 色の切り替えポイントを用意
var stop1_1 = document.createElementNS(SVG, 'stop');
var stop1_2 = document.createElementNS(SVG, 'stop');
stop1_1.setAttribute('offset', '0%');
stop1_1.setAttribute('stop-color', '#0000ff');
stop1_2.setAttribute('offset', '100%');
stop1_2.setAttribute('stop-color', '#00ffff');
colorGradient.appendChild(stop1_1);
colorGradient.appendChild(stop1_2);
defs.appendChild(colorGradient);

var alphaGradient = document.createElementNS(SVG, 'linearGradient');
alphaGradient.setAttribute('id', 'alphaGradient');
// グラデーションの方向を左上から左下方向に
alphaGradient.setAttribute('x1', '0');
alphaGradient.setAttribute('x2', '0');
alphaGradient.setAttribute('y1', '0');
alphaGradient.setAttribute('y2', '100%');
var stop2_1 = document.createElementNS(SVG, 'stop');
var stop2_2 = document.createElementNS(SVG, 'stop');
stop2_1.setAttribute('offset', '0%');
stop2_1.setAttribute('stop-color', '#0000ff');
stop2_1.setAttribute('stop-opacity', '1');
stop2_2.setAttribute('offset', '100%');
stop2_2.setAttribute('stop-color', '#00ffff');
stop2_2.setAttribute('stop-opacity', '0');
alphaGradient.appendChild(stop2_1);
alphaGradient.appendChild(stop2_2);
defs.appendChild(alphaGradient);

var rect1 = document.createElementNS(SVG, 'rect');
rect1.setAttribute('x', 10);
rect1.setAttribute('y', 10);
rect1.setAttribute('width', 100);
rect1.setAttribute('height', 50);
rect1.setAttribute('fill', 'url(#colorGradient)');

var rect2 = document.createElementNS(SVG, 'rect');
rect2.setAttribute('x', 10);
rect2.setAttribute('y', 60);
rect2.setAttribute('width', 100);
rect2.setAttribute('height', 50);
rect2.setAttribute('fill', 'url(#alphaGradient)');

svg.appendChild(rect1);
svg.appendChild(rect2);

Raphaelもグラデーションを扱うことができます。

線形グラデーションのサンプルコード(Raphael)
var rect = paper.rect(10, 10, 100, 50);
rect.attr({
  gradient:'0-#ff0000-#ff00ff',
  stroke:'none'
});
var rect = paper.rect(10, 60, 100, 50);
rect.attr({
  gradient:'270-#ff0000-#ff00ff',
  stroke:'none',
  "fill-opacity":0
});
線形グラデーションのサンプル
(SVG)
線形グラデーションのサンプル
(SVGDOM)
線形グラデーションのサンプル
(Raphael)
円形グラデーションのサンプルコード(SVG)
<defs>
  <radialGradient id="colorGradient">
    <stop offset="0%" stop-color="#00ff00" stop-opacity="1"/>
    <stop offset="80%" stop-color="#ffff00" stop-opacity="0.5"/>
    <stop offset="100%" stop-color="#ffff00" stop-opacity="0"/>
  </radialGradient>
</defs>
<rect x="10" y="10" width="100" height="100" fill="url(#colorGradient)"/>
円形グラデーションのサンプルコード(Raphael)
var root = document.getElementById('R-sample6');
paper = Raphael(root, 120, 120);
var circle = paper.circle(60, 60, 50);
circle.attr({
  fill:'r#ff0000-#ff00ff',
  stroke:'none',
  "fill-opacity":0
});
円形グラデーションのサンプル
(SVG)
円形グラデーションのサンプル
(Raphael)

まとめ

今回は座標変換、テキスト、グラデーションなど、SVGのやや高度なAPIを紹介しました。やはり、DOMベースでの記述は長くなってしまう傾向があります。次回はそのあたりの解決方法など、SVGの応用的な処理を解説したいと思います。

おすすめ記事

記事・ニュース一覧