2012年11月19日月曜日

svgでレタープレス効果を実現してみる

svgで最も利用価値のある要素はuse要素です(断言).一つの図形定義を何度も使いまわすのはグラフィックを描く際の常套手段ですが,今回はそれを応用してレタープレス効果を作ってみます.

レタープレス,つまり文字の型押しをグラフィックで実現するには,文字の凹凸を表現する事になります.これを実現するには元となる図形を少しずらし,影とハイライト部を作り出す方法が考えられます.例えばcssで作るのであれば次のようになるでしょう.

Who are you ?

cssの内容
background-color: lightgrey;
color: grey;
font-family: serif;
font-size: 75px;
font-weight: bold;
text-shadow: 1px 1px 0 white,-1px -1px 1px black;

cssではtext-shadowプロパティを用いて元となる文字列の影を二つ作って重ねるだけです.
では同様のことをsvgで実現するにはどうすれば良いでしょう?

この時に役立つのがuse要素です.cssで「影を作る」動作をuse要素で代用するのです.元となる文字列をuse要素で複写し,塗り潰しを影とハイライトとして重ねてしまうのです.またsvgでは文字列に対してstrokeとfillの二つの塗り潰しが可能ですから,これを利用しない手はありません.cssと同じ事を実現しても面白くないので文字の周りをくぼませるといった効果を付けてみましょう.

Who are you ?

<?xml version="1.0" standalone="no"?>
<svg width="700px" height="150px" viewBox="0 0 700 150" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
 <defs>
  <!--ベースの文字列-->
  <text id="text" x="350" y="100" text-anchor="middle" 
   font-size="75" font-family="serif" font-weight="bold">Who are you ?</text>
  <use id="ftext" xlink:href="#text" filter="url(#blur)"/>
  <!--ぼかしフィルタ-->
  <filter id="blur">
   <feGaussianBlur stdDeviation="1"/>
  </filter>
  <!--地の模様-->
  <pattern id="face" 
   patternUnits="userSpaceOnUse" patternContentUnits="userSpaceOnUse" 
   x="0" y="0" width="50" height="50" 
   patternTransform="rotate(45)">
   <rect width="50" height="50" fill="skyblue"/>
   <g fill="white">
    <rect width="25" height="50" opacity="0.5"/>
    <rect width="50" height="25" opacity="0.5"/>
   </g>
  </pattern>
 </defs>
 <!--文字を重ねていく-->
 <rect width="100%" height="100%" fill="url(#face)"/>
 <g color="midnightblue">
  <g stroke-width="12" fill="none">
   <use xlink:href="#ftext" x="-1" y="-1" stroke="currentColor"/>
   <use xlink:href="#text" x="1" y="1" stroke="white"/>
   <use xlink:href="#text" stroke="currentColor" fill="none"/>
  </g>
  <g>
   <use xlink:href="#ftext" x="1" y="1" fill="currentColor"/>
   <use xlink:href="#text" x="-1" y="-1" fill="white"/>
   <use xlink:href="#text" fill="url(#face)"/>
  </g>
 </g>
</svg>

文字列の定義が一箇所にまとまっているため,後から内容を編集するのも簡単ですね.このようにuse要素は図形の定義を一箇所にまとめるのにうってつけの要素です.

ですが,use要素を使う場合はそのふるまいに注意する必要があります.

use要素から参照する文字列や図形要素では共通して利用したい属性のみを設定します.
この例では元となるtext要素にfill属性やstroke属性が設定されていません.さもないとuse要素側で設定しているfill属性・stroke属性が有効とならないからです.逆にfont-size等全ての参照先で共通的に利用したいものはtext要素内で定義しています.

※仕様的にはuse要素は参照先の要素が自身の子要素としてコピーされたものとして振る舞い,動作はg要素に準ずると定められています.つまり,子要素に設定されている属性は外側から変更することができないのです.

この点さえ注意すれば後は煮るなり,焼くなりどうとでもなります.svgの可能性を大きく広げているuse要素ですから是非試してみてください.


追記)同様のことはfilterでも実現することができます.元の文字列を原始フィルタで切り貼りし重ねることで立体感を出していきます.

Who are you ?

<svg width="700px" height="150px" viewBox="0 0 700 150">
 <defs>
  <!--地の模様-->
  <pattern id="face" 
   patternUnits="userSpaceOnUse" patternContentUnits="userSpaceOnUse" 
   x="0" y="0" width="50" height="50" 
   patternTransform="rotate(45)">
   <rect width="50" height="50" fill="skyblue"/>
   <g fill="white">
    <rect width="25" height="50" opacity="0.5"/>
    <rect width="50" height="25" opacity="0.5"/>
   </g>
  </pattern>
  <filter id="letterPress">
   <!--地のグラフィック-->
   <feMorphology in="SourceAlpha" operator="dilate" radius="6" result="borderedFace"/>
   <!--影-->
   <feGaussianBlur in="borderedFace" stdDeviation="1"/>
   <feOffset dx="-1" dy="-1" result="baseShadow"/>
   <!--ハイライト-->
   <feFlood flood-color="white"/>
   <feComposite in2="borderedFace" operator="in"/>
   <feOffset dx="1" dy="1" result="baseHilight"/>
   <!--ベース-->
   <feFlood flood-color="midnightblue"/>
   <feComposite in2="borderedFace" operator="in" result="base"/>
   <!--テキスト部-->
   <!--影-->
   <feGaussianBlur in="SourceAlpha" stdDeviation="1"/>
   <feOffset dx="1" dy="1" result="textShadow"/>
   <!--ハイライト-->
   <feFlood flood-color="white"/>
   <feComposite in2="SourceAlpha" operator="in"/>
   <feOffset dx="-1" dy="-1" result="textHilight"/>
   <!--合成する-->
   <feMerge>
    <feMergeNode in="baseShadow"/>
    <feMergeNode in="baseHilight"/>
    <feMergeNode in="base"/>
    <feMergeNode in="textShadow"/>
    <feMergeNode in="textHilight"/>
    <feMergeNode in="SourceGraphic"/>
   </feMerge>
  </filter>
 </defs>
 <rect width="100%" height="100%" fill="url(#face)"/>
 <text id="text" x="350" y="100" text-anchor="middle" 
  font-size="75" font-family="serif" font-weight="bold"
  fill="url(#face)"
  filter="url(#letterPress)">Who are you ?</text>
</svg>

0 件のコメント:

コメントを投稿