【Shopify】Liquidコードの画像出力について改めて考えてみる

【Shopify】Liquidコードの画像出力について改めて考えてみる

多様化するデバイス、求められるクオリティ。
画像タグは今、かなり複雑な仕様になっており、ウェブデザイナーを悩ませる大きな要因となっています。

「SEO対策にaltさえ指定しておけばいいんじゃないの?」なんて思ってる方は、はっきりいってインターネット創世記からアップデートされてません。

そんな方はこの記事にすら辿りつけないかもしれませんが...。

<img>タグ

画像タグは、高解像度ディスプレイをはじめ、スマホやタブレットの普及によるデバイスの多様化に伴い、どんどん複雑になってきました。

Shopifyテーマをカスタマイズする上で、画像の出力方法にも工夫が必要です。

属性について

実装にあたり最低限必要とされている属性は「src」「width」「height」「alt」の4種類とされています。

<img src="image.jpg" width="800" height="450" alt="代替テキスト">

「src」属性は「ソース(Source)」の略でデータの位置を指定します。

src属性

width属性は「ウィドゥス」と読み横幅を指定、「height」は「ハイト」と読み高さを指定し、それぞれサイズを指定するだけでなくCLS(レイアウトシフト)を防ぐための必須属性です。

width属性

height属性

alt属性は「オルト」と読み「オルタナティブ(Alternative)」の略で、画像が表示されない状態でも表示されたり、スクリーンリーダーに対応するのでアクセシビリティにも有効です。
また、Googleなどの検索エンジンが画像認識を行う際にも参照されるようです。

 alt属性

画像出力はサイトスピード評価に影響が出やすいので、さらに表示「loading」と「decoding」属性を加えるのが良いとされています。

<img src="image.jpg" width="800" height="450" loading="lazy" decoding="async" alt="代替テキスト">

loading属性は「ローディング」と読み、画像表示のタイミングを指定できます。
基本的には「lazy」値しか使わない印象ですが、ファーストビュー画像はすぐに表示されたほうが良いので「eager」値を指定するか、属性自体付与しないのがお勧めです。

loading属性

decoding属性は「デコーディング」と読み、画像をどのように読み込むかを指定できます。
値は「sync(シンク)」「async(エイシンク)」「auto(オート)」の3種類で、レンダリングブロック対策に非同期のasync値で指定するのが望ましいです。

decoding属性

レスポンシブに対応させて最適な画像を出力させるには「srcset」や「sizes」属性が必要になります。

<img src="image.jpg" srcset="small.jpg 320w, medium.jpg 560w, large.jpg 960w, image.jpg 1280w" sizes="(min-width: 750px) 50vw, 100vw" width="800" height="450" loading="lazy" decoding="async" alt="代替テキスト">

srcset属性は「ソースセット」と読み、複数の画像パスを指定できるので、レスポンシブ画像や高解像度ディスプレイ用画像出力に使用します。

srcset属性

sizes属性は「サイゼス」と読み、ウィンドウ幅別に画像サイズ指定が可能です。

sizes属性

srcsetとsizes属性を適切に使うことで、レスポンシブに最適な画像出力が可能になりますが、難易度がかなり高いです。

また、デザインや仕様によっては「class」属性を付けることもあり、さらにこんなに長く複雑になります。

<img src="/path/image.jpg" srcset="/path/small.jpg 320w, /path/medium.jpg 560w, /path/large.jpg 960w, /path/image.jpg 1280w" sizes="(min-width: 750px) 50vw, 100vw" width="800" height="450" loading="lazy" decoding="async" alt="代替テキスト" class="image">

これでも省略しているくらいなので、<img>タグに至っては静的コーディングより動的コーディングの方が簡単ですね。

ちなみに上記の<img>タグは、モバイル時に100%幅、デスクトップ時に50%幅になる、よくある「画像+テキスト」レイアウトの一例です。

これをLiquidコードで出力すると、以下のコードなります。

Liquidコードで出力

{%- liquid
	assign image_width = 800
	assign image_height = image_width | divided_by: image.aspect_ratio | round
	assign sizes = '(min-width: 750px) 50vw, 100vw'
	echo image | image_url: width: image.width | image_tag: width: image_width, height: image_height, sizes: sizes, loading: 'lazy', decoding: 'async', class: 'image'
-%}

「image_tag」はかなり優秀で、「srcset」や「alt」を自動的に出力してくれます。

「width」「height」は指定せずとも画像サイズを認識して自動で出力してくれますが実画像サイズで指定されますので、敢えてデスクトップデザインの最大サイズで指定しています。

「画像の高さ(image_height)」は「画像の幅(image_width)」を「画像アスペクト比(image.aspect_ratio)」で割って四捨五入しています。

基本的には「width」と「height」さえ記述してあれば比率で計算され、CLSを避けられるので実画像サイズでも問題はないとのことですが、CSSの読み込みタイミングとか、画像がはみ出して予期せぬ横スクロールが出たりとかもするので、出力サイズで指定するのが望ましいと考えています。

<picture>タグ

画像を出力する際は基本的にimgタグで問題ありませんが、次世代画像フォーマット「WebP(ウェッピー)」を利用する際は<picture>タグが推奨されています。

また、モバイルとデスクトップで画像サイズが異なる際は、CSSで表示/非表示を切り替えることもありますが、<picture>タグを使うがスマートです。

HTML構造は以下になります。

<picture class="responsive-image">
	<!-- モバイル出力用(画面幅559px未満)比率1:1 -->
	<source srcset="path/mobile.webp" media="(max-width: 559px)" type="image/webp">
	<!-- タブレット出力用(画面幅959px未満)比率3:2 -->
	<source srcset="path/tablet.webp" media="(max-width: 959px)" type="image/webp">
	<!-- デスクトップ出力用(画面幅960px以上)比率16:9 -->
	<source srcset="path/desktop_1200x.webp 1200w, path/desktop_1600x.webp 1600w, path/desktop_2000x.webp 2000w" media="(min-width: 960px)" type="image/webp">
	<!-- WebP非対応ブラウザ出力用 比率16:9 -->
	<img src="path/desktop.jpg" alt="代替テキスト">
</picture>

コメントに書かれている通り、モバイルは正方形のWebP画像、タブレットは3:2のWebP画像、デスクトップは16:9のWebP画像、WebP非対応ブラウザは16:9のJPEG画像が出力されます。 

この時点でいくつか疑問が生じますよね?

そもそもWebPって何?

WebPとは、2010年にGoogleが開発した新型の画像ファイル形式です。
従来のJPEGやPNGなどの画像ファイル形式に比べて、高い圧縮効率と品質を実現しているので、サイトの表示速度改善に最適です。

当初はChromeのみ出力可能でしたが、現在は主要なモダンブラウザ全てで出力が可能です。

ちなみにShopifyは利用ブラウザに合わせて最適な画像フォーマットに変換されるので、JPEGやPNG、GIFでも主要ブラウザならWebPフォーマットに変換し出力されます。

Shopify CDN

Shopifyテーマへ実装する場合はWebP出力用に<picture>タグは不要なんですが、本件はデバイスごとの画像比率が異なる際の対応についてまとめていますので、ついでのご紹介となります。

widthとheightは?

前述の通りCLS対策の為の必須属性と書いてきましたが、上記のコード例には記述していません。

各デバイス同比率であれば<img>タグに記述すればよいのですが、デバイスごとに異なる場合はCSSにて指定します。

.responsive-image {
	aspect-ratio: 1;
}
.responsive-image img {
	width: 100%;
	height: auto;
	object-fit: cover;
}
@media (min-width: 560px) {
	.responsive-image {
		aspect-ratio: 3/2;
	}
}
@media (min-width: 960px) {
	.responsive-image {
		aspect-ratio: 16/9;
	}
}

CSSのメディアクエリを使ってデバイスごとの比率を指定します。
imgに指定しても構いませんが、他の画像タグへの影響を考慮して親要素の<picture>に指定してあります。

<source>タグにもwidthとheight属性は指定できるようですが、<img>タグを制御するもので実際に出力されないため意味がないようです。

ブラウザ幅2000px以上の場合は?

最後に指定した画像が優先的に出力されるため、ブラウザ幅が2000px以上の場合は「desktop_2000x.webp」が出力されます。

必要に応じて更に大きいサイズの画像を指定してください。

<img>タグだけで行けるのでは?

ShopifyならWebP切り替えを気にしなくていいので、<img>タグだけでも一見実装可能に思えますが、何度試しても思った出力ができませんでした…。

その際の失敗したコード例は以下になります。

{% liquid
  assign mobile_image = section.settings.mobile | image_url
  assign tablet_image = section.settings.tablet | image_url
  assign desktop_image = section.settings.desktop | image_url
  assign media_queries = '1200,1600,2000' | split: ','
  capture desktop_srcset
    for media_query in media_queries
      assign width = media_query | prepend: '&width=' | append: ' ' | append: media_query | append: 'w'
      echo desktop_image | append: width
      unless forloop.last
        echo ', '
      endunless
    endfor
  endcapture
%}

<div class="responsive-image">
  <img
    src="{{ desktop_image }}"
    srcset="{{ mobile_image }} 560vw, {{ tablet_image }} 960vw, {{ desktop_srcset }}"
    alt="代替テキスト"
  >
</div>

↓実際の出力

画面幅が変わってもの画像が切り替わりません。

結局<picture>タグを使ったほうが、思い通りの出力ができました。
以下がコード例で、Liquid部分はそのままです。

{% liquid
  assign mobile_image = section.settings.mobile | image_url
  assign tablet_image = section.settings.tablet | image_url
  assign desktop_image = section.settings.desktop | image_url
  assign media_queries = '1200,1600,2000' | split: ','
  capture desktop_srcset
    for media_query in media_queries
      assign width = media_query | prepend: '&width=' | append: ' ' | append: media_query | append: 'w'
      echo desktop_image | append: width
      unless forloop.last
        echo ', '
      endunless
    endfor
  endcapture
%}

<picture class="responsive-image">
  <source srcset="{{ mobile_image }}" media="(max-width: 559px)" type="image/jpeg">
  <source srcset="{{ tablet_image }}" media="(max-width: 959px)" type="image/jpeg">
  <source srcset="{{ desktop_srcset }}" media="(min-width: 960px)" type="image/jpeg">
  <img src="{{ desktop_image }}" alt="代替テキスト">
</picture>

↓成功した出力

代替テキスト

Shopify CDN

PageSpeed Insightsでサイトの表示速度を計測してみると、アプリから出力されるJSファイルや、解析タグの他に、大きな画像がレンダリングブロックで足を引っ張っているのがよくわかります。

一般的にはロスレス圧縮などを使って軽量化した上でサーバーにアップロードしますが、ShopifyはCDNが優秀なので何もしなくてもロスレス圧縮した画像が出力されるようです。

まずはこの画像をご覧ください。

1920x1280pxにトリミングし、最高画質の圧縮で指定してJPEG書き出したところ、ファイルサイズは1.06MBでした。

ShopifyのCDNにアップロードするとファイルリストの表示上は同じ1.06MBですが、出力したJPEG画像はなんと約252KBまで下がっています。
しかもWebPに変換され約116KBまで軽量化されます。約1/10サイズ!

しかも画像を複数用意しなくても、URLのパラメータ指定でwidth指定するだけでサイズ調整ができるのもありがたいですね。

フルサイズ compress-sample.jpg 116KB
W1000px compress-sample.jpg?width=1000 55.2KB
W600px compress-sample.jpg?width=600 30.77KB

image_url

image_urlタグでサイズを指定して、適切なサイズを出力するだけでかなり軽量化ができます。

{{ image | image_url: width: 1000 }}

また、heightやcropを指定することで↓このようにトリミングもできてしまいます。

{{ image | image_url: width: 600, height: 600, crop: 'center' }}

srcsetやsizes属性を併用して、デバイスごとに最適な画像出力を心がけましょう。

image_url

file_img_url

アイキャッチやアップロード画像などは、「image_url」を使えば先程のLiquidコードで柔軟な出力ができますが、「file_img_url」を使って直接画像URLを参照するケースも少なくありません。

その場合は、以下のコードで出力できます。

{% assign sample_image = 'compress-sample.jpg' | file_img_url: '600x600', crop: 'center' %}
<img src="{{ sample_image }}" width="600" height="600" alt="">

image_urlやimage_tagは使えませんので、予めfile_img_urlでサイズ指定したものを定義して<img>タグで出力します。

現在は非推奨となったimg_urlと同じ使い方です。

file_img_url

image_tag

このタグの登場で、画像出力はめちゃめちゃ便利になりました。

前述にもありますが、基本的には以下のように単独でもwidthやheight、alt属性をはじめ、srcset属性も書き出してくれます。

{% assign image = section.settings.image %}
{{ image | image_url: width: image.width | image_tag }}

これに必要な属性を加えたり、値を指定することで柔軟な画像出力ができます。

{% assign image = section.settings.image %}
{{ image | image_url: width: image.width | image_tag: width: 300, height: 300, loading: 'lazy', decoding: 'async', data-id: image.id }}

多様化で複雑となった画像出力も、Shopifyならこんなにスマートに出力できます。

最後に

ウェブ上で使う画像フォーマットといえば、主にJPEGとPNG、GIFの3種類です。

GIF画像はパラパラ漫画のようなアニメーションができるので以前は動きのあるサイトに重宝されていましたが、「256色しか使えない」「透過するとガタガタ」「ファイルサイズが重い」とデメリットも多く、ほとんど使う機会はなくなりましたが、たま〜にGIF画像を使いたいという方もいらっしゃいます。

上位互換というべきAPNGというPNG画像のアニメーションが登場しましたし、MP4の動画ファイルの方がスムーズでキレイだったりするので、GIFは使わずいずれかを実装しています。

また、JPEGとPNGの使い分けを理解せずに使っている方も多いです。

基本的に「写真はJPEG」「イラストはPNG」がお勧めですが、写真に近いフルCGのイラストなどは逆にJPEGが良いでしょう。

いずれもWebPに変換できる画像フォーマットなので、現在はWebPが覇権を握っている画像業界ですが、AVIFというさらに優れた画像フォーマットもあります。

ただ、手軽に変換できる環境がまだまだ整っていないので浸透していないとのことですが、主要なモダンブラウザは全て対応済みだそうです。

ウェブデザイナーと画像タグの戦いは、まだまだ続きそうです。

この記事を書いた人

モリタオウ

株式会社テンカ 代表取締役 / ウェブクリエイター / グラフィックデザイナー

1977年12月20日生まれ。広告代理店や企業広報を経て、2007年12月にデザイン事務所「モリタ・クリエイト」を創業。2022年12月に「株式会社テンカ」を設立。