2012年3月6日火曜日

html5とcss3で斜め罫線を引くあれこれ

日本語環境ではほぼ避けては通れないと思われる斜め罫線の引き方について考えてみた.現状様々な方法が編み出されているわけだけれど,それらを整理してみよう.
  1. borderを利用するパターン
    古典的な方法.borderを使って斜め罫線を表現する.おおむね,次の2つに分けられる.
    1. 要素本体を用いる
    2. :before :after擬似要素を用いる
  2. 内部のdiv要素を用いるパターン
    内部に罫線専用のdiv要素を配置し,transformを使って回転させる方法.
  3. 画像を用いるパターン
    何らかの画像を用意して対応するパターン.画像の配置方法としてimg要素(svg要素を含む)を用いるものと,背景画像として利用する方法の2つに分けられる.
    1. 外部ファイルを参照する
      予めファイルを用意しておく方法.古めのブラウザでも対応できる方法.下記のcanvasでの描画を事前に行っておくこととほぼ同等であり,ここでは考察の対象としない.
    2. canvasで描画する
      javascriptでラスタ形式の画像を描画しようという方法.
    3. linear-gradientで描画する
      css内部で描画してしまおうという方法.
    4. svgで描画する
      svgでベクタ形式の画像を作る方法.
    5. vmlで描画する
      ie系のみで動作する.
以上を9つのパターンに分け,その特徴を探っていくこととする.

1.border古典パターン

古典的な斜め罫線作成パターン.斜め罫線を引きたい要素のサイズを0とし,border-leftとborder-topの幅を大きくとり,その色の境界で罫線を表現するもの.
borderを罫線として使ってしまうため,ここから更に枠線を引くにはoutline,box-shadowを使うと良い.その際は他の要素と描画位置がずれてしまうのでposition:relativeなどで微調整が必要となる.
要素本体のサイズが0で有るため,内部に要素が存在している場合,領域からはみ出てしまう.また原理上単色の実線による罫線を表すことはできない.
古くから使われている方法なので,ほとんどのブラウザで見た目を再現できる点が魅力.

2.border擬似要素パターン

前述のパターンを改良したもの.css2から導入された擬似要素を絶対位置配置し,そのborderで斜め罫線を実現するもの.beforeとafterの2つの擬似要素を使えるので,これを1pxずらして重ねることで斜めの実線を表現する.
罫線を配置する要素の上に被さるように表示されるので,元の要素の内容が隠れてしまう.cssの記述量がかなり長い.また,仕組み上罫線の重ねがけが出来ない.取り消しの☓罫線は作れない.やや無理やりだが,内部にdiv要素を配置すれば対応可能.
ie6,7等のレガシーブラウザには対応していない.が,それを無視してしまえばこれといった問題は見つからない.

3.div要素rotateパターン

罫線を引きたい要素の中にdiv要素を配置し,transform:rotateを使って見た目を傾けるもの.skewでもいいけれど今度は角度の計算が必要だったりと,あまり使いたくないパターン.回転後の図形が描画範囲からはみ出てしまったり,罫線のためだけにdiv要素を配置するってのもあまり感心しない.

4.canvasパターン

javascriptで罫線を描画してしまおうというパターン.img要素を動的に作ってしまおうという点で明快である.対角線の描画だけで済むので面倒な角度の計算等は必要ない.欠点としてはjavascriptが動作しない環境では動作しないところと,やりたいことに反し割と記述が面倒なところ.
ieでは8以前が未対応.
例)
            var canvas, ctx;
            canvas = document.getElementById("inner");
            ctx = canvas.getContext("2d");
            ctx.beginPath();
            ctx.moveTo(0, 0);
            ctx.lineTo(100, 100);
            ctx.closePath();
            ctx.stroke();

5.canvas背景パターン

4のパターンを背景に適用したもの.canvasのもつtoDataURLメソッドとcssのもつurl関数とを組み合わせ,base64形式にエンコードされた罫線画像をbackground-imageプロパティに埋め込んでしまうもの.要素の内部にコンテンツを配置したいケースに有効.
一般にbackground-imageを用いた場合,background-sizeに100% 100%を指定すると,背景画像が対象となる要素のサイズに自動敵に拡大されるため,要素の大きさが可変であってもある程度見た目が維持されるというメリットがある.(元の画像と要素の大きさとの差があまりに大きすぎると,画像の拡大に伴うジャギが発生して見た目が悪くなってしまう.)

6.gradientパターン

css3のもつgradient関数を使い斜め罫線を生成してしまおうというパターン.
一見単純だが,gradient関数の仕様上対角線の長さ及び角度を計算せねばならず,いまいち使い勝手が悪くおすすめできない.
現状ベンダープリフィクス付きであるため,利用には注意が必要.
例)
#gradient{
    background-image:linear-gradient(
        45deg, transparent 70px, black 70px, black 71px, transparent 71px);
}

7.svgパターン

div要素の内部にsvg要素を配置して罫線を配置するパターン.複雑な図形ならいざ知らず,単なる対角線の描画だけならsvg要素も非常に単純であり,もっとも有望なパターンと言える.
ie系だと8以前が未対応.
 例)
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
       <path d="M 0 0 L 100 100 z" stroke="black" stroke-width="1"/>
</svg>

8.svg背景パターン

5と7の応用で,こちらのアイディアを拝借したもの.svgファイルがxmlファイルであることに着目し,javascriptなどで事前にurlエンコードした内容をcssのbackground-imageに設定する方法.htmlの構造を壊さないのが嬉しい.また,エンコード後の内容もcanvasのバイナリ列よりは読める.
例)
#svgBg{
    background-image:url(data:image/svg+xml,%3C?xml%20version=%221.0%22?%3E%3Csvg%20width=%22100%22%20height=%22100%22%20xmlns=%22http://www.w3.org/2000/svg%22%3E%3Cpath%20d=%22M%200%200%20L%20100%20100%20z%22%20stroke=%22black%22%20stroke-width=%221%22/%3E%3C/svg%3E);
}
元のsvg
<?xml version="1.0"?><svg width="100" height="100" xmlns="http://www.w3.org/2000/svg"><path d="M 0 0 L 100 100 z" stroke="black" stroke-width="1"/></svg>

9.vmlパターン(ieのみ)

vmlを用いて罫線を引いてしまうもの.詳しくはこちらを参照.svg同様記述が少なくて済む.
業務用アプリケーションのようにieだけを対象とし,バージョンが新旧バラバラの場合はこちらを使うと良いかもしれない.ie8ではcssの記述が若干異なるようだ.

比較するとこんなかんじになろうか.windowsXP上のie8をどう捉えるかで選択肢が大きく変化する.個人的には2の擬似要素を使うものとsvgを使うものが無難かと思う.canvasもいいが,javascriptを使うのはいささか大げさだと思う.

パターンブラウザリサイズスクリプト重ねがけ記述量
1border古典
レガシー含む
なし不可普通
2border擬似要素
ie8含む
なし不可多い
3div要素rotateなし可能普通
4canvas
ie9以降
あり可能多め
5canvas背景
ie9以降

見た目が若干変化
あり可能多め
6gradient
ie系がネック

サイズの再計算
なし可能
背景画像の複数指定
普通
7svg
ie9以降
なし可能少なめ
8svg背景
ie9以降

見た目が若干変化
なし可能少なめ
9vml
ieのみ
なし可能少ない

実際に動作させてみたのはこちら.


0 件のコメント:

コメントを投稿