我個人很喜歡Butterfly主題一進去後就是一張大圖片很有震撼感,加上會出現打字特效顯示名言金句,所以打算將這個效果也套過來我的網站。
_config.kira.yml
加入設定把這些東西直接加在最底部就行了
hero:
enable: true
image: /images/<圖片名稱> # 首屏大圖
title: <大標題> # 不填會用 site.title
typing: true # true=打字特效;false=淡入/淡出輪播
typingSpeed: 32 # 打字速度(毫秒/字;typing=true 才有效)
rotateInterval: 4000 # 每句停留毫秒(typing=false 用)
subtitles: # 你要輪播的句子(可放多句)
- 我最擅長的就是一蹶不振。
- 任何困難都能將我擊倒。
之後只改 subtitles 就能換輪播內容;想改速度、是否打字也都在這裡調。
butterfly是使用Hitokoto這個網站進行隨機產生句子的,類似的網站也有其他可以自己選擇。
英文的一言網
當然你也可以用歷史上的今天這種網站,有很多東西可以加,不過我只想用我自己的爛句子。
要用的話可以點進去那些一言網內有寫教學(之後可能會加在側邊吧,但首頁我只想放我自己的)
#加上Hitokoto舉例
hero:
enable: true
image: /images/<圖片名稱> # 首屏大圖
title: <大標題> # 不填會用 site.title
typing: true # true=打字特效;false=淡入/淡出輪播
typingSpeed: 32 # 打字速度(毫秒/字;typing=true 才有效)
rotateInterval: 4000 # 每句停留毫秒(typing=false 用)
subtitles: # 你要輪播的句子(可放多句)
- 我最擅長的就是一蹶不振。
- 任何困難都能將我擊倒。
hitokoto: # (可選)要的分類,留空代表全隨機
categories: [a, b, c] # a動畫 b漫畫 c遊戲 d文學 e原創 f網路 g其他 h影視 i詩詞 j諺語 k歌詞 l成語
建立資料夾與檔案:
mkdir -p scripts
touch scripts/home-hero.js
把下面整段貼進 scripts/home-hero.js
:
/* global hexo */
// 讀 hero 設定(優先 _config.kira.yml)
function getHeroCfg(ctx) {
return (ctx.theme && ctx.theme.config && ctx.theme.config.hero) ||
(ctx.config && ctx.config.hero) || {};
}
function esc(s=''){return String(s).replace(/[&<>"']/g, m=>({ '&':'&','<':'<','>':'>','"':'"',"'":''' }[m]));}
hexo.extend.filter.register('after_render:html', function (html, data) {
if (!data || !data.path) return html;
const isHome = String(data.path).replace(/\\/g,'/') === 'index.html';
if (!isHome) return html;
const cfg = getHeroCfg(this);
if (cfg.enable === false) return html;
const img = esc(cfg.image || '/images/hero.jpg');
const title = esc(cfg.title || this.config.title || 'Welcome');
const typing = !!cfg.typing;
const speed = Number(cfg.typingSpeed || 32);
const hold = Number(cfg.rotateInterval || 4000);
const list = Array.isArray(cfg.subtitles) && cfg.subtitles.length ? cfg.subtitles : ['Welcome.'];
const json = JSON.stringify(list);
const headInject = `
<style id="home-hero-css">
.hero-banner{
position:relative;
width:100vw;
height:100svh;
margin:0 0 28px;
margin-left:calc(50% - 50vw);
overflow:hidden;
}
.hero-bg{position:absolute;inset:0;background-size:cover;background-position:center;filter:brightness(.82)}
.hero-bg::after{content:"";position:absolute;inset:0;background:rgba(0,0,0,.25)}
.hero-inner{position:absolute;inset:0;display:flex;flex-direction:column;justify-content:center;align-items:center;text-align:center;color:#fff;padding:0 1rem}
.hero-title{font-weight:800;line-height:1.1;font-size:clamp(36px,5vw,64px);text-shadow:0 4px 24px rgba(0,0,0,.5)}
.hero-sub-wrap{display:inline-flex;align-items:baseline;gap:.25rem;opacity:0;transition:opacity .6s ease}
.hero-sub{margin-top:.75rem;font-size:clamp(14px,2vw,20px);opacity:.92;text-shadow:0 2px 12px rgba(0,0,0,.4);min-height:1.6em}
.typed-cursor{display:${typing ? 'inline-block' : 'none'};margin-left:2px;opacity:1;animation:typed-cursor-blink .7s infinite}
@keyframes typed-cursor-blink{0%,45%{opacity:1}46%,100%{opacity:0}}
/* ↓ 向下箭頭按鈕(置底中) */
.hero-down{
position:absolute; left:50%; bottom:18px; transform:translateX(-50%);
width:42px; height:42px; border-radius:9999px;
border:2px solid rgba(255,255,255,.9);
color:#fff; background:rgba(0,0,0,.18);
display:flex; align-items:center; justify-content:center;
cursor:pointer; transition:background .2s ease, transform .2s ease, opacity .2s ease;
backdrop-filter:blur(2px);
animation:hero-bounce 1.8s infinite;
}
.hero-down:hover{ background:rgba(0,0,0,.28); transform:translateX(-50%) translateY(2px); }
@keyframes hero-bounce{ 0%,100%{ bottom:18px } 50%{ bottom:24px } }
.hero-down svg{ display:block }
</style>`;
const bodyInject = `
<div class="hero-banner" role="banner" aria-label="Site hero">
<div class="hero-bg" style="background-image:url('${img}')"></div>
<div class="hero-inner">
<h1 class="hero-title">${title}</h1>
<div class="hero-sub-wrap" id="hero-sub-wrap">
<div class="hero-sub" id="hero-sub" aria-live="polite"></div>
<span class="typed-cursor">|</span>
</div>
<!-- 向下箭頭 -->
<button class="hero-down" id="hero-down" aria-label="捲動到內容">
<svg width="20" height="20" viewBox="0 0 24 24" aria-hidden="true">
<path fill="currentColor" d="M12 16.5L4.5 9l1.4-1.4L12 13.7l6.1-6.1L19.5 9z"/>
</svg>
</button>
</div>
</div>
<script>
(function(){
var items = ${json};
var el = document.getElementById('hero-sub');
var wrap = document.getElementById('hero-sub-wrap');
var typing = ${typing ? 'true' : 'false'};
var speed = ${speed};
var hold = ${hold};
var i = 0;
function setOpacity(v){ wrap.style.opacity = v; }
function fadeIn(cb){ requestAnimationFrame(function(){ setOpacity(1); if(cb) setTimeout(cb, 600); }); }
function fadeOut(cb){ setOpacity(0); if(cb) setTimeout(cb, 600); }
function typeText(text, done){
el.textContent = '';
var j = 0;
(function tick(){
if (j < text.length){
el.textContent += text.charAt(j++);
setTimeout(tick, speed);
} else { if (done) done(); }
})();
}
function show(idx){
var text = String(items[idx]);
if (typing){
setOpacity(1);
typeText(text, function(){ setTimeout(next, hold); });
} else {
el.textContent = text;
fadeIn(function(){ setTimeout(function(){ fadeOut(next); }, hold); });
}
}
function next(){
i = (i + 1) % items.length;
if (typing){ fadeOut(function(){ show(i); }); }
else { show(i); }
}
if (!items.length) items = ['Welcome.'];
if (typing){ setOpacity(0); fadeOut(function(){ show(0); }); } else { show(0); }
// ↓ 點擊向下箭頭,平滑捲到下一個區塊
// ↓ 點擊向下箭頭,平滑捲到 Hero 底部(自動扣固定導覽列)
var btn = document.getElementById('hero-down');
if (btn){
btn.addEventListener('click', function(){
var hero = document.querySelector('.hero-banner');
if (!hero) return;
// Hero 底部的頁面座標
var y = hero.getBoundingClientRect().bottom + window.pageYOffset;
// 如果有固定 header,就扣掉高度(常見 class/元素都掃過)
var header = document.querySelector('.kira-header, header, .navbar, .site-header');
if (header && getComputedStyle(header).position === 'fixed') {
y -= header.offsetHeight || 0;
}
window.scrollTo({ top: Math.max(0, Math.round(y)), behavior: 'smooth' });
});
}
})();
</script>`;
html = html.replace('</head>', headInject + '\n</head>');
html = html.replace('<body>', '<body>\n' + bodyInject);
return html;
});
這樣就能有打字特效加上輪播句子(我最喜歡的就是模擬打字的特效)
以及有一個按鈕可以按一下就往下翻滾畫面
#加上Hitokoto舉例
// JSONP 取得 Hitokoto
function jsonp(url, cb){
var fn = 'jp_' + Math.random().toString(36).slice(2);
window[fn] = function(d){ cb(d); delete window[fn]; s.remove(); };
var s = document.createElement('script');
s.src = url + (url.indexOf('?')>-1?'&':'?') + 'callback=' + fn;
document.body.appendChild(s);
}
var toTW = (window.OpenCC && OpenCC.Converter) ? OpenCC.Converter({ from: 'cn', to: 'tw' }) : function(s){return s;};
jsonp('${hitokotoUrl}', function(d){
var s = (d && d.hitokoto) ? ('「' + d.hitokoto + '」 — ' + (d.from_who || d.from || '一言')) :
(fallback.length ? rand(fallback) : 'Welcome.');
s = toTW(s); // 轉繁體
start(s);
});
})();
</script>`;
改高度:把 height:100svh
換成 82vh
或 560px
遮罩深淺:改 .hero-bg::after
的 rgba(0,0,0,.25)
(越大越暗)
只要版心寬度、不要全幅:把 .hero-banner
的
width:100vw
改成 width:100%
,並刪掉 margin-left:calc(50% - 50vw)
字幕只顯示一行:在 .hero-sub
補 white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
今天更新的東西感覺有點多,明天就輕鬆一點更新小東西吧