[IMAGE TEXT MENU 01]画像テキストを用いたレスポンシブなメニューをつくる
画像テキストを用いたメニューをつくってみよう。しかもレスポンシブデザインに対応させ、さらにPC版とスマホ版で一部異なるデザインにする。こうした場合は必要な画像ファイル数がかなり増えてしまい、実装の手間がかかる上に、サーバへのリクエストも無駄に増えてしまう。そこで、CSSスプライトやSassを使用して、これらの問題を解決する方法を紹介しよう。
Photoshopから個別画像の書き出し
作成するのは、テキスト部分がすべて画像のPC・スマホ対応メニューだ(01、02)。
01 作成するサンプル
シンプルなUIでも、PC版とスマホ版で30枚の画像が必要となる
<ul class="button-list">
<li><a href="#" class="button top active"><span class="sprite">トップ</span></a></li>
<li><a href="#" class="button blog"><span class="sprite">ブログ</span></a></li>
<li><a href="#" class="button latest"><span class="sprite">新着</span></a></li>
<li><a href="#" class="button popular"><span class="sprite">人気</span></a></li>
<li><a href="#" class="button recommended"><span class="sprite">おすすめ</span></a></li>
<li><a href="#" class="button search"><span class="sprite">検索</span></a></li>
</ul>
02 HTMLの抜粋(sample/sample.html)
まず、Photoshopでボタンのテキストをデザインする。ボタン状態は「通常」「ホバー(PC版のみ)」「選択中」を用意し、Retinaディスプレイなどの高解像度画面向けに、PC版とスマホ版で異なる解像度のボタンを作成する(03)。
03 Photoshopで作成したデザイン
Photosohpのアートボードにボタンの状態ごとのデザインをまとめていく
あとは、各ボタンのデザインをPNGファイルとして書き出す(04)。
04 Photoshopから書き出し
バージョンCCでは、レイヤーから個別に画像書き出しができる。書き出したいレイヤーを選択し、右クリックで「PNGとしてクイック書き出し」を実行するだけだ。レイヤー名が書き出し後のファイル名となるので、規則性のある命名をしよう
スプライト画像の作成
Photoshopで書き出した画像は30枚にもなる。このままだとサーバへのリクエストが無駄に増えるので、画像を一枚にまとめて background-positionで位置をずらして配置する「CSSスプライト」を使用する。今回、スプライト画像の作成にはTexturePacker※1を使う。
Photoshopから書き出した画像をTexture Packerの画面にドラッグすると、画像のプレビューが作成される(05)。Publish sprite sheetボタンを押して保存先を指定すると、スプライト画像とCSSコードが出力される。
05 TexturePacker から書き出し
無料版では使用できないオプションがあるので、エラーメッセージが出る場合はオプション内容を確認しよう
スプライト画像の最適化
スプライト画像をそのまま使用するのではなく、少しでもデータ量を軽くしておきたい。ImageOptim(OS X対応)※2やPNGGauntlet(Windows対応)※3といった、透過PNGの容量を最適化できる無料アプリケーションがある。最適化の効果は画像によって異なるが、多いときは20%から40%ほどの容量削減効果が期待できるので、ぜひ行っておきたい。
[IMAGE TEXT MENU 02]Sassの機能をフル活用してスプライト画像を効率よく配置する
メニューでCSSスプライトを使う際に面倒なのが、同じようなCSSスタイル定義をボタンごとに何度も繰り返す作業だ。さらにレスポンシブ対応ともなると、ボタン一つで多くのスタイル定義が必要になる。このような繰り返し部分を持つUIの場合、Sass※4の変数、連想配列、@mixin、@eachなどの機能を効果的に使うと効率よく実装することができる。
TexturePackerで生成したCSSから、変数の一覧を作成する。テキストエディタの検索置換機能などを使って、「変数にスペース区切りで数値が代入された」データに整える(01)。
.item1-1x{width:42px; height:15px; background-position: -107px -25px}
.item1-2x{width:82px; height:29px; background-position: -5px -91px}
.item1-active-1x{width:42px; height:15px; background-position: -154px -25px}
.item1-active-2x{width:82px; height:29px; background-position: -313px -91px}
.item1-hover-1x{width:42px; height:15px; background-position: -201px -25px
$item1-1x: 42px 15px -107px -25px;
$item1-2x: 82px 29px -5px -91px;
$item1-active-1x: 42px 15px -154px -25px;
$item1-active-2x: 82px 29px -313px -91px;
$item1-hover-1x: 42px 15px -201px -25px;
省略
$item6-1x: 30px 15px -334px -5px;
$item6-2x: 59px 29px -318px -57px;
$item6-active-1x: 30px 15px -369px -5px;
$item6-active-2x: 59px 29px -254px -57px;
$item6-hover-1x: 30px 15px -229px -5px;
01 TexturePackerから書き出したCSS(上)
SCSS(sample/style.scss)。class名を変数名に変更し、プロパティを除き、「変数にスペース区切りで数値が代入された」データに整形する(下)
@mixinの作成
整形したデータを使って、スプライト画像を配置する@mixinを作成する(02)。この第2引数に0.5を入れると、画像をRetinaディスプレイ用の2分の1サイズで表示することができる。途中の計算では、変数の内部から数値を取得するためにSassのnth()関数を使用している。
変数をmap型のデータにまとめる
Sass 3.3以降では「map型」というデータ型で、連想配列を作成できるようになった。今回は作成した変数をボタンごと、デバイスごとに整理してmapに格納した(03)。
@eachによるループ
map型のデータを対象に、@eachでまとめてスタイルを作成している(04)。@eachを使うと、連想配列の内容一つひとつに対して処理を行える。内部のデータへのアクセスには、Sassのmap-get()関数を使用している。
// 配列と拡大率からスプライト画像を配置するミックスインを定義
@mixin sprite($sprite, $scale:1) {
// スプライト画像全体の横幅・高さをここに入力
$sprite-width: 477px;
$sprite-height: 125px;
// 背景画像サイズを計算
background-size: $sprite-width * $scale, $sprite-height * $scale;
// オフセットを計算
$sprite-offset-x: nth($sprite, 3) * $scale;
$sprite-offset-y: nth($sprite, 4) * $scale;
background-position: $sprite-offset-x $sprite-offset-y;
// 表示サイズを計算
width: nth($sprite, 1) * $scale;
height: nth($sprite, 2) * $scale;
}
02 SCSS(sample/style.scss)
// map型データを作成
$spriteMap: (
top: (
// PC用データ (通常, ホバー用, アクティブ用)
pc: ($item1-1x, $item1-hover-1x, $item1-active-1x),
// SP用データ (通常, アクティブ用)
sp: ($item1-2x, $item1-active-2x)
),
省略
search: (
pc: ($item6-1x, $item6-hover-1x, $item6-active-1x),
sp: ($item6-2x, $item6-active-2x)
)
);
03 SCSS(sample/style.scss)
// map型データを対象にループでルールを作成
@each $key, $item in $spriteMap {
$pc: map-get($item, pc); $sp: map-get($item, sp);
.#{$key} { // インターポレーションを使い、class名にmapのキー名を代入
> .sprite { // SPサイズ
@include sprite(nth($sp, 1), 0.5);
}
&.active > .sprite { // SPアクティブ状態
@include sprite(nth($sp, 2), 0.5);
}
@media all and (min-width: 600px) {
> .sprite { // PCサイズ
@include sprite(nth($pc, 1), 1);
}
&:hover > .sprite { // PCサイズホバー状態
@include sprite(nth($pc, 2), 1);
}
&:active > .sprite, // PCサイズアクティブ状態
&.active > .sprite {
@include sprite(nth($pc, 3), 1);
}
}
}
}
.sprite {
background-image: url(./sprite.png);
background-repeat: no-repeat;
text-indent: 100%; overflow: hidden;
white-space: nowrap; display: inline-block;
}
04 SCSS(sample/style.scss)
※1 TexturePackerはさまざまな形式でスプライト画像を作成できるアプリケーション(OS X、Windows対応)で、無料版でもCSSスプライトを書き出すことができる。 https://www.codeandweb.com/texturepacker
※2 ImageOptim
※3 PNGGauntlet
4 Sassとは、CSSを効率よく記述するためのCSS拡張メタ言語。変数やネストなど、CSS仕様には用意されていない機能を使うことができる。
藤田遼
インターネット・アカデミーにてHTML・CSSを学ぶ。2013年より(株)LIGに入社。現在はWebデザイナーとして主にコーポレートサイトのリニューアルやキャンペーンサイトのデザイン業務に従事している。
堀祐磨
多摩美術大学を中退後、デザイナーとしてグラフィックデザイン・Webデザインを経験。デザイン業務のかたわらプログラミングを行うようになり、Webフロントエンドへ関心を持つ。2015年に(株)LIGに入社。 http://liginc.co.jp/