临近过年上班也没啥动力,就想着总结一下2024年看过哪些电影,突然灵机一动,想到可以给博客加上一个观影栏目,以卡片的形式展示看过的电影列表,之前逛其他博客的时候也有看到类似的电影记录栏目。说干就干,差不多一个小时就实现了一个简单的页面,同时把2023年和2024年看过的电影都列出来了,如下图所示。
实现这个效果大概需要增加三个代码文件,分别是movies.yaml存放电影数据,page/movies/index.md新增一个菜单页面,以及layouts/_default/movies.html编写这个页面的具体样式。
增加影片数据
有的人是通过API读取豆瓣的观影记录,这样很方便,不过我并不用豆瓣,所以选择了最笨的方法,手动编写电影的标题、封面图、年份和评分,按照观看年份分组,手动去豆瓣下载封面图,看上去好像很笨,其实一年就看了大概十部电影,分分钟就填写完成了。
在data文件夹下新建一个movies.yaml,或者其他文件夹下,记得修改代码中的.Site.data.movies成你放置的文件路径。共有三个yaml,分别是movies.yaml、shows.yaml、books.yaml
2024:
- title: 飞驰人生2
cover: /images/movies/2024/feichi.jpg
year: 2024
rating: 7.6
- title: 周处除三害
cover: /images/movies/2024/zhouchu.jpg
rating: 8.1
year: 2023
- title: 姥姥的外孙
cover: /images/movies/2024/laolao.jpg
rating: 8.8
year: 2024
2023:
- title: 流浪地球2
cover: /images/movies/2023/liulang.jpg
year: 2023
rating: 8.3
增加观影页面
content/page/movies/index.md 新增一个菜单页面,slug是地址路径,layout是使用的模板名称,然后设置菜单的顺序以及图标
---
title: 观影
description: 个人年度电影总结,列出所有观看过的电影,按照年份分组
slug: movies
layout: movies
readingTime: false
toc: false
comments: false
license: false
menu:
main:
weight: -45
params:
icon: tag
---
增加观影页面代码
layouts/_default/movies.html
这个文件编写html和css样式,也可以定义一些变量在markdown文件中声明。这里我把电影按照年份分组,在第一排放置了年份选择按钮,按照年份倒序排列,默认选择最近的一年。该代码文件由cursor编写:
点击展开代码
{{ define "body-class" }}template-movies{{ end }}
{{ define "main" }}
<header>
<h2 class="article-title">观影记录</h2>
</header>
<!-- Movies Section -->
{{ $moviesData := .Site.Data.movies }}
{{ $currentYear := now.Format "2006" }}
{{ $sortedYears := slice }}
{{ range $year, $movies := $moviesData }}
{{ $sortedYears = $sortedYears | append $year }}
{{ end }}
{{ $sortedYears = sort $sortedYears "value" "desc" }}
{{ $latestYear := index $sortedYears 0 }}
<div class="year-buttons">
{{ range $sortedYears }}
<button class="year-btn{{ if eq . $latestYear }} active{{ end }}" data-year="{{ . }}">{{ . }}</button>
{{ end }}
</div>
{{ if $moviesData }}
<h3 class="section-title">电影</h3>
{{ range $year, $movies := $moviesData }}
<div class="movies-grid{{ if eq $year $latestYear }} active{{ end }}" data-year="{{ $year }}" data-type="movies">
{{ range $movies }}
<div class="movie-card">
<div class="movie-poster">
{{ if .cover }}
<img src="{{ .cover }}" alt="{{ .title }}" loading="lazy">
{{ else }}
<div class="no-poster">No Poster Available</div>
{{ end }}
</div>
<div class="movie-info">
<h3 class="movie-title">{{ .title }} <span class="movie-year">{{ .year }}</span></h3>
<div class="movie-meta">
{{ if .rating }}
<span class="rating">⭐ {{ printf "%.1f" .rating }}</span>
{{ end }}
</div>
</div>
</div>
{{ end }}
</div>
{{ end }}
{{ end }}
<!-- TV Shows Section -->
{{ $tvData := .Site.Data.shows }}
{{ $tvSortedYears := slice }}
{{ range $year, $shows := $tvData }}
{{ $tvSortedYears = $tvSortedYears | append $year }}
{{ end }}
<h3 class="section-title">剧集</h3>
{{ range $year, $shows := $tvData }}
<div class="movies-grid{{ if eq $year $latestYear }} active{{ end }}" data-year="{{ $year }}" data-type="tv">
{{ range $shows }}
<div class="movie-card">
<div class="movie-poster">
{{ if .cover }}
<img src="{{ .cover }}" alt="{{ .title }}" loading="lazy">
{{ else }}
<div class="no-poster">No Poster Available</div>
{{ end }}
</div>
<div class="movie-info">
<h3 class="movie-title">{{ .title }} <span class="movie-year">{{ .year }}</span></h3>
<div class="movie-meta">
{{ if .rating }}
<span class="rating">⭐ {{ printf "%.1f" .rating }}</span>
{{ end }}
</div>
</div>
</div>
{{ end }}
</div>
{{ end }}
<!-- Books Section -->
{{ $booksData := .Site.Data.books }}
{{ $bookSortedYears := slice }}
{{ range $year, $books := $booksData }}
{{ $bookSortedYears = $bookSortedYears | append $year }}
{{ end }}
<h3 class="section-title">书籍</h3>
{{ range $year, $books := $booksData }}
<div class="movies-grid{{ if eq $year $latestYear }} active{{ end }}" data-year="{{ $year }}" data-type="books">
{{ range $books }}
<div class="movie-card">
<div class="movie-poster">
{{ if .cover }}
<img src="{{ .cover }}" alt="{{ .title }}" loading="lazy">
{{ else }}
<div class="no-poster">No Cover Available</div>
{{ end }}
</div>
<div class="movie-info">
<h3 class="movie-title">{{ .title }} <span class="movie-year">{{ .year }}</span></h3>
<div class="movie-meta">
{{ if .rating }}
<span class="rating">⭐ {{ printf "%.1f" .rating }}</span>
{{ end }}
</div>
</div>
</div>
{{ end }}
</div>
{{ end }}
<style>
.section-title {
margin: 0rem 0 0.5rem 0; /* 减小上下边距 */
font-size: 1.8rem;
color: var(--card-text-color-main);
}
.year-buttons {
margin: 0rem 0;
text-align: left;
}
.year-btn {
padding: 0.7rem 1.5rem;
margin: 0 0rem;
border: 2px solid var(--card-background);
background: transparent;
color: var(--card-text-color-secondary);
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
font-size: 1.6rem;
font-weight: 500;
opacity: 0.7;
}
.year-btn:hover {
opacity: 0.9;
border-color: var(--accent-color);
}
.year-btn.active {
background: var(--accent-color);
color: var(--accent-color-text);
border-color: var(--accent-color);
opacity: 1;
}
.movies-grid {
display: none;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 1rem;
padding: 0rem 0; /* 减小上下内边距 */
}
@media (min-width: 768px) {
.movies-grid {
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1.2rem;
}
}
.movies-grid.active {
display: grid;
}
.movie-card {
background: var(--card-background);
border-radius: 6px;
overflow: hidden;
transition: transform 0.2s;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.movie-card:hover {
transform: translateY(-2px);
}
.movie-poster {
position: relative;
padding-top: 130%;
background: #2a2a2a;
}
.movie-poster img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.no-poster {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #888;
font-size: 0.9rem;
}
.movie-info {
padding: 0.8rem;
}
.movie-title {
margin: 0 0 0.5rem 0;
font-size: 1.4rem;
line-height: 1.3;
font-weight: 600;
color: var(--card-text-color-main);
display: flex;
justify-content: space-between;
align-items: center;
}
.movie-year {
font-size: 1.2rem;
color: var(--card-text-color-secondary);
font-weight: 600;
}
.movie-meta {
display: flex;
justify-content: space-between;
font-size: 1.2rem;
color: var(--card-text-color-secondary);
}
.rating {
font-weight: 500;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
const yearButtons = document.querySelectorAll('.year-btn');
const grids = document.querySelectorAll('.movies-grid');
yearButtons.forEach(button => {
button.addEventListener('click', function() {
const year = this.dataset.year;
// Remove active class from all buttons
yearButtons.forEach(btn => btn.classList.remove('active'));
// Remove active class from all grids
grids.forEach(grid => grid.classList.remove('active'));
// Add active class to clicked button
this.classList.add('active');
// Add active class to all corresponding grids with matching year
document.querySelectorAll(`.movies-grid[data-year="${year}"]`).forEach(grid => {
grid.classList.add('active');
});
});
});
});
</script>
{{ partialCached "footer/footer" . }}
{{ end }}