Movable Type のテンプレートタグで使用可能な演算関数を使って画像の高さを算出する

Movable Type でアップロードされる元画像のサイズが統一されておらず、さらに AssetThumbnailLink テンプレートタグが使用できない状況において、元画像とは異なるサイズに縮小された画像の img 要素に、アスペクト比が変わらないように width、height 属性値をセットするために op モディファイアによる四則計算を利用する話。

ちょっとタイトルだと何言ってんのかわからないと思いますが......

この Blog で出力している AMP (Accelerated Mobile Pages) に、新デザイン (参考エントリー) を適用しているとき、記事の下部に一覧として表示している 「関連記事」 部分にある画像のサムネイルに、width 属性と height 属性をつける必要がありました (AMP では、amp-img 要素にこの 2つの属性が必須です)。

この部分の画像は、Movable Type (この Blog は Movable Type で構築されています) のカスタムフィールドで各記事に登録できるようにしてある OGP 画像を引っ張るようにしてあります。Movable Type では、<$mt:AssetProperty property="image_width"$> などで、登録した元画像のサイズを取得することは簡単にできますが、当然、この部分の width 属性と height 属性に入れたいのは、元のでかい画像のサイズではなく、この場所に合わせて縮小した画像のサイズになりますね。

で、Movable Type ちょっとわかる人なら、サムネイル画像を AssetThumbnailLink テンプレートタグで書き出せば width 属性と height 属性も自動で付くじゃん、ってなると思うんですが、AssetThumbnailLink はオリジナルファイルへのリンク付きのサムネイルを丸ごと (つまり、a 要素と img 要素をセットで) 出力するので、当 Blog の記事リストのように、img 要素だけが欲しいという場所では使いにくかったり (regex_replace グローバルモディファイアを使って a 要素を削除するみたいな力技もありますけども)、そもそも、CDN 使って画像処理をしてたりする関係で AssetThumbnailLink だと使い勝手が悪かったので、少しトリッキーな手法で解決しましたよというお話。

何がしたかったのか

この Blog の 「AMP ではない、通常ページ」 で、各記事下に配置している、関連エントリーは、基本的に下記のような HTML と Movable Type テンプレートタグによって書き出しています (一部説明に不要なコードは省いています)。なお、EntryDataOgpimageSet は、カスタムフィールドで設定しているテンプレートタグなので標準のテンプレートタグではありません。

<div class="block-related-entries">
  <div class="module-related-entries-inner">
    <aside>
      <div class="module-related-entries-header">
        <h2>関連記事</h2>
      </div>
      <div class="block-list-entry-2nd">
        <$mt:EntryCategory setvar="cate"$>
        <$mt:EntryID setvar="id"$><mt:Entries id="$id"></mt:Entries>
        <mt:Entries category="$cate" limit="10" unique="1">
        <div class="module-list-entry-item">
          <article id="article-no-<$mt:EntryID$>">
            <a href="<$mt:EntryPermalink abs2rel="1"$>" rel="bookmark">
              <div class="module-entry-item-title">
                <h3 class="module-normalize"><$mt:EntryTitle encode_html="1"$></h3>
              </div>
              <div class="module-entry-item-img">
                <mt:Unless strip_linefeeds="1">
                <img src="
                <mt:If tag="EntryDataOgpimageSet">
                https://burnworks.imgix.net
                <mt:EntryDataOgpimageSetAsset><$mt:AssetURL abs2rel="1"$></mt:EntryDataOgpimageSetAsset>
                ?[...imgix_parameter...]
                <mt:Else>
                ...略...
                </mt:If>" alt="" />
                </mt:Unless>
              </div>
            </a>
          </article>
        </div>
        </mt:Entries>
      </div>
    </aside>
  </div>
</div>

見ての通り、img 要素に width 属性も height 属性も付けていません。装飾的に使われている小さい画像なので、CSS でサイズをするだけで十分という判断からです。

しかし、AMP だと前述の通り、width 属性と height 属性が必須なので付けないといけないとわけです。つまり、img 要素の部分が下記のように amp-img 要素にならないといけないわけですね。

<div class="module-entry-item-img">
  <amp-img layout="responsive"
              src="[...略...]"
              width="120"
              height="[ここの値は画像ごとに違うの]"
              alt="">
  </amp-img>
</div>

width 属性の値は固定 (120px) なので、テンプレートに width="120" とハードコーディングしてしまえばよいとして、登録される画像サイズが固定ではない関係から、height 属性の値はそれぞれの画像ごとに、横幅の 120px に対して縦横比 (アスペクト比) が合うように算出してあげないといけません。

テンプレートタグと演算関数を使って画像の高さを算出する

ある画像の横幅が 120px まで縮小されたとして、その時、アスペクト比が変わらない高さの値を算出すること自体は簡単です。つまり、元画像の横幅に対する、120px の比率を調べて、同じ比率を元画像の高さにかけてあげればいいわけです。

例えば、1200px × 800px の元画像が、横幅が 120px になるまで縮小された場合、横幅は元サイズの 1/10 になっているわけですから、元画像の高さも同じく 1/10 してあげれば、80px という高さが算出できます。

そこまでは別に難しい話ではないのですが、実際にテンプレートタグでどう実装するか...... と考えたときに、そういえば (一部) テンプレートタグって op モディファイアを使って簡単な計算ならできるじゃん、ということで解決策が出ましたよと。

実際のソースコード

ということで、下記のようになりました (説明に不要なコードは省略しています)。

<amp-img layout="responsive" src="
<mt:If tag="EntryDataOgpimageSet">
https://burnworks.imgix.net
<mt:EntryDataOgpimageSetAsset>
<$mt:AssetProperty property="image_width" setvar="width"$>
<$mt:AssetProperty property="image_height" setvar="height"$>
<$mt:SetVar name="width" value="120" op="/"$>
<$mt:SetVar name="height" value="$width" op="/"$>
<$mt:SetVar name="height" value="0.5" op="+"$>
<$mt:GetVar name="height" regex_replace="/(\d*)\.(\d*)/","$1" setvar="height"$>
<$mt:AssetURL abs2rel="1"$></mt:EntryDataOgpimageSetAsset>
?[...imgix_parameter...]
<mt:Else>
...略...
</mt:If>" alt="" width="120" height="<$mt:Var name="height"$>">
</amp-img>

高さの計算に関係する記述を抜き出してみます。

<mt:EntryDataOgpimageSetAsset>
<$mt:AssetProperty property="image_width" setvar="width"$>
<$mt:AssetProperty property="image_height" setvar="height"$>
<$mt:SetVar name="width" value="120" op="/"$>
<$mt:SetVar name="height" value="$width" op="/"$>
<$mt:SetVar name="height" value="0.5" op="+"$>
<$mt:GetVar name="height" regex_replace="/(\d*)\.(\d*)/","$1" setvar="height"$>
</mt:EntryDataOgpimageSetAsset>

height="<$mt:Var name="height"$>"

まず、元の画像の横幅、高さを取得し、それぞれ、widthheight 変数にセットします。

<$mt:AssetProperty property="image_width" setvar="width"$>
<$mt:AssetProperty property="image_height" setvar="height"$>

次に、元画像の横幅 (変数 width にセットした値) を 「120」 で割ります (変数 width には、計算結果がセットされます)。

<$mt:SetVar name="width" value="120" op="/"$>

次に、元画像の高さを、先ほどの変数 width の値で割ります (変数 height には、計算結果がセットされます)。

<$mt:SetVar name="height" value="$width" op="/"$>

このままだとサイズによっては小数点以下の数値が出てしまいますので、変数 height の値を四捨五入します。四捨五入は 0.5 を足してから小数点以下を削除すればよいので、

<$mt:SetVar name="height" value="0.5" op="+"$>
<$mt:GetVar name="height" regex_replace="/(\d*)\.(\d*)/","$1" setvar="height"$>

op モディファイアで変数 height の値に 0.5 を足してから、regex_replace グローバルモディファイアで小数点以下を消してやります。

あとは、この変数 height の値を、height 属性値に入れてあげれば完成です。

height="<$mt:Var name="height"$>"

ひと手間かかりましたが、これで AMP に関しても問題なく記事下の関連エントリー部分を実装することができました。

関連エントリー

Social Share