てがろぐの画像表示をGridで制御できるようにしてみた【それ以外にも色々追加&変更】
以前、てがろぐの画像サイズをJavaScriptで制御する記事を書きました。
しかし、「処理が複雑な気がする…」という気持ちが拭えなかった事と、ページが表示された時にリサイズする動作が目に入っていた(処理がもたついていた)事もあり、仕組みを改修する事にしました。
最近になってやっと『グリッドレイアウト』がどういうものか分かってきたので、今回はそれを使って表示を整えていきます。
グリッドは、行と列を定義する水平線と垂直線の集合が交差したものです。要素をグリッド上の行と列の中に配置することができます。
(引用元:https://developer.mozilla.org/ja/docs/Web/CSS/Guides/Grid_layout/Basic_concepts)
Gridという機能は2017年頃からあったようですが、IEのせいかあまり利用されているイメージが湧かなくて、ずっとよく分からないままだったんですよね😅
今回はそれ以外にも色々手を加えているので、まとめて書いていこうと思います。
※この記事に記載しているコードはほぼGeminiが作成したものです。
元々はimgタグを囲っているaタグのimagelinkクラスにinline-blockを当てて制御していました。 グリッドレイアウトにするのに一つ問題が。並べるのは画像要素なのでそれらをタグで囲まないといけないのですが、てがろぐの仕様上直接HTMLで囲う指定ができないのです。 ※↓てがろぐの基本的なHTML構造 てがろぐは文章と添付画像をまとめてdivタグの中に入れている形なので、そのままdivタグをGridにしてしまうと文章まで巻き込んでしまうんです。しかも文章はタグで囲まれていないので、Gridの子要素として制御するのが難しい形になっていました。 なのでちょっと力技?かもしれませんが、『JavaScriptを使って画像タグをdivタグで囲む』という形にしてみました。 最初は『添付された画像の枚数によって表示サイズを変える』という指定をしていましたが、「全部同じサイズでも綺麗に表示されるのでは…?」と思い直し、4列で統一することにしました。 5枚の時に1枚だけ下にズレますが…まぁいいでしょう。 以前batファイルで『ファイル名から日付を抽出する処理』を作りました。 てがろぐに投稿する際、『スクリーンショットを撮影した日時で投稿したい』という気持ちがあり、その日付入力作業を楽にする為に作ったのですが… batファイルの為パソコンで作業する必要があり、気軽にてがろぐ投稿ができないジレンマがありました。結果面倒になってスクリーンショットがどんどん溜まっていくという… この問題を解決する為に、専用の処理を行うphpファイルを作成してみました。以下はphpコードの抜粋です。 phpファイルでは『記事の最初に設定されている画像の撮影日時を抽出し、それを投稿日時と差し替える処理』を行うようにしました。 画像の撮影日時は、 これでスマホからでも日付の変更が簡単にできるようになり、とても快適になりました。 【注意事項】 『miniフォルダにサムネイルを追加する処理』を改良しました。 以前はデータベースを使って管理していたのですが、Geminiに聞いたところ「ファイル更新日の比較だけで処理できる」とのことだったので、そちらを採用することにしました。以下はphpコードの抜粋です。 今までの処理はデータベースが絡んでいるせいで状況の把握とメンテナンスが大変だったので、シンプルな処理にできて良かったです。 また、処理の中に『画像のサイズ変更だけでなく、"webpファイル"として保存する処理(拡張子は元ファイルと同じままにする)』を入れたので、ファイルサイズをより軽くリサイズできるようになりました。 ※拡張子を元ファイルと同じにしているのは、てがろぐの仕様に合わせるためです。 中身をwebpにして・拡張子だけpngやjpgにできるというのは今回初めて知りました。ペイントソフト等でこのような処理をするのは難しいらしいです。 以前からedit.htmを使って投稿編集ページを改造していました。 「画像一覧ページ」を読み込んで編集中に画像内容を確認できるようにしていたのですが、今回グリッドレイアウトを学んだので、この画像一覧ページにも適用してみました。 ついでにJavaScriptを使い画像のファイル名をタップすると専用コードやファイル名をコピーできるようにしました。これで投稿内容の編集もだいぶしやすくなりました。 …と、ここまで記事を書いていたのですが、てがろぐ作者様のツイートで新しい機能が追加されることを知りました。 投稿欄の下部に「本日中にUPした全画像」または「最も最近にUPした指定個の画像」のサムネイルを並べておき、押すと『その画像を表示するための記法』が投稿欄のカーソル位置に挿入される機能を作りました。一括UPしておいた複数画像を望みの場所に配置しつつ本文を書きたい場合に便利?(てがろぐ) pic.twitter.com/X8jDVTRhbF 次のベータ版で公開される予定とのこと。上記の画像一覧ページは不要になるかもしれませんね😅 JavaScript・CSS・PHP、それぞれかなりシンプルな仕組みにできたので満足しています。 グリッドレイアウトは便利ですね。今まで"こうしたいな"と思うレイアウトに中々できずに四苦八苦していたのですが、これを使えば一気に解決できそうです。 サイト内にはまだ他にも無理矢理レイアウトしている部分がいくつかあるので、グリッドレイアウトを使って少しずつ改修していこうと思います。画像の表示リサイズについて
<section class="onelogbox">
<div class="onelog_commentbox">
<!-- ここに本文が挿入されます。(文章と画像タグがまとめて挿入される) -->
文章の例。こんな感じになっている。 <a href="タグ用URL" class="taglink">#テスト</a><br>
<a class="imagelink" href="画像URL"><img src="画像URL"></a>
</div>
<div class="onelog_datebox">
<!-- 投稿日時や編集ボタン、カテゴリなど -->
</div>
</section>// JavaScript
// 画像を自動でdivで囲う処理
document.querySelectorAll('.onelog_commentbox').forEach(box => {
// 記事内のすべての画像リンクを取得
const images = box.querySelectorAll('a.imagelink');
if (images.length === 0) return; // 画像がなければ何もしない
// 画像を包むための親divを作成
const gridWrapper = document.createElement('div');
gridWrapper.classList.add('image-grid');
// データの数(枚数)を属性として持たせておく(CSSで枚数別の調整がしたい場合に便利)
gridWrapper.setAttribute('data-image-count', images.length);
// 最初の画像の直前に親divを挿入
images[0].parentNode.insertBefore(gridWrapper, images[0]);
// すべての画像を親divの中に移動させる
images.forEach(imgLink => {
gridWrapper.appendChild(imgLink);
// 既存のスタイル(JSでついたサイズ指定など)があればリセット
imgLink.style = '';
const img = imgLink.querySelector('img');
if (img) img.style = '';
});
});/* CSS */
/* 画像グリッドのコンテナ */
.image-grid {
display: grid;
gap: 5px; /* 画像と画像の間の隙間 */
margin-top: 5px; /* 本文との間隔 */
width: 100%;
/* 4列で折り返していく設定 */
grid-template-columns: repeat(4, 1fr);
grid-auto-flow: row; /* 折り返し有効 */
}
/* 個別の画像リンク */
.image-grid .imagelink {
display: block;
width: 100%;
height: auto;
/* 画像本体 */
& img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 4px;
border: 2px solid #ccc;
aspect-ratio: 1/1;
}
}
投稿日時を撮影日時に変更する
// 投稿データ(XML)内のコメントから [PICT:...] タグを検索
if (preg_match('/\[PICT:\s*([^\]\s]+?)\s*\]/i', (string)$log->comment, $matches)) {
$imageFile = $matches[1];
}private function getImageDateTime(string $filePath): ?DateTimeImmutable
{
// 1. ファイル名から日時推測(正規表現でフォーマット解析)
$fileName = basename($filePath);
$patterns = [
// パターンA: 4桁西暦 (YYYYMMDD-HHMMSS)
['regex' => '/(\d{4})(\d{2})(\d{2})[-_](\d{2})(\d{2})(\d{2})/', 'format' => 'YmdHis'],
// パターンB: 2桁西暦 (YYMMDD-HHMMSS)
['regex' => '/(\d{2})(\d{2})(\d{2})[-_](\d{2})(\d{2})(\d{2})/', 'format' => 'ymdHis'],
];
foreach ($patterns as $pattern) {
if (preg_match($pattern['regex'], $fileName, $matches)) {
$dateString = implode('', array_slice($matches, 1));
$dt = DateTimeImmutable::createFromFormat($pattern['format'], $dateString);
if ($dt) return $dt;
}
}
// 2. EXIF情報の確認(画像自体に撮影日時が含まれている場合)
if (function_exists('exif_read_data')) {
// ... (EXIF読み込み処理)
}
// 3. 最終手段:ファイルの更新日時
$fileTime = filemtime($filePath);
return $fileTime ? (new DateTimeImmutable())->setTimestamp($fileTime) : null;
}
ファイル名→EXIF情報→ファイル更新日時
の順に見ていきます。時々抽出に失敗する事もありますが、今のところ大方抽出できているので問題無いです。
※てがろぐのデータベース(tegalog.xml)を操作するので、同じようなコードを使用する前にはバックアップを必ず取っておいてください。
※実際のコードには安全対策として簡易的な認証を入れています。サムネイル作成処理を簡略化
$allowed_types = ['jpg', 'jpeg', 'png', 'webp'];
// --- 新規・更新チェック ---
foreach (glob($source_dir . '*') as $source_path) {
if (is_dir($source_path)) continue;
$filename = basename($source_path);
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
// 許可された拡張子以外はスキップ
if (!in_array($ext, $allowed_types)) continue;
// ★重要:保存先も「元と同じファイル名(拡張子込み)」にする
$dest_path = $mini_dir . $filename;
// ファイルが存在しない、または元画像の方が新しい場合のみ実行
if (!file_exists($dest_path) || filemtime($source_path) > filemtime($dest_path)) {
if (generate_mini_fake_webp($source_path, $dest_path, 300)) {
echo "【更新】 {$filename} (中身はWebPとして保存されました)<br>";
}
}
}// 元画像の短辺を取得
$short_side = min($width, $height);
// 元画像がターゲット(300px)より小さい場合はリサイズしない(元のサイズを維持)
if ($short_side <= $target_width) {
$new_width = $width;
$new_height = $height;
}
// それ以外(大きい場合)のみ縮小計算を行う
else {
if ($width > $height) {
$new_height = $target_width;
$new_width = floor($width * ($target_width / $height));
} else {
// ... (縦横比の計算)
}
}// ★拡張子が何であれ、中身は imagewebp() でWebPとして保存する
$result = imagewebp($canvas, $dest, 80);投稿編集画面を改造
最後に
