此内容根据文章生成,并经过人工审核,仅用于文章内容的解释与总结
本篇教程基于 Hexo 6.3.0
& Solitude 1.4.7
的动态相册页魔改教程 📝
如果没有服务器可以搭建memos,可以使用iCat自用的memos服务
引用站外链接
效果预览
站内链接
创建数据
- 创建并修改
[blogRoot]/source/photos/index.md
页面
1 2 3 4 5 6 7 8 9 10 11 12 13
| --- title: 生活相册 date: 2024-02-10 12:17:08 type: photo comments: false cover: 'https://img.meuicat.com/banner' desc: 封の生活色彩 leftend: '活在当下 热烈且自由' rightbtn: '部署项目' rightbtnlink: /posts/648b1ceb.html ---
<!-- 页面内容 -->
|
- 新建
[blogRoot]/themes/Solitude/layout/includes/page/photo.pug
页面,并新增以下内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| include ../widgets/page/banner
if theme.photo.bar.enable .icat-status-bar .status-bar-tips= theme.photo.bar.tips .status-bar #bar-box each item in theme.photo.bar.list - const content = item.split(' || ') .status-bar-item a(onclick="sco.photos('" + content[0] + "')") #{content[1]} #status-bar-button(onclick="sco.statusbar('bar-box')") i.scoicon.sco-show-right-line if theme.photo.more.enable - const contents = theme.photo.more.link.split(' || ') if contents[0].startsWith('/') a.status-bar-more(href="javascript:void(0)" onclick="pjax.loadUrl('" + contents[0] + "')") #{contents[1]} else a.status-bar-more(href=contents[0]) #{contents[1]}
div.gallery-photos.page img(src=theme.photo.loading style="margin:auto")
|
- 修改
[blogRoot]/themes/Solitude/layout/page.pug
来使页面匹配
( + 号直接删除 即是正常缩进)
1 2 3 4 5 6
| when 'album' include includes/page/album + when 'photo' + include includes/page/photo default include includes/page/default
|
- 新建
[blogRoot]/themes/Solitude/source/css/_page/photo.styl
样式文件,并新增以下内容
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
| .icat-status-bar margin: 16px 0 display: flex white-space: nowrap align-items: center background: var(--sco-card-bg) border-radius: 8px padding: 0 12px border: var(--style-border-always) transition: .6s box-shadow: var(--sco-shadow-border)
&:hover border-color: var(--sco-blue) box-shadow: var(--sco-shadow-blue)
.status-bar padding: 0.4rem 0 0.4rem 0.4rem white-space: nowrap overflow: hidden transition: .3s width: 100% justify-content: space-between user-select: none display: flex align-items: center font-size: 15px
#bar-box white-space: nowrap overflow-x: scroll overflow-y: hidden display: flex border-radius: 8px align-items: center height: 30px
.status-bar-item a padding: 0.1rem 0.5rem margin-right: 10px font-weight: 700 border-radius: 8px display: flex align-items: center height: 30px color: var(--sco-fontcolor) opacity: .8 transition: .6s
&:hover background: var(--sco-blue) opacity: 1 color: var(--sco-white)
&.selected a background: var(--sco-blue) !important opacity: 1 !important color: var(--sco-white) !important pointer-events: none
#status-bar-button margin-left: 12px cursor: pointer height: 22px display: flex align-items: center transition: .6s
&:hover color: var(--sco-blue)
.status-bar-more margin-left: 12px font-weight: 400 color: var(--sco-fontcolor) opacity: .8 transition: .6s
&:hover color: var(--sco-blue)
#bar-box::-webkit-scrollbar display: none
.gallery-photos width: 100% text-align: center animation: slide-in .6s .4s backwards
.gallery-photo min-height: 5rem width: 24.99% padding: 4px position: relative animation: slide-in 0.6s 0.4s backwards
+maxWidth1024() width: 33.3%
+maxWidth768() width: 49.9% padding: 3px
+minWidth2000() width: 20%
&:hover img transform: scale(1.1)
a border-radius: 8px border: var(--style-border-always) box-shadow: var(--sco-shadow-border) display: block overflow: hidden transition: .6s
&:hover border-color: var(--sco-blue) box-shadow: var(--sco-shadow-blue)
img display: block width: 100% animation: fadeIn 1s cursor: pointer transition: all .4s ease-in-out
.photo-title, .photo-time max-width: calc(100% - 7px) line-height: 1.8 position: absolute left: 4px font-size: 14px background: rgba(0,0,0,0.3) padding: 0px 8px color: #fff animation: fadeIn 1s
.photo-title bottom:4px border-radius: 0 8px 0 8px
+maxWidth768() font-size: 12px left: 3px bottom: 3px
.photo-time top:4px border-radius: 8px 0 8px 0
|
- 在
[blogRoot]/themes/Solitude/source/_page/index.styl
样式文件下面新增内容
( + 号直接删除 即是正常缩进)
1 2 3 4 5 6 7
| if hexo-config('music.enable') @import "music.styl"
@import "share.styl"
+if hexo-config('photo.enable') + @import "photo.styl"
|
- 在
[blogRoot]/themes/Solitude/source/js/main.js
文件中,第六百九十六行后,新增以下内容
( 注:第2、3、39行,需要根据自己的需求进行替换)
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| photos(tag) { let url = '你的memos地址' let apiUrl = tag ? `${url}/api/v1/memo?creatorId=用户ID&tag=${tag}` : `${url}/api/v1/memo?creatorId=用户ID&tag=首次进入需要显示的图片分类`; fetch(apiUrl).then(res => res.json()).then(data => { let html = '', imgs = [] data.forEach(item => { let ls = item.content.match(/\!\[.*?\]\(.*?\)/g) if (ls) imgs = imgs.concat(ls) if (item.resourceList.length) { item.resourceList.forEach(t => { if (t.externalLink) imgs.push(`![](${t.externalLink})`) else imgs.push(`![](${url}/o/r/${t.id}/${t.publicId}/${t.filename})`) }) } }) if (imgs) imgs.forEach(item => { let img = item.replace(/!\[.*?\]\((.*?)\)/g, '$1'), time, title, tat = item.replace(/!\[(.*?)\]\(.*?\)/g, '$1') if (tat.indexOf(' ') != -1) { time = tat.split(' ')[0] title = tat.split(' ')[1] } else title = tat html += `<div class="gallery-photo"><a href="${img}" data-fancybox="gallery" class="fancybox" data-thumb="${img}"><img class="no-lazyload photo-img" loading='lazy' decoding="async" src="${img}"></a>` title ? html += `<span class="photo-title">${title}</span>` : '' time ? html += `<span class="photo-time">${time}</span>` : '' html += `</div>` }) document.querySelector('.gallery-photos.page').innerHTML = html imgStatus.watch('.photo-img', () => { waterfall('.gallery-photos') }) window.Lately && Lately.init({ target: '.photo-time' }) }).catch() var statusBarItemItems = document.querySelectorAll('.status-bar-item'); let firstElement = statusBarItemItems[1]; firstElement.classList.add('selected'); Array.from(statusBarItemItems).forEach(function (element) { element.onclick = function (event) { var selectedElements = document.querySelectorAll('.status-bar-item.selected'); Array.from(selectedElements).forEach(function (selectedElement) { selectedElement.classList.remove('selected'); }); element.classList.add('selected'); event.stopPropagation(); event.preventDefault(); return false; }; }); }, statusbar(elementId) { const container = document.getElementById(elementId);
if (container) { const buttonId = (elementId === "category-bar-items") ? "category-bar-button" : "status-bar-button"; const button = document.getElementById(buttonId); const maxScroll = container.scrollWidth - container.clientWidth; if (container.scrollLeft + container.clientWidth >= maxScroll - 8) { container.scrollTo({ left: 0, behavior: "smooth" }); } else { container.scrollBy({ left: container.clientWidth, behavior: "smooth" }); } container.addEventListener("scroll", function() { button.style.transform = (container.scrollLeft + container.clientWidth >= maxScroll - 8) ? "rotate(180deg)" : ""; }, { once: true }); } }
|
- 在
[blogRoot]/themes/Solitude/source/js/main.js
文件末尾处,进行新增以下内容
( + 号直接删除 即是正常缩进)
1 2 3 4 5 6 7 8 9 10 11 12 13
| ···
window.refreshFn = () => { ···
if (location.pathname == '/photos/') sco.photos() }
window.onresize = () => { if (location.pathname == '/photos/') waterfall('.gallery-photos'); };
···
|
- 创建
[blogRoot]/source/js/imgStatus.min.js
文件,并新增以下内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| ! function() { this.loaded = 0, this.failed = 0, this.total = 0, this.watch = function(a, b) { var c = document.querySelectorAll(a); if (!c.length) return console.log("[imgStatus]: There aren't any images associated with this selector (" + a + ")!"); this.total = c.length; for (var d = 0; d < this.total; d++) isCached(c[d].src) ? this._setLoaded(b) : c[d].addEventListener ? (c[d].addEventListener("load", this._setLoaded.bind(this, b)), c[d].addEventListener("error", this._setFailed.bind(this, b))) : (c[d].attachEvent("onload", this._setLoaded.bind(this, b)), c[d].attachEvent("onerror", this._setFailed.bind(this, b))) }, this.isCached = function(a) { var b = new Image; return b.src = a, b.complete }, this._setFailed = function(a, b) { ++this.failed, "function" == typeof a && a(this) }, this._setLoaded = function(a, b) { ++this.loaded, "function" == typeof a && a(this) }, this.isDone = function() { return this.loaded + this.failed === this.total ? !0 : !1 }, "object" == typeof window && (window.imgStatus = this) }();
|
- 创建
[blogRoot]/source/js/lately.min.js
文件,并新增以下内容
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| ! function() { window.Lately = new function() { var t = this; this.lang = { second: "秒", minute: "分钟", hour: "小时", day: "天", month: "个月", year: "年", ago: "前", error: "NaN" }; var e = function(e) { e = new Date(n(e)); var r = new function() { this.second = (Date.now() - e.getTime()) / 1e3, this.minute = this.second / 60, this.hour = this.minute / 60, this.day = this.hour / 24, this.month = this.day / 30, this.year = this.month / 12 }, i = Object.keys(r).reverse().find(function(t) { return r[t] >= 1 }); return (i ? function(t, e) { return Math.floor(t) + e }(r[i], t.lang[i]) : t.lang.error) + t.lang.ago }, n = function(t) { return t = new Date(t && ("number" == typeof t ? t : t.replace(/-/g, "/").replace("T", " "))), !isNaN(t.getTime()) && t.getTime() }; return { init: function() { var r = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}, i = r.target, a = void 0 === i ? "time" : i, o = r.lang; o && (t.lang = o); var u = !0, h = !1, l = void 0; try { for (var s, c = document.querySelectorAll(a)[Symbol.iterator](); !(u = (s = c.next()).done); u = !0) { var f = s.value, g = n(f.dateTime) || n(f.title) || n(f.innerHTML) || 0; if (!g) return; f.title = new Date(g).toLocaleString(), f.innerHTML = e(g) } } catch (t) { h = !0, l = t } finally { try { !u && c. return &&c. return () } finally { if (h) throw l } } }, format: e } } }();
|
- 在
_config.Solitude.yml
主题配置文件中 extends
下的 head
和 body
引入 lately.min.js
imgStatus.min.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| ···
extends: head: - <script async src="/js/statistics.js"></script> body: - <script type="text/javascript" src="/js/imgStatus.min.js"></script> - <script type="text/javascript" src="/js/lately.min.js"></script>
···
|
- 在
_config.butterfly.yml
主题配置文件中,新增以下配置项
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
|
photo: enable: true bar: enable: false tips: 年份类别 list: - 相册 || 全部 - 2023 || 2023 - 2022 || 2022 - 2021 || 2021 - 2020 || 2020 - 2019 || 2019 - 2018 || 2018 - 2017 || 2017 more: enable: false link: /album/ || 影集 loading: https://yife68.gitee.io/icat-pic/blog/loading.svg
|
使用参数
memos api
地址格式如下所示:
https://memos地址/api/v1/memo?creatorId=用户ID&tag=标签名
memos地址就是首页地址,如:memos.meuicat.com
用户ID获取方式:
点击个人头像,然后点击 RSS
根据浏览器链接获取ID
如url是:https://memos.meuicat.com/u/1/rss.xml
则creatorId就是1
最后完整链接如下:
https://memos.meuicat.com/api/v1/memo?creatorId=1&tag=相册
能看到数据则为正确链接
1 2 3 4 5 6 7 8 9
| #相册 <!-- 写法就是markdown的写法,中括号里先写时间再写标题,中间使用空格隔开 --> ![2023-01-29 我是标题](图片链接) <!-- 若不想要时间只写标题即可 --> ![我是标题](图片链接) <!-- 若不想要标题只写时间即可,只不过后面需要添加空格 --> ![2023-01-29 ](图片链接) <!-- 也可以只填写图片链接 --> ![](图片链接)
|
1 2 3 4 5
| #相册 ![2023-02-09 ](https://s11.ax1x.com/2023/03/16/pp3jQ3V.jpg) ![ 犹豫没要微信](https://s11.ax1x.com/2023/03/17/ppGlZid.jpg) ![2022-10-12 可可爱爱没有脑袋](https://s11.ax1x.com/2023/03/16/pp34dpj.jpg) ![](https://s11.ax1x.com/2023/03/15/pp39iRI.jpg)
|
魔改适配
已适配Butterfly主题,具体魔改教程可前往下方文章查看
引用站外链接
Butterfly的魔改教程:动态相册页
用照片来定格住最好的自己;是时光流逝的见证者,亦是记录者