主题的模板改成 NexT.Pisces 后,背景看着有些单调,就想加点特效。
搜索一番,发现现在 NexT 主题可以很方便的引入自定义内容了,不需要修改主题的源文件,方便多了。
通过咨询 ChatGPT ,加上了雪花飘落的效果。
下面是具体的添加方法。
Hexo v5.4.2
NexT v8.15.0
适用于深色主题
修改主题设置
使用了 Javascript 和 CSS 来实现效果
在 NexT 主题的 _config.yaml
中,找到 custom_file_path
,取消对应的注释
_config.yaml1 2 3 4 5
| custom_file_path: style: source/_data/styles.styl bodyEnd: source/_data/body-end.njk
|
然后在 Hexo 目录下的 source
文件夹,创建 _data
文件夹
再创建 styles.styl
和 body-end.njk
文件
添加实现代码
添加 JS 代码
在 body-end.njk
中加入 JS 代码
将动画交给 CSS 处理,不过动态效果还是使用 JS 处理动画的表现更好
预览
source/_data/body-end.njk1 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
| <script> const snowflakes = ["⛄", "❄", "❄", "❆", "❅", "✥"]; function createSnowflake() { const snowflake = document.createElement("span"); snowflake.classList.add("snowflake"); const randomIndex = Math.floor(Math.random() * snowflakes.length); snowflake.textContent = snowflakes[randomIndex];
snowflake.style.left = `${Math.random() * 100}vw`; snowflake.style.top = `-30px`; const size = Math.random() * 18 + 10; snowflake.style.fontSize = `${size}px`; const opacity = Math.random() * 0.6 + (size > 18 ? 0.4 : 0); snowflake.style.setProperty("--opacity", opacity); const fallDuration = Math.random() * 10 + 10; const rotateDuration = Math.random() * 3 + 1;
snowflake.style.animationDuration = `${fallDuration}s, ${fallDuration}s`; const translateX = (Math.random() * 500 - 200); snowflake.style.setProperty("--translateX", `${translateX}px`); snowflake.style.setProperty("--translateY", `${window.innerHeight}px`);
document.body.appendChild(snowflake); setTimeout(() => { snowflake.remove(); }, fallDuration * 1000); } function snowfallAnimation() { const sidebarnav = document.querySelector('.sidebar'); const sidebarnavdisplay = window.getComputedStyle(sidebarnav).getPropertyValue('display'); if (sidebarnavdisplay !== 'none') { createSnowflake(); } setTimeout(snowfallAnimation, 150); } snowfallAnimation(); </script>
|
source/_data/body-end.njk1 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
| <script> const snowflakes = ["⛄", "❄", "❅", "❉", "✥"]; function createSnowflake() { const snowflake = document.createElement("span"); snowflake.classList.add("snowflake"); const randomIndex = Math.floor(Math.random() * snowflakes.length); snowflake.textContent = snowflakes[randomIndex];
snowflake.style.left = `${Math.random() * 100}vw`; snowflake.style.top = '0px'; const size = Math.random() * 18 + 10; snowflake.style.fontSize = `${size}px`; snowflake.style.opacity = Math.random() * 0.6 + (size > 18 ? 0.4 : 0); const translateYDuration = Math.random() * 20 + 5; const translateXDuration = Math.random() * 5 + 2; const rotationDuration = Math.random() * 3 + 1; const accumulateDuration = 10;
let startTime = null; function update(timestamp) { if (!startTime) startTime = timestamp; const progress = (timestamp - startTime) / 1000; let translateY = (progress / translateYDuration) * 2000; const translateX = Math.sin((progress / translateXDuration) * Math.PI) * 100; const rotation = (progress / rotationDuration) * 360;
if (translateY + snowflake.offsetHeight > window.innerHeight) { translateY = window.innerHeight - snowflake.offsetHeight;
if (progress > translateYDuration + accumulateDuration) { snowflake.remove(); return; } }
snowflake.style.transform = `translateY(${translateY}px) translateX(${translateX}px) rotate(${rotation}deg)`;
if (progress < translateYDuration + accumulateDuration) { requestAnimationFrame(update); } else { snowflake.remove(); } } requestAnimationFrame(update);
document.body.appendChild(snowflake); }
const snowflakeCreationInterval = 250;
function snowfallAnimation() { const sidebarnav = document.querySelector('.sidebar'); const sidebarnavdisplay = window.getComputedStyle(sidebarnav).getPropertyValue('display'); if (sidebarnavdisplay !== 'none') { createSnowflake(); } setTimeout(() => requestAnimationFrame(snowfallAnimation), snowflakeCreationInterval); }
requestAnimationFrame(snowfallAnimation); </script>
|
source/_data/body-end.njk1 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
| <script> let snowflakeInterval;
function createSnowflake() { const snowflake = document.createElement("span"); snowflake.classList.add("snowflake");
snowflake.textContent = Math.random() < 0.1 ? "⛄" : "❄";
snowflake.style.left = `${Math.random() * 100}vw`; snowflake.style.top = '0px';
const size = Math.random() * 18 + 12; snowflake.style.fontSize = `${size}px`; snowflake.style.opacity = Math.random() * 0.6 + 0.3;
const translateYDuration = Math.random() * 20 + 5; const translateXDuration = Math.random() * 5 + 2; const rotationDuration = Math.random() * 3 + 1;
let startTime = null;
function update(timestamp) { if (!startTime) startTime = timestamp;
const progress = (timestamp - startTime) / 1000; const translateY = (progress / translateYDuration) * 2000; const translateX = Math.sin((progress / translateXDuration) * Math.PI) * 100; const rotation = (progress / rotationDuration) * 360;
snowflake.style.transform = `translateY(${translateY}px) translateX(${translateX}px) rotate(${rotation}deg)`;
if (progress < translateYDuration) { requestAnimationFrame(update); } else { snowflake.remove(); } }
requestAnimationFrame(update); document.body.appendChild(snowflake); }
function startSnowfall() { const sidebarnav = document.querySelector('.sidebar'); const sidebarnavdisplay = window.getComputedStyle(sidebarnav).getPropertyValue('display'); if (sidebarnavdisplay !== 'none') { snowflakeInterval = setInterval(createSnowflake, 350); } }
function stopSnowfall() { clearInterval(snowflakeInterval); }
function handleVisibilityChange() { if (document.visibilityState === "visible") { startSnowfall(); } else { stopSnowfall(); } }
document.addEventListener("visibilitychange", handleVisibilityChange);
if (document.visibilityState === "visible") { startSnowfall(); } </script>
|
1
| snowflake.textContent = Math.random() < 0.1 ? "⛄" : "❄";
|
这里设置了 10% 会生成一个 ⛄ ,如果只想生成一种可以使用下面的代码
1 2
| snowflake.textContent = "❄"
|
添加样式
在 styles.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
| .snowflake { position: fixed; pointer-events: none; animation-name: snowflakeFallRotate, snowflakeFadeOut; animation-timing-function: linear; animation-iteration-count: 1; animation-fill-mode: forwards; color: white; pointer-events: none; z-index: -1; text-shadow: 0 0 1px rgba(255, 255, 255, 0.8), 0 0 5px rgba(255, 255, 255, 0.8); }
@keyframes snowflakeFallRotate { 0% { transform: translateY(0) translateX(0) rotate(0); } 70% { transform: translateY(var(--translateY)) translateX(var(--translateX)) rotate(720deg); } 100% { transform: translateY(var(--translateY)) translateX(var(--translateX)) rotate(800deg); } }
@keyframes snowflakeFadeOut { 0%, 90% { opacity: var(--opacity); } 100% { opacity: 0; } }
|
1 2 3 4 5 6 7 8 9
| .snowflake { position: fixed; color: white; display: block; pointer-events: none; z-index: -1; text-shadow: 0 0 5px rgba(255, 255, 255, 0.8), 0 0 10px rgba(255, 255, 255, 0.8); }
|
此时可以 hexo g && hexo s
预览了。
不过这时可以看到由于 NexT.Pisces 边栏和文章主体部分没有透明效果,效果并不好。
我们可以在 styles.styl
中加入下面内容,加上一点透明效果。
source/_data/styles.styl1 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
| .sidebar { opacity: 0.85; } .header, .main-inner { background-color: rgba(255,255,255,0.8); } .menu-item a:hover, .menu-item a.menu-item-active { background-color: rgba(150,150,150,0.1); } .site-brand-container { background-color: rgba(0,0,0,0.75); }
if (hexo-config('darkmode')) { @media (prefers-color-scheme: dark){ .sidebar { opacity: 0.8; } .header, .main-inner { background-color: rgba(39,39,39,0.75); } .site-brand-container { background-color: rgba(0,0,0,0.5); } } }
|
本地预览没有问题,既可以推送到服务端了。
限制在顶部 30%
看了几天,确实花里胡哨 😅 ,只在顶部有一点效果就不错
修改 JS
source/_data/body-end.njk1 2 3 4 5
| const fallDuration = Math.random() * 10 + 5;
snowflake.style.setProperty("--translateY", `${window.innerHeight * 0.3}px`);
|
修改样式文件动画的部分
source/_data/styles.styl1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @keyframes snowflakeFallRotate { 0% { transform: translateY(0) translateX(0) rotate(0); } 100% { transform: translateY(var(--translateY)) translateX(var(--translateX)) rotate(800deg); } }
@keyframes snowflakeFadeOut { 0%, 50% { opacity: var(--opacity); } 100% { opacity: 0; } }
|