Featured image of post Hugo博客相册功能实现

Hugo博客相册功能实现

如何为基于Stack主题的Hugo博客添加照片相册功能

前言

在使用Hugo搭建个人博客时,除了写文章,我还希望能有一个专门展示照片的地方。本文记录了为基于Stack主题的Hugo博客添加相册功能的完整实现过程,希望对有类似需求的朋友提供参考。

功能需求

我对相册功能的需求很简单:

  1. 首页展示不同的相册集合,每个相册有封面图和描述
  2. 点击相册进入详情页,展示该相册下的所有照片
  3. 照片支持懒加载,提高页面加载速度
  4. 支持照片点击查看大图和轻松浏览

实现思路

相册功能主要由以下几个部分组成:

  1. 相册数据配置:使用YAML数据文件存储相册信息
  2. 相册列表页面:展示所有相册,使用CSS Grid布局
  3. 单个相册页面:展示特定相册中的照片,使用瀑布流布局
  4. 静态资源:照片文件按相册分类存储

具体实现

1. 创建相册入口页面

首先,在content/page/gallery/index.md中创建相册入口页面:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
---
title: "相册"
description: "我的照片集合"
slug: "gallery"
layout: "gallery"
menu:
    main:
        weight: 4
        params: 
            icon: image
---

这里收集了我的一些照片回忆,记录生活的美好瞬间。

2. 创建相册数据文件

data/gallery.yaml中定义所有相册信息:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
- title: "今天的云"
  slug: "clouds"
  description: "记录下每天不同的云朵形状"
  cover: "/photos/clouds/cover.jpg"

- title: "行东京"
  slug: "tokyo"
  description: "漫步在东京的街头巷尾"
  cover: "/photos/tokyo/cover.jpg"

- title: "今天吃咩"
  slug: "food"
  description: "记录美食与生活"
  cover: "/photos/food/cover.jpg"

这里只需要配置相册的基本信息和封面图,实际照片将从对应文件夹中自动读取。

3. 创建相册列表布局

layouts/_default/gallery.html中实现相册列表页面:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{{ define "main" }}
<header class="page-header">
    <h1>{{ .Title }}</h1>
    {{ with .Description }}
    <div class="page-description">
        {{ . }}
    </div>
    {{ end }}
</header>

<section class="gallery-collections">
    {{ range $idx, $album := .Site.Data.gallery }}
        {{ $albumPath := printf "./static/photos/%s" $album.slug }}
        {{ $photoCount := 0 }}
        {{ range readDir $albumPath }}
            {{ if findRE "(?i)\\.(gif|jpg|jpeg|tiff|png|bmp|webp|avif|jxl)$" .Name }}
                {{ if ne .Name "cover.jpg" }}
                    {{ $photoCount = add $photoCount 1 }}
                {{ end }}
            {{ end }}
        {{ end }}
        <div class="album-card">
            <a href="/gallery/{{ $album.slug }}">
                <div class="album-cover">
                    <img src="{{ $album.cover }}" alt="{{ $album.title }}的封面" loading="lazy"/>
                </div>
                <div class="album-info">
                    <h2>{{ $album.title }}</h2>
                    <p>{{ $album.description }}</p>
                    <span class="photo-count">{{ $photoCount }} 张照片</span>
                </div>
            </a>
        </div>
    {{ end }}
</section>

{{ end }}

这个模板使用CSS Grid创建一个响应式的相册卡片网格,并自动计算每个相册中的照片数量。

4. 创建单个相册页面

为每个相册创建对应的内容页面。例如,在content/gallery/clouds/index.md中:

1
2
3
4
5
6
7
---
title: "今天的云"
description: "记录下每天不同的云朵形状"
date: 2024-04-02T10:00:00+08:00
albumSlug: "clouds"
layout: "photos"
---

注意layout使用了photos,这个布局负责展示相册中的所有照片。

5. 创建照片展示布局

layouts/_default/photos.html中实现照片展示页面:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
{{ define "main" }}
<div class="gallery-header">
    <h1>{{ .Title }}</h1>
    {{ with .Description }}
    <div class="gallery-description">
        {{ . }}
    </div>
    {{ end }}
    <div class="album-nav">
        <a href="/gallery/" class="back-to-albums">
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                <path d="M15 18l-6-6 6-6"></path>
            </svg>
            返回相册列表
        </a>
    </div>
</div>

<div class="gallery-photos page">
  {{ $albumSlug := .Params.albumSlug }}
  {{ $albumPath := printf "./static/photos/%s" $albumSlug }}
  {{ range (sort (readDir $albumPath) "Name" "asc")}}
    {{ if ( .Name | findRE "(?i)\\.(gif|jpg|jpeg|tiff|png|bmp|webp|avif|jxl)$") }}
    <div class="gallery-photo">
      <img class="photo-img" loading='lazy' decoding="async" src="/photos/{{ $albumSlug }}/{{ .Name }}" alt="{{ .Name }}" />
    </div>
    {{ end }}
  {{ end }}
</div>


{{ end }}

这个布局使用瀑布流展示照片,并支持照片懒加载和点击查看大图。

6. 创建照片目录结构

照片文件按照以下结构组织:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
static/
  photos/
    clouds/
      cover.jpg  # 相册封面
      cloud1.jpg  # 照片1
      cloud2.jpg  # 照片2
      ...
    tokyo/
      cover.jpg
      tokyo1.jpg
      ...
    food/
      cover.jpg
      food1.jpg
      ...

每个相册都有自己的文件夹,封面图命名为cover.jpg,也可以根据你的需求自定义为其他照片,在gallery.yaml中修改即可。

7. 更新菜单配置

config/_default/menu.toml中添加相册菜单项:

1
2
3
4
5
6
[[main]]
identifier = "gallery"
name = "相册"
url = "/gallery/"
[main.params]
icon = "image"

优化与改进

在实现过程中,我遇到了一些问题并进行了优化:

相对路径问题

最初使用{{ $.Site.BaseURL }}生成链接,但在局域网访问时会出现问题。改为使用相对路径/gallery//photos/解决了这个问题。

照片数量计算

最初在YAML数据文件中定义每张照片,后改为直接读取文件夹中的照片:

1
2
3
4
5
6
7
8
9
{{ $albumPath := printf "./static/photos/%s" $album.slug }}
{{ $photoCount := 0 }}
{{ range readDir $albumPath }}
    {{ if findRE "(?i)\\.(gif|jpg|jpeg|tiff|png|bmp|webp|avif|jxl)$" .Name }}
        {{ if ne .Name "cover.jpg" }}
            {{ $photoCount = add $photoCount 1 }}
        {{ end }}
    {{ end }}
{{ end }}

大小写敏感的图片格式

添加(?i)标志使正则表达式大小写不敏感,支持.JPG.jpg等不同大小写的文件扩展名:

1
{{ if ( .Name | findRE "(?i)\\.(gif|jpg|jpeg|tiff|png|bmp|webp|avif|jxl)$") }}

最终效果

相册首页

实现了一个简洁美观的相册功能,主要特点:

  1. 响应式设计,适应不同设备
  2. 图片懒加载,提高加载速度
  3. 照片瀑布流布局,充分利用屏幕空间
  4. 支持点击查看大图和轻松浏览
  5. 使用文件系统组织照片,便于管理

总结

通过自定义布局和数据文件,在Hugo博客中实现了功能完善的相册系统。整个实现过程展示了Hugo强大的模板功能和灵活性,不需要额外的插件或数据库支持,就能打造一个美观实用的相册功能。

希望这篇文章对想要在Hugo博客中添加相册功能的朋友有所帮助!如有问题或改进建议,欢迎在评论区交流。

功能优化与调整记录

在实现基本功能后,我根据实际使用情况对相册功能进行了一些优化和调整。以下是优化过程中的一些关键修改:

1. 照片排序优化

最初实现中,相册中的照片是按照文件名降序(“desc”)排列的。但在实际使用过程中,我发现按照文件名升序(“asc”)排列更符合浏览习惯,特别是当照片按照时间顺序命名时。

修改前:

1
{{ range (sort (readDir $albumPath) "Name" "desc")}}

修改后:

1
{{ range (sort (readDir $albumPath) "Name" "asc")}}

这样,照片就会按照文件名从小到大的顺序排列,通常对应时间的先后顺序,提供更好的浏览体验。

2. 移除照片标题和时间显示

在早期版本中,我曾实现了从文件名中提取并显示照片标题和拍摄时间的功能:

1
2
3
4
5
<div class="gallery-photo">
  <img class="photo-img" loading='lazy' decoding="async" src="/photos/{{ $albumSlug }}/{{ .Name }}" alt="{{ .Name }}" />
  <span class="photo-title">{{ .Name | replaceRE "^[0-9 -]+(.*)[.].*" "$1"}}</span>
  <span class="photo-time">{{ .Name | replaceRE "^([0-9-]+).*[.].*" "$1" }}</span>
</div>

这要求我按照特定格式命名照片文件,如"2024-04-02-樱花.jpg",其中"2024-04-02"会被提取为时间,“樱花"会被提取为标题。

但在实际使用中,我发现这种命名方式有些繁琐,而且页面上显示这些信息会让相册页面显得过于拥挤。因此,我移除了这些显示元素,回归到更简洁的界面设计:

1
2
3
<div class="gallery-photo">
  <img class="photo-img" loading='lazy' decoding="async" src="/photos/{{ $albumSlug }}/{{ .Name }}" alt="{{ .Name }}" />
</div>

3. 相册配置的灵活性

data/gallery.yaml中,我为相册配置增加了更多灵活性。可以自定义封面图路径,不一定非要使用相册文件夹中的"cover.jpg”:

1
2
3
4
- title: "今天的云"
  slug: "clouds"
  description: "记录下每天不同的云朵形状"
  cover: "/photos/clouds/best-cloud.jpg"  # 可以指定任意图片作为封面

4. 文件命名建议

经过实践,我发现以下文件命名约定效果较好:

  • 相册文件夹:使用简短的英文单词,如"clouds"、“tokyo”
  • 照片文件:使用日期前缀命名,如"2024-04-02-1.jpg"、“2024-04-02-2.jpg”

这种命名方式既保持了文件的良好组织,又便于按时间顺序浏览照片。

5. 照片加载优化

为了进一步优化照片加载性能,我在img标签中增加了更多的属性:

1
2
3
4
5
<img class="photo-img" 
     loading="lazy" 
     decoding="async" 
     src="/photos/{{ $albumSlug }}/{{ .Name }}" 
     alt="{{ .Name }}" />

loading="lazy"确保了图片只有在即将进入视口时才会加载,而decoding="async"让浏览器可以异步解码图片,不阻塞主线程,提高页面响应速度。

最终配置参考

以下是最终使用的相册配置和模板代码,供参考:

相册数据配置(data/gallery.yaml):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
- title: "今天的云"
  slug: "clouds"
  description: "记录下每天不同的云朵形状"
  cover: "/photos/clouds/cover.jpg"

- title: "行东京"
  slug: "tokyo"
  description: "漫步在东京的街头巷尾"
  cover: "/photos/tokyo/cover.jpg"

- title: "今天吃咩"
  slug: "food"
  description: "记录美食与生活"
  cover: "/photos/food/cover.jpg"

照片显示模板(layouts/_default/photos.html)核心部分:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<div class="gallery-photos page">
  {{ $albumSlug := .Params.albumSlug }}
  {{ $albumPath := printf "./static/photos/%s" $albumSlug }}
  {{ range (sort (readDir $albumPath) "Name" "asc")}}
    {{ if ( .Name | findRE "(?i)\\.(gif|jpg|jpeg|tiff|png|bmp|webp|avif|jxl)$") }}
    <div class="gallery-photo">
      <img class="photo-img" loading='lazy' decoding="async" src="/photos/{{ $albumSlug }}/{{ .Name }}" alt="{{ .Name }}" />
    </div>
    {{ end }}
  {{ end }}
</div>

通过这些优化和调整,相册功能变得更加简洁、高效,提供了更好的用户体验。

Made By ❤️ishineee_
使用 Hugo 构建
主题 StackJimmy 设计