2023年3月の前回記事「テキストの回り込み(1)」で、テキスト主体のページの右下隅に画像を配置するレイアウトについて掲載しました。しかし、最終的な微調整の段階で画像の高さを変更しているため、わずかとはいえ、アスペクト比が変わってしまうという欠点がありました。メインとなる文章に添える挿入画的な画像であれば、気にならない程度と思うものの、画像に作品的な位置付けを持たせたいような場合には致命的な欠点となります。
今回は、この欠点を解消し、かつ、同一ページの複数個所で、この右下画像配置のレイアウトを採用したい場合に、javascriptのプログラムを汎用ルーチン化して効率的に使い回せるように、処理の標準化・普遍化を図ることとしました。
実現したいレイアウト
下の例のように、テキスト主体のブロックの右下隅に画像を配置したものを2つ持つページをサンプルとしました。
Fig.1 サンプルページ
2つというのは、複数という意味で、いくつあっても問題はありません。そのために関数を汎用化し、4つの引数を渡すことにより、どのようなサイズであっても画像右下配置のブロック構成を実現できるようにしています。
ブロック構成
サンプルページのブロック構成を図解すると次のようになります。
Fig.2 ブロック図
PDFファイルはこちらからブロック構造(PDFファイル)
最初の版と異なる点は、画像の位置をテキスト1行 ( lineHeight ) 分以下の幅で調整するときに、画像の上パディンクの値で行うようにした点です。これによって、レスポンシブのあるポイントにおいては、画像の上に最大でテキスト1行分未満の隙間ができるものの画像のアスペクト比は常に維持することができるようになりました。約1行分のスペースを大きく感じるかもしれませんが、そのパディング値を上下に分割してスペース感を軽減する方法よりは下端を揃えることの方が視覚的効果は大きいと思います。
同じようなブロック構造が上下に重なっていますが、これは同一ページの中で複数のブロック構造を扱うことができるという意味です。以降、上のブロック構造についての説明を進めます。
基本的な構造として、fh01_box1 ブロックの中に、テキストを包含する名称任意の essay ブロック、吊り紐の役割を果たす fh01_string ブロック、右下配置の画像 fh01_image ブロックと3つのブロックが含まれます。ここで、「fh01_box1」、「fh01_string」、「fh01_image」の3つのセレクタ名については、javascript の関数に引数として渡すことになるのでネーミングルールが必要になります。ここでは、「01」という数字の部分を通番として使用し、ページ中の複数の右下画像配置ブロックに対して 01 ~ 99 と割り当てます。
処理方式
少し細かくなりますが、処理方式について整理していきます。
説明は各ブロックの名称は設定された CSS セレクタ名によります。fig.3 のブロック図の中で、「fh01_box1」、「fh01_string」、「fh01_image」の3つのブロックが処理対象となります。
考え方として、左辺の高さ(左辺高といいます)を「fh01_box1」の高さとします。そして右辺の高さ(右辺高といいます)は「fh01_string」と「fh01_image」を加算したものとします。最初に「左辺高<右辺高」の場合は、そのままで処理不要なので、プログラムを終了します。
その先は、左辺高=右辺高となるように「fh01_string」の高さを調整する処理が続きます。テキストの回り込みの変化によって左辺高はテキストの1行の高さで増減します。左辺と右辺の高さの差をテキスト1行の高さ以下にまで調整するのが、「粗調整」で、その先、テキスト1行の高さ以下を調整するのが「微調整」です。
この左辺・右辺に対する粗調整・微調整は次の5段階からなります。以下、Fig.3の事例に沿って説明を進めます。画像は横 150px、縦 113px のサイズで、テキストは line-height:22px になります。
ロード直後の状態
5段階の処理の前段としてページのロード直後の状態を確認します。「fh01_string」は CSS の設定で height:5px となっているため、画像の高さ 113px を足しても 118px となります。一方、左辺高は、CSS の「line-heit:22px」から、Fig.3 のテキスト9行程度で 198px 程度になり、「左辺高<=右辺高」となります。もしこの段階でテキスト行が5行以下の程度であれば「左辺高<=右辺高」となり、そもそも処理が不要なのでこの時点で処理を終了します。
Fig.3 ページのロード直後の例
この段階で「左辺高>=右辺高」であれば、第一段階の処理に移行します。
第一段階の処理
第一段階では、「左辺高>右辺高」のケースのみ処理対象とします。ここで「fh01_string」の高さを「fh01_box1高」-「fh01_image高」に設定します。これによってリフローが発生します。その結果、左辺と右辺の底面がおおよそ揃うはずです。
Fig.4 第一段階の処理後
ロード直後と第一段階後を比較すると、ロード直後が「左辺高>右辺高」である限り、第一段階の処理によってテキストの形がL字型から上下逆L字型に変わり、テキストのエリアの面積は変わらないことから、第一段階の結果として、「左辺高」と「右辺高」はかなり確率で一致するはずです。しかし、上の Fig.4 ではそうなっていません。その原因は、次の2つが考えられます。
(1)プロポーショナルフォントによる行末位置の変動
(2)英単語の折り返しの有無による行末位置の変動
Fig.3 と Fig.4 をよく比較すると、(1) と (2) の両方が発生しています。そうなるように事例を作っています。初期段階で (2) による折り返しが発生し、行数が1行増えていたところ、第一段階の処理によって、(1) の原因が解消し、結果、行数が8行に減少しました。結果、期待していた「左辺高」=「右辺高」とはならずに、「左辺高<右辺高」となってしまいました。この場合は次の第二段階の処理を行います。
一方、「左辺高>右辺高」となった場合は、第二段階の処理を飛ばして第三段階の処理に進みます。
第二段階の処理
第二段階は、いわばイレギュラーケースに対する処理で、高くなりすぎた右辺高を、結果を評価しながらテキスト1行分の高さずつ削っていきます。具体的には「fh01_string」の高さからテキストの「line-height」1行分を削ります。そして、左右の高さが一致したか、あるいは、「左辺高>右辺高」となるまで繰り返します。
Fig.5 第二段階の処理後
第二段階の処理結果、「左辺高 = 右辺高」となった場合は、その時点で処理完了とします。そして、「左辺高>右辺高」の場合を対象として、次の第三段階の処理に移行します。
第三段階の処理
第三段階の処理は微調整処理で、第二段階を経由したケースに対する最終的な仕上げを行います。
この時点で「左辺高-右辺高」はテキスト1行の高さ未満となっているため、その差分をそのまま「fh01_image」の padding-top の値とて設定します。「fh01_image」の padding-top は初期値として 0px が CSS で設定されています。
Fig.6 第三段階の処理後
Fig.6で、緑色の枠は image ボックスの botder 線です。padding-top:22px の効果が見えます。
これで、第一段階で「左辺高>右辺高」となったケースの処理はすべて終了です。
第四段階の処理
次に第四段階として、第一段階で「左辺高>右辺高」となったケースの処理に入ります。最初に「左辺高>右辺高」以外のケースについては処理を終了します。そして、「fh01_string」について、結果を評価しながらテキスト1行分の高さを追加していきます。具体的には「fh01_string」に「lineH」を加算します。この処理は、「左辺高>右辺高」から「左辺高<右辺高」に変化したポイントを見つけることが目的になっています。
「左辺高>右辺高」から「左辺高<右辺高」に変化したいわば臨界点は Fig.7 のようになります。これは、「fh01_string」から「lineH」を削ると、確実に「左辺高>右辺高」に戻ることが保証されたポイントということになります。
Fig.7 第四段階の処理後
第五段階の処理
第五段階では、最初に「左辺高=右辺高」の場合を処理終了と判定します。そして、「左辺高<右辺高」の場合に「fh01_string」の高さをテキスト1行分削ります。これによって第四段階で保証されたように確実に「左辺高>右辺高」の限界点に戻り、続いて微調整処理に入ります。
微調整処理は第三段階の処理と全く同じで、「左辺高-右辺高」はテキスト1行の高さ未満となっているため、その差分をそのまま「fh01_image」の padding-top の値として設定します。「fh01_image」の padding-top は初期値として 0px が CSS で設定されています。
結果はFig.6 とまったく同じになります。これで、すべての処理が終了します。
この第五段階の後段の処理は第三段階の処理とほぼ同じものですが、プログラムの構成上重複記載が避けられないため、このようなダアイグラム構成となります。昭和の時代のアセンブラ言語のようにニーモニックでジャンプするような書き方ができるプログラム言語であれば、シンプルに凝縮した記述が可能かと思います。
プログラムの効率化
プログラムは javascript で記述していますが、ポイントとなる動作は次の通りです。
・プログラムの動作タイミングは window.onload と window.onrisize の2つのイベントで行っている。そして、window.onrisize についてはチャタリング防止の観点から、タイマーによる遅延処理を行っています。
・画面を描画するに際して画像右下配置ブロックが複数ある場合は、それぞれのブロックについてプログラムで調整する必要があります。つまり、それぞれのブロックのパラメータを引数としてセットしてそれぞれに関数を呼び出すことになります。そのためには関数呼び出し行が複数必要になります。一方、イベントのトリガーは2つあることから、関数を呼び出すための関数を経由することとしました。
Fig.6 関数の連携
サンプルページ・処理フロー
サンプルページでは、ウインドウの幅を狭めてレスポンシブ動作を確認できます。
・サンプルページ (別ページで開きます)
処理フロー(例によって昭和のツールであるフローチャートで表現しています)
・メインルーチン (PDFファイル)別ページで開きます。
・floatHeight 関数 ( 1/2 ) (PDFファイル)別ページで開きます。
・floatHeight 関数 ( 2/2 ) (PDFファイル)別ページで開きます。