(这个页面是站长自用的,用来记录他改造博客主题的过程,防止改不回去)
(本页面会持续更新一些博客优化 / 美化代码)
本文的某些代码仅限于 Argon 主题
—
Mimosa 提醒您:在改动主题代码前一定要记得做好备份工作。
本文最后更新时间:2025.4.11
实况照片
iOS 的实况照片实质上就是一张静态图片与 heic 视频格式的结合。
这里引用 Apple Developer 的 livephotoskit.js (官方使用文档:LivePhotosKit JS | Apple Developer Documentation)来实现。
若需展示动态图片,需将鼠标悬浮至图片左上角的
LIVE
图案上。示例:
(源代码参考 » WordPress 古腾堡编辑器增加 live 实况图预览模块 – 漫川笔记,由于源代码有一点 bug,Mimosa 对其进行二次修改:优化了编辑页面的图片展示以及展示图片路径、展示实况图片的自适应大小)
step.1
在主题的 functions.php 中,在 <?php
后插入以下代码(注:必须在 php 标签内插入,没有 php 标签的自己建一个,因为这是 php 代码):
// 实况图片 function enqueue_livephotoskit_script() { wp_enqueue_script('livephotoskit-js', 'https://source.loneapex.cn/js/livephotoskit.js', array(), null, true); } add_action('wp_enqueue_scripts', 'enqueue_livephotoskit_script'); function register_custom_live_photos_block() { wp_register_script( 'custom-live-photos-block', get_template_directory_uri() . '/block.js', array('wp-blocks', 'wp-element', 'wp-editor', 'wp-components', 'wp-i18n'), filemtime(get_template_directory() . '/block.js') ); register_block_type('custom/live-photos-block', array( 'editor_script' => 'custom-live-photos-block', 'render_callback' => 'render_custom_live_photos_block' )); } add_action('init', 'register_custom_live_photos_block'); function render_custom_live_photos_block($attributes) { if (!isset($attributes['photoURL']) || !isset($attributes['videoURL'])) { return ''; } $width = '100%'; $height = '300px'; // Example fixed height, adjust as necessary return sprintf( '<div class="live-photo-wrapper" style="width:%s; height:%s; position:relative;"> <div data-live-photo data-photo-src="%s" data-video-src="%s" style="width:100%%; height:100%%;"></div> </div>', esc_attr($width), esc_attr($height), esc_url($attributes['photoURL']), esc_url($attributes['videoURL']) ); }
step.2
在博客主题的根目录(与 functions.php 同级目录)建一个 block.js
文件
(function (blocks, editor, element, components) { var el = element.createElement; var MediaUpload = editor.MediaUpload; var InspectorControls = editor.InspectorControls; var TextControl = components.TextControl; blocks.registerBlockType('custom/live-photos-block', { title: 'Live Photos Block', icon: 'camera', category: 'media', attributes: { photoURL: { type: 'string', default: '' }, videoURL: { type: 'string', default: '' }, width: { type: 'number', default: 400 }, height: { type: 'number', default: 300 } }, edit: function (props) { var attributes = props.attributes; var setAttributes = props.setAttributes; return el( 'div', { className: props.className }, el('p', {}, '选择图片和视频:'), el( MediaUpload, { onSelect: function (media) { setAttributes({ photoURL: media.url }); }, allowedTypes: 'image', render: function (obj) { var fileName = ''; if (attributes.photoURL) { // 从 URL 中提取文件名 fileName = attributes.photoURL.split('/').pop(); } return el( components.Button, { className: 'button button-large', onClick: obj.open }, attributes.photoURL ? fileName : '选择图片' ); } } ), el( MediaUpload, { onSelect: function (media) { setAttributes({ videoURL: media.url }); }, allowedTypes: 'video', render: function (obj) { var fileNam1e = ''; if (attributes.videoURL) { // 从 URL 中提取文件名 fileName1 = attributes.videoURL.split('/').pop(); } return el( components.Button, { className: 'button button-large', onClick: obj.open }, attributes.videoURL ? fileName1 : '选择视频' ); } } ), el(InspectorControls, {}, el(TextControl, { label: '宽度(px)', value: attributes.width, onChange: function (value) { setAttributes({ width: parseInt(value, 10) || 0 }); } }), el(TextControl, { label: '高度(px)', value: attributes.height, onChange: function (value) { setAttributes({ height: parseInt(value, 10) || 0 }); } }) ) ); }, save: function () { // 后台通过 PHP 渲染,前端保存为空 return null; } }); }( window.wp.blocks, window.wp.editor, window.wp.element, window.wp.components ));
step.3
在写文章的时候应该会出现如图所示的区块,把 iOS 的实况照片转换成视频格式,然后分别上传图片和视频即可
文章中插入音乐播放器
<link rel="stylesheet" href="https://loneapex.cn/extra-js/Aplayer/APlayer.min.css"> <script src="https://loneapex.cn/extra-js/Aplayer/APlayer.min.js"></script> <script src="https://loneapex.cn/extra-js/Aplayer/Meting.min.js"></script> <meting-js server="netease" type="song" id="761626" fixed="false" mini="false" order="list" loop="all" preload="false" list-folded="true" ></meting-js>
作词 : Neko Hacker
作曲 : Neko Hacker
编曲 : Neko Hacker
刹那の誓い - 鬼頭明里 (きとう あかり)
词:Neko Hacker
曲:Neko Hacker
编曲:Neko Hacker
終わりを待つ刹那に君は (袖手以待结束的瞬间)
ここに何を残すのだろう (你将于此留下何物万千)
疑い知らぬ言葉に永遠を (许诺的话语 无疑弥坚)
星空へと誓った (向星空道出永恒之爱的誓言)
たった一度の出逢いで (一次邂逅之缘)
変わっていったこの世界 (世界足以改变)
君にだけは隠せないんだ (只是对你仍无法藏掩)
触れていたいという願い (想要伸手触及的心愿)
喜びも悲しみも (和与你一起的悲伤喜悦)
その瞬間が訪れるその日まで (直至那一刻降临那天)
信じてみよう (信任之心与时更迭)
これから何回も何回も何回も (无数次共度仍未完结)
何回も季節は巡る (四季轮回 岁岁年年)
そのたび何回も何回も何回も (无数次旅程时节)
君とこの場所に (都回到你在的原点)
月は輝き 星は瞬く (月盈之晟 星悬之烨)
その身果てるまで照らせるように (为你我披上满身辉洁)
終わりを待つ刹那を今は (只身静待终章启卷)
愛しき君となら永遠と呼ぼう (此刻的永恒 只因你的出现)
懐かしいようなこの暖かさ (彼时温暖 至今未灭)
凍てつく心が溶かされていった (内心的坚冰层层融解)
君と共にいられるなら (倘若与你执手并肩)
きっと二度目の出逢いは (定是重逢之歌吟得清越)
不安すらも感じていた (不安之感雪藏心田)
続くはずのない平和を (无法维系和平的世界)
信じることこそが正解? (唯有给予坚信才是正确?)
この気持ち この痛み (纵使此般隐痛不歇)
苦しみが無くならずある限り (也只是向不会消失的结果倾斜)
信じていよう (仍心怀不懈信念)
月は輝き 星は瞬く (星月交辉 晦暗卒谢)
この身捧げても守れるように (愿将己身的守护奉献)
終わりを待つ刹那を今は (刹那光阴 只为迎来终焉)
終わりは来るのだと知りながら (我明知是末日迫在眉睫)
愛しき君となら永遠と呼ぼう (若有亲爱的你在 一刹也定格为永远)
これから何回も何回も何回も (随情形无数次复原重叠)
何回も季節は巡る (任季节无数次流转变迁)
そのたび何回も何回も何回も (总是无数次旅程向前)
君とこの場所に (未来的乐章都在此与你继续谱写)
- 1 刹那の誓い
这里引用的是 aplayer 播放器 + meting 获取网易云音乐
参数:
- id=’外链播放器 id’, 必须参数(比如网易云音乐播放单曲就填写该音乐 id,歌单填写歌单的 id,皆可通过网易云音乐的分享链接查看具体 id)
- type=[song = 单曲,playlist = 歌单,album = 专辑,search = 搜索结果,artist = 艺术家], 必须参数
- server=[netease = 网易云音乐,tencent=QQ 音乐,kugou = 酷狗音乐,xiami = 虾米音乐,baidu = 百度音乐], 必须参数
- fixed = 启用固定模式,固定在左下角,默认 false
- mini = 在页面左下角启用迷你模式,默认 false(不启用的话就是上面的效果)
- preload=[none,metadata,auto] (预加载)
- mutex=[互斥锁,默认 true], 默认 false
- order=[random = 随机播放,list = 列表播放]
- loop=[all = 全部循环,one = 循环一次,none = 不循环]
- volume=[音量,默认 0.7]
- lrc-type=[歌词类型,默认 0]
- list-folded=[列表是否折叠,默认 false]
- list-max-height = 列表最大高度,默认 340px
- storage-name = 本地存储存储密钥,用于存储播放器设置,默认 metingjs
节日弹窗
差不多这个效果,附带烟花
由于每次打开网站都会显示这玩意而过于繁琐,我用 JavaScript 和 localStorage 来实现弹窗仅在每个浏览器只显示一次。
不需要烟花的可以把注释 =
下面的 for 循环改成 弹窗逻辑=
for (let i = 0; i < 0; i++) {
为了防止网站主题文件的样式预设干扰,这里把所有样式后面都加了 !important
权重,并且把所有变量名都起的特别长(防止重复变量名干扰)
for 循环那儿可以调整烟花的个数(i < 8
那儿)和每个烟花间隔的延迟;const particleCount_2025 = 45;
可以调整每个烟花微粒的个数(别太多了,容易死机的);
colorList_2025
这个列表是烟花微粒的随机颜色
<!--弹窗--> <style> /* 弹窗 */ .popup_modal_2025_newyear { display: none; position: fixed; z-index: 99999; left: 50%; top: 50%; transform: translate(-50%, -50%); background: white !important; padding: 20px !important; border-radius: 10px !important; text-align: center !important; width: 320px !important; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3) !important; } .popup_modal_2025_newyear h2 { margin: 0 !important; font-size: 22px !important; } .popup_modal_2025_newyear p { margin: 15px 0 !important; } .popup_modal_2025_newyear button { margin-top: 10px !important; padding: 8px 20px !important; border: none !important; background-color: #007BFF !important; color: white !important; border-radius: 5px !important; cursor: pointer !important; } /* 烟花粒子 */ .firework_particle_2025 { position: absolute !important; width: 6px !important; height: 6px !important; opacity: 1 !important; border-radius: 2px !important; } .cillo_2025_text_h2 { color: #A52A2A !important; } .cillo_2025_text_p { color: #8B4500 !important; } </style> <!-- 弹窗 --> <div id="modal_container_2025_happy_newyear" class="popup_modal_2025_newyear"> <h2 class="cillo_2025_text_h2">
2025 新年快乐
</h2> <p class="cillo_2025_text_p">愿新的一年,所有的美好都如期而至,所有的期待都不负等待。</p> <button onclick="closePopup_2025_newyear()">确定</button> </div> <script> // ===
烟花特效 === function createFireworkEffect_2025(x, y) { const colorList_2025 = ["#FFFF00", "#7CFC00", "#33FF57", "#339FFF", "#E633FF"]; const particleCount_2025 = 45; //每个烟花的微粒数量 for (let i = 0; i < particleCount_2025; i++) { let particleElement_2025 = document.createElement("div"); particleElement_2025.classList.add("firework_particle_2025"); particleElement_2025.style.backgroundColor = colorList_2025[Math.floor(Math.random() * colorList_2025.length)]; particleElement_2025.style.left = `${x}px`; particleElement_2025.style.top = `${y}px`; document.body.appendChild(particleElement_2025); let angle_2025 = (Math.PI * 2 * i) / particleCount_2025; let velocity_2025 = Math.random() * 4 + 2; let gravity_2025 = 0.15; let xVelocity_2025 = Math.cos(angle_2025) * velocity_2025; let yVelocity_2025 = Math.sin(angle_2025) * velocity_2025; let fadeOut_2025 = 1; let animation_2025 = setInterval(() => { xVelocity_2025 *= 0.98; yVelocity_2025 += gravity_2025; let newX_2025 = parseFloat(particleElement_2025.style.left) + xVelocity_2025; let newY_2025 = parseFloat(particleElement_2025.style.top) + yVelocity_2025; particleElement_2025.style.left = `${newX_2025}px`; particleElement_2025.style.top = `${newY_2025}px`; fadeOut_2025 -= 0.02; particleElement_2025.style.opacity = fadeOut_2025; if (fadeOut_2025 <= 0) { clearInterval(animation_2025); particleElement_2025.remove(); } }, 20); } } //随机位置 function randomPosition_2025() { return [Math.random() * window.innerWidth * 0.9, Math.random() * window.innerHeight * 0.6]; } // ===
弹窗逻辑 === function showPopup_2025_newyear() { if (!localStorage.getItem("popup_2025_newyear_shown")) { document.getElementById("modal_container_2025_happy_newyear").style.display = "block"; for (let i = 0; i < 8; i++) { setTimeout(() => { let [x, y] = randomPosition_2025(); createFireworkEffect_2025(x, y); }, i * 650); // 每次间隔 650ms } } } function closePopup_2025_newyear() { document.getElementById("modal_container_2025_happy_newyear").style.display = "none"; localStorage.setItem("popup_2025_newyear_shown", "true"); } showPopup_2025_newyear(); </script>
过年灯笼
详情见这篇文章:给博客挂上赛博灯笼吧 – 阿草 Mimosa 的小屋
(注:这东西用 window.onload
加载,小心别与其他代码冲突。window.onload
的特性是其语句仅能执行一次。若有若干 window.onload
代码块,仅执行第一个 window.onload
。(我曾经踩过的大坑))
悼念色
为了表示悼念,使整个网站变为黑白色调的。在额外 CSS 的最底部添加下列代码即可:
/*网站黑白色(悼念)*/ html { filter: grayscale(100%); -webkit-filter: grayscale(100%); -moz-filter: grayscale(100%); -ms-filter: grayscale(100%); -o-filter: grayscale(100%); filter: url("data:image/svg+xml;utf8,#grayscale"); filter:progid:DXImageTransform.Microsoft.BasicImage(grayscale=1); -webkit-filter: grayscale(1); }
站内邮件发送内容
functions.php 1432 行 直接找注释 //发送评论通知邮件
就行
归档页面添加说说短文章
详见 Argon 主题:文章归档页面添加说说类型 – 阿草 Mimosa 的小屋
讨论区给多个特定邮箱添加标识
(为了便于管理,我基于 argon 主题管理页面设置的)
在 setting.php 最后头(但不是最后)添加
//讨论区 「朋友」标识 号代替邮箱输入 argon_update_option_allow_tags('friend_mail_sign');
然后,去前面(出现很多 tr 和 td、th 标签的地方)加上这些:
<tr><th class="subtitle"><h2><?php _e('讨论区朋友标识', 'argon');?></h2></th></tr> <tr><th class="subtitle" colspan="2"><h2><?php _e('讨论区朋友标识', 'argon');?></h2></th></tr> <tr> <th scope="row"> <label><?php _e('邮箱列表(每行一个)', 'argon');?></label> </th> <td> <textarea name="friend_mail_sign" rows="5" cols="50" placeholder="example1@qq.com example2@gmail.com" ><?php echo htmlspecialchars(get_option('friend_mail_sign')); ?></textarea> <p class="description"><?php _e('每行输入一个需要显示「朋友」标识的邮箱', 'argon');?></p> </td> </tr>
这样是为了在主题后台管理页面输入邮箱,然后让前端获取就行了。
所以,打开 functions.php 文件,找到 //评论样式格式化
注释(我的在 1077 行)
在博主标识后面添加
<?php $friendEmails = explode("\n", get_option('friend_mail_sign', '')); $friendEmails = array_map('trim', $friendEmails); // 去除首尾空格 $friendEmails = array_filter($friendEmails); // 过滤空行 if (in_array($comment->comment_author_email, $friendEmails)) { echo '<span class="badge badge-warning">' . __('朋友', 'argon') . '</span>'; } ?>
然后往下找,找到 //评论样式格式化 (说说预览界面)
注释,我们也要把说说预览给打上标签,过程和代码同上。
首页文章只显示人工摘要,而非截取文字
首页显示文章摘要可以更好地让读者了解文章内容。
但是,argon 主题的文章摘要功能开启后,若没有为文章添加人工摘要,则会自动截取文章的前部分文章作为摘要,这样的效果只会很丑,且无实际意义。
因此,修改主题代码:若有人工摘要则显示,若无人工摘要则不显示。
记得在 argon 主题选项 的 「文章内容预览截取字数」 填上数字
在 网站根目录 /wp-content/themes/argon/template-parts 中 找到 content-preview-1.php
(其中有多个 content-preview.php,经过 Mimosa 实测只改动第一个即可实现效果)
将
<?php if ($trim_words_count > 0){ ?> <div class="post-content"> <?php if (get_option("argon_hide_shortcode_in_preview") == 'true'){ $preview = wp_trim_words(do_shortcode(get_the_content('...')), $trim_words_count); }else{ $preview = wp_trim_words(get_the_content('...'), $trim_words_count); } if (post_password_required()){ $preview = __("这篇文章受密码保护,输入密码才能阅读", 'argon'); } if ($preview == ""){ $preview = __("这篇文章没有摘要", 'argon'); } if ($post -> post_excerpt){ $preview = $post -> post_excerpt; } echo $preview; ?> </div> <?php } ?>
替换为
<?php $preview = ''; if ($post -> post_excerpt){ $preview = $post -> post_excerpt; }elseif (post_password_required()){ $preview = __("这篇文章受密码保护,输入密码才能阅读", 'argon'); } if (!empty($preview)){ ?> <div class="post-content"> <?php if (get_option("argon_hide_shortcode_in_preview") == 'true'){ echo do_shortcode($preview); }else{ echo $preview; } ?> </div> <?php } ?>
即可。
文内外跳转 (文章锚点)
(理论适用于 h1-h6 标签 仅适用于 Argon)
添加至 footer.php 即可:转自 Bensz
<!-- 标题自动锚点: Start --> <script> window.addEventListener('load', function() { // 构建标题文本与 Argon ID 的映射表 const headers = document.querySelectorAll('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]'); const textToIdMap = new Map(); headers.forEach(header => { const id = header.id; const text = header.textContent.trim(); textToIdMap.set(text, id); // 标题文本 -> ID 映射 }); // 替换页面中的基于文本的锚点链接 const links = document.querySelectorAll('a[href^="#"]'); links.forEach(link => { const targetText = decodeURIComponent(link.getAttribute('href').slice(1)); // 获取锚点文本 if (textToIdMap.has(targetText)) { link.setAttribute('href', `#${textToIdMap.get(targetText)}`); // 替换为 Argon 的 ID } }); //文外跳转 if (window.location.hash) { const hash = window.location.hash.slice(1); // 去掉 # let targetElement; // 优先检查哈希值是否是一个有效的 ID targetElement = document.getElementById(hash); if (!targetElement) { // 如果哈希值是标题文本,检查映射表 const decodedHash = decodeURIComponent(hash); // 解码哈希值 if (textToIdMap.has(decodedHash)) { const targetId = textToIdMap.get(decodedHash); // 获取对应的 ID targetElement = document.getElementById(targetId); // 查找对应 ID 的元素 } // 替换图片的 src 属性 const lazyImages = document.querySelectorAll('img.lazyload[data-original]'); lazyImages.forEach(img => { img.src = img.getAttribute('data-original'); // 直接替换为真正的图片链接 }); } // 如果找到了目标元素,滚动到该元素 if (targetElement) { targetElement.scrollIntoView({ behavior: 'smooth', block: 'start' }); } } }); </script> <!-- 标题自动锚点: End -->