ほろ酔い開発日誌

有意義な技術的Tipsを共有出来たら嬉しいです。Ruby、Railsが好きです。Web開発全般(Rails多め、フロント、サーバー、インフラ)、データ分析、機械学習あたりの記事が中心になる予定です。

Webサイトの画像を適切に表示させるために

Webサイトを作成する上で、サムネイル画像の見栄えが大きく良し悪しに関わることは言うまでもないと思います。また、画像のサイズが大きすぎてサイトの表示速度が遅くなるなどの問題も起きるので画像の扱いはポイントになってくるわけです。

今回は特にサムネイル画像の見栄えに重きをおいて僕の考えをまとめたいと思います。

画像の縦横比を維持する

まず、画像の縦横比について考えてみたいと思います。サムネイル画像が潰れていたらなんか残念ですよね。ただし、サムネイルのサイズは統一したいので対応方法をまとめます。

backgroundを利用する

cssのプロパティのbackgroundを用います。特に、background-positionbackground-sizeの使い方がポイントです。 以下のような感じです。これで200x200のサイズで画像の中央部のちょうどいい部分で埋まります。わざわざstyle属性でbackground-imageを書いたのはhtmlサイドで画像のパスを変更出来て便利だからです。このやり方は結構使うので覚えておくと良いと思います。

<div class="resize_image" style="background-image: url('hoge.png')">
</div>

<style>
.resize_image {
  width: 200px;
  height: 200px;
  background-repeat: no-repeat;
  background-position: center center;
  background-size: cover;
}
</style>

以下の記事があるのでより詳しく見たい方はこちらを参照下さい。

【図解】CSSだけで画像の縦横比を維持したサムネイルを表示する

object-fit を使う

前項で紹介した記事の中にもあるのですが、 object-fitというプロパティがcssにあります。ただし、IE、Edgeといったブラウザが未対応なのが残念ですが。 ただこれすごいです。 以下のような記述で前項と同じような実装が出来ます!記述が簡単なのもポイント高いですし、imgタグで出来るというところも強いですよね。backgroundでやるのはなんかもやもやが残ります。html構造としてちゃんとimgにしたいところなのでこれは嬉しい機能です。(はやく全ブラウザ対応してください。。)

<img class="resize_image" src="hoge.png">
</div>

<style>
.resize_image {
  width: 200px;
  height: 200px;
  object-fit: contain;
}
</style>

block要素の中の中央に画像を配置させる

以下のように設定することで親要素中央に画像をいい感じに配置してくれます。

.parent-element {
  position: relative;
  width: 300px;
  height: 300px;
  overflow: hidden;
}
img.child-element {
  width: 100%;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  margin: auto;
}

以下URLが参考になります。

CSSでblock要素を上下左右中央寄せにする、イマドキの方法。

画像サイズをいい感じに計算する

フロントかサーバーで画像のサイズを計算してそのサイズを適用させるという方法です。画像を読み込むときにサイズを計算してサイズを変えるというやり方です。欠点は受け取る画像サイズは変わらないので画像が軽くなるわけではないということ(これはcssで画像のサイズを変えるときにも言えますが)、画像が多いと計算にコストをかけることになるということですね。あと、なんかごちゃつきそうで嫌ということですかね。フロントで簡単にやるなら一瞬resize前の画像表示され ますし。

ここでは、Railsでいい感じの画像サイズを出す方法をちょっと考えてみます。 基本的にwidthのmax幅でとって、heightが特定の高さを超えるとheightのmaxで調整するメソッドを考えます。

module ManageImage
  def resize_main_image(width_max, height_max, main_image)
    main_image_size = FastImage.size(main_image) # originalのサイズ取得
    standard_size = main_image_size[0] # widthを基準として取得
    main_image_size = main_image_size.map{ |size| size*width_max/standard_size } # widthがmaxサイズになるように取得
    if main_image_size[1] > height_max # 特定の高さを超えたとき
      standard_size = main_image_size[1] # heightを基準として取得
      main_image_size = main_image_size.map{ |size| size*height_max/standard_size } # heightがmaxサイズになるように取得
    end
    main_image_size # 結果を返す
  end
end

以下みたいな感じに使います。

include ManageImage
main_image_size = resize_main_image(690, 500, "http://hoge.com/poge.png")

一例ですが、画像をいい感じに調整出来ますよね。

画像を登録するときに適切なサイズを用意する

直接適切なサイズの画像を受け取るのが最高ですよね。だって、例えば全部のサイズが80x80で返ってくるんですから。方法はいくつかあります。

まず、画像登録時にresizeして複数登録しておいて、適切な画像を読み込んで使うという方法です。具体的な方法としては、Railsだと carrierwave というgemでresizeが簡単に出来ます。ただし、画像の枚数がサイズごとにかさみますし、サイズの変更に柔軟に対応出来ないということもあるのでなかなか厳しい気もします。

画像サーバーでresizeしてcacheサーバーに保持する

この方法も適切なサイズの画像を直接受け取ります。僕が今思いつく方法で最も良い方法だと思います。良いというのは運用しやすく、パフォーマンスも高いということです。準備するのはまあまあ大変です。 具体的にどんなものかというと、まず、画像を保存するのはoriginalサイズ一枚です。そして画像を呼び出すときに、http://hoge.com/poge.png?200x200 みたいにサイズを指定したらそのサイズでいい感じにresizeされたものが返ってくるという仕組みです。さらに、resizeしたものはcacheとして保持されるので速度も速いです。(勿論、はじめの一回目に呼びされたサイズはcacheされてないのでごめんなさいですね)

以下の記事が参考になります。

[AWS]CloudFront + nginx(http_image_filter_module) + S3 を使って画像変換サーバを構築する

S3にoriginalの画像を保存、nginxで画像をresize、CloudFrontにcacheという感じです。

ちなみに、僕はこの体勢を構築したことはなくて構築して頂いたものを使っただけです。とても便利すぎます。今度自分でもやってみます。

画像のパス切れに対応する

次に画像のパス切れ対策をします。画像が空っぽだと恥ずかしいので。

imgタグに対応する

以下のように画像の読み込みを失敗したときに代わりの画像を表示させて上げればオーケーです。

$(function() {
  $('img').error(function() {
    $(this).attr('src', '/images/noimage.png');
  });
});

backgroundに対応する

以下のように複数のurlを用意すると前に設定したものが読み込まれないとき後に設定したものが読み込まれます。

<div class="resize_image" style="background-image: url('hoge.png'), url('no-image.png')">
</div>

以上、画像をどう扱うに関してまとめました。別の方法もあるよとか、俺のベストプラクティスとかあればコメント頂けますと嬉しいです!