# ShokaX 0.3.6 迁移 0.5.4
由于差异巨大,只得采取重新安装的方式
# 安装ShokaX
pnpm add hexo-cli -g
hexo init
pnpm add hexo-theme-shokax
node ./node_modules/hexo-theme-shokax/toolbox/hoistdep.mjs
同时为了方便管理,创建软链接
mklink /J .\themes\shokax .\node_modules\hexo-theme-shokax
# 启用主题
删除根目录下的_config.landscape.yml
打开根目录下的_config.yml
:
-
将
theme
更改为shokax
-
停用默认代码高亮
syntax_highlighter: false
-
添加如下配置在最末尾
markdown: render: # 渲染器设置 html: false # 过滤 HTML 标签 xhtmlOut: true # 使用 '/' 来闭合单标签 (比如 <br />)。 breaks: true # 转换段落里的 '\n' 到 <br>。 linkify: true # 将类似 URL 的文本自动转换为链接。 typographer: quotes: "“”‘’" plugins: # markdown-it 插件设置 - plugin: name: markdown-it-toc-and-anchor enable: true options: # 文章目录以及锚点应用的 class 名称,shoka 系主题必须设置成这样 tocClassName: "toc" anchorClassName: "anchor" - plugin: name: markdown-it-multimd-table enable: true options: multiline: true rowspan: true headerless: true - plugin: name: ./markdown-it-furigana enable: true options: fallbackParens: "()" - plugin: name: ./markdown-it-spoiler enable: true options: title: "你知道得太多了"
-
启动文件压缩,添加在最末尾
minify: js: enable: false # ShokaX 自带 esbuild 优化,不建议开启,其他主题建议开启 exclude: # 排除文件,接受 string[],需符合 micromatch 格式 css: enable: true # 开启 CSS 优化 options: targets: ">= 0.5%" # browserslist 格式的 target exclude: # 排除文件,接受 string[],需符合 micromatch 格式 html: minifier: html-minifier enable: true # 开启 HTML 优化 options: comments: false # 是否保留注释内容 exclude: # 排除文件,接受 string[],需符合 micromatch 格式 image: enable: false # 开启图片预处理和自动 WebP 化 options: avif: false webp: true # 预留配置项,现版本无作用 quality: 80 # 质量,支持1-100的整数、lossless或nearLossless effort: 2 # CPU 工作量,0-6之间的整数(越低越快) replaceSrc: false # 自动替换生成html中的本地图片链接为webp链接 # 对于使用 CI 工作流部署的用户,请注意本配置可能导致图片 404,如果出现请关闭 # 我们更建议使用 Service Worker 来在用户侧实现 replaceSrc 的功能,这将能够以一种侵入式更小的方式实现链接替换 exclude:
-
开启feed生成,添加在最末尾
feed: limit: 20 order_by: "-date" tag_dir: false category_dir: false rss: enable: true template: "node_modules/hexo-theme-shokax/layout/_alternate/rss.ejs" output: "rss.xml" atom: enable: true template: "node_modules/hexo-theme-shokax/layout/_alternate/atom.ejs" output: "atom.xml" jsonFeed: enable: true template: "node_modules/hexo-theme-shokax/layout/_alternate/json.ejs" output: "feed.json"
同时,将<root>/themes/shokax/source/assets
移动到<root>/source/_data/assets/
,同时添加自己头像avatar.jpg
在<root>/themes/shokax/_images.yml
中添加至少6张图片的链接
随后主题即可正常使用
# 加载动画替换
更改<root>/themes/shokax/layout/_partials/loading.pug
div(id="loading")
div(class="loader")
div(class="dot")
div(class="dot")
div(class="dot")
div(class="dot")
div(class="dot")
更改<root>/themes/shokax/layout/_partials/loading.pug
#loading {
@extend $fix-fullscreen;
background-color: var(--grey-1);
if(!hexo-config('loader.start')) {
display: none;
}
}
body
margin 0
.loader
position absolute
top 50%
left 40%
margin-left 10%
transform translate3d(-50%,-50%,0)
.dot
width 24px
height 24px
background #3ac
border-radius 100%
display inline-block
animation slide 1s infinite
for n in (1..5)
.dot:nth-child({n})
animation-delay (.1s * n)
background red(#3ac, (50 * n))
@keyframes slide
0%
transform scale(1)
50%
opacity .3
transform scale(2)
100%
transform scale(1)
# Live2D部署
# 本地编译
首先去官方仓库克隆项目stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platform
随后去Live2D官网下载SDK for Web 下载Live2D Cubism SDK for Web | Live2D Cubism
解压SDK后放入项目src
文件夹中,路径参考src/CubismSdkForWeb-5-r.4
修改tsconfig.json
,排除官方示例文件夹
{
"compilerOptions": {
"target": "es2017",
"module": "esnext",
"moduleResolution": "node",
"declaration": true,
"outDir": "build",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": false,
"skipLibCheck": true,
"removeComments": true,
"allowJs": true,
"paths": {
"@framework/*": [
"./src/CubismSdkForWeb-5-r.4/Framework/src/*"
],
"@demo/*": [
"./src/CubismSdkForWeb-5-r.4/Samples/TypeScript/Demo/src/*"
]
}
},
"include": ["src"],
"exclude": [
"node_modules",
"build",
"dist",
"src/CubismSdkForWeb-5-r.4/Samples/**/*.config.*"
]
}
随后在终端输入npm install
,安装完毕后输入npm run build
开始构建
# 推送仓库
构建完毕后在项目根目录下创建models
文件夹,并放入想使用的模型,我以莲的模型为例,路径为/models/ren
随后将项目推送到github,然后访问发布一个版本,这里我Release填为0.1.0
访问地址https://cdn.jsdelivr.net/gh/你的用户名/你的仓库名@发布的版本号/文件路径
,如果不填版本号默认拉取最新版
关于autoload.js,即为https://fastly.jsdelivr.net/gh/AquaOH/live2d-widget/dist/autoload.js
,
关于模型链接,即为https://fastly.jsdelivr.net/gh/AquaOH/live2d-widget/models/ren/L.model3.json
需要一段时间才能够同步,这个过程通常极其缓慢...
# 配置文件
为了能够正常使用,还需要修改autoload.js
将const live2d_path = 'https://fastly.jsdelivr.net/npm/live2d-widgets@1.0.0-rc.6/dist/';
的值改为你的值,比如我的为https://fastly.jsdelivr.net/gh/AquaOH/live2d-widget/dist/
同时在waifu-tips.json
中添加需要的模型,示例如下
"models": [
{
"name": "Ren",
"paths": [
"https://fastly.jsdelivr.net/gh/AquaOH/live2d-widget/models/ren/L.model3.json"
],
"message": "枯れない世界と終わる花"
},
{
"name": "Potion-Maker/Pio",
"paths": [
"https://fastly.jsdelivr.net/gh/fghrsh/live2d_api/model/Potion-Maker/Pio/index.json"
],
"message": "来自 Potion Maker 的 Pio 酱 ~"
},
{
"name": "Potion-Maker/Tia",
"paths": [
"https://fastly.jsdelivr.net/gh/fghrsh/live2d_api/model/Potion-Maker/Tia/index.json"
],
"message": "来自 Potion Maker 的 Tia 酱 ~"
}
]
# 引入博客
在/themes/shokax/layout/_partials/layout.pug
中找到这一段:
!= _js('siteInit.js')
!= shokax_inject('bodyEnd')
在这两行之间添加:
!= _js('siteInit.js')
script(src="https://fastly.jsdelivr.net/gh/AquaOH/live2d-widget/dist/autoload.js")
!= shokax_inject('bodyEnd')
同时设置下样式,在文件<Hexo根目录>\source\_data\custom.styl
中添加如下样式调节按钮颜色和live2d层级(没有请创建)
/* 修复 Live2D 的完整层级结构 */
#waifu,
#waifu-canvas,
#live2d {
z-index: 99999 !important;
}
#waifu-toggle {
background-color: #ffc1cc !important;
&:hover {
background-color: #ff8a95 !important;
}
}
#waifu-tool svg {
fill: #ffc1cc !important;
&:hover {
fill: #ff8a95 !important;
}
}
[data-theme="dark"] {
#waifu-toggle {
background-color: #cc9199 !important;
&:hover {
background-color: #cc5a66 !important;
}
}
#waifu-tool svg {
fill: #cc9199 !important;
&:hover {
fill: #cc5a66 !important;
}
}
}
# 工具按钮居右
在文件<Hexo根目录>\source\_data\custom.styl
中添加如下样式调节按钮位置
#tool {
left: auto !important;
right: 1rem !important;
&.affix {
right: 0px !important;
}
}
# 背景图片小方格
在文件<Hexo根目录>\source\_data\custom.styl
中添加如下样式
#header::before {
background: url("https://cdn.jsdelivr.net/gh/lavender816/CDN@1.8/img/dot.jpg");
content:'';
position:absolute;
height:100vh;
top:0;
bottom:0;
left:0;
right:0;
z-index:-4;
background-attachment:fixed;
}
# 更改加载动画
这两个文件替换成你想要的加载动画就行
themes/shokax/layout/_partials/loading.pug
div(class="loader")
div(class="dot")
div(class="dot")
div(class="dot")
div(class="dot")
div(class="dot")
themes/shokax/source/css/_common/components/third-party/loading.styl
body
margin 0
.loader
position absolute
top 50%
left 40%
margin-left 10%
transform translate3d(-50%,-50%,0)
.dot
width 24px
height 24px
background #3ac
border-radius 100%
display inline-block
animation slide 1s infinite
for n in (1..5)
.dot:nth-child({n})
animation-delay (.1s * n)
background red(#3ac, (50 * n))
@keyframes slide
0%
transform scale(1)
50%
opacity .3
transform scale(2)
100%
transform scale(1)
# 漂浮物
放入图片source/_data/assets/float.png
添加新文件themes/shokax/scripts/sakura.js
themes/shokax/scripts/sakura.js
添加新文件views/sakura.pug
script.
// 漂浮物
// 新增函数
// 首先获取 Url,然后把 Url 通过 // 截成两部分,再从后一部分中截取相对路径。如果截取到的相对路径中有参数,则把参数去掉。
// 获取相对路径
function GetUrlRelativePath()
{
var url = document.location.toString();
var arrUrl = url.split("//");
var start = arrUrl[1].indexOf("/");
var relUrl = arrUrl[1].substring(start);//stop省略,截取从start开始到结尾的所有字符
if(relUrl.indexOf("?") != -1){
relUrl = relUrl.split("?")[0];
}
return relUrl;
}
var allowAll = true; // true 则允许所有网页存在漂浮物
// 允许显示漂浮物的网址列表
var urlAllowList = [
"/about/",
"friends/",
]
var isAllowFloat = false; // 全局变量,允许使用漂浮特效
// 判断
function decide(){
isAllowFloat = true;
if(!allowAll){
// 判断当前页面是否为指定页面
var url = GetUrlRelativePath();
var i = 0;
for(;i<urlAllowList.length;i++){
if(url===urlAllowList[i]){
// console.log(i);
isAllowFloat = true;
break;
}
}
if(i===urlAllowList.length){
isAllowFloat = false;
}
}
console.log(isAllowFloat)
if(isAllowFloat)startFloat();
}
var stop, staticx;
var img = new Image();
img.src = "/assets/float.png"; // 图片
function Float(x, y, s, r, fn) {
this.x = x;
this.y = y;
this.s = s;
this.r = r;
this.fn = fn;
}
Float.prototype.draw = function(cxt) {
cxt.save();
var xc = 40 * this.s / 4;
cxt.translate(this.x, this.y);
cxt.rotate(this.r);
cxt.drawImage(img, 0, 0, 35 * this.s, 35 * this.s)
// 漂浮物大小
cxt.restore();
}
Float.prototype.update = function() {
this.x = this.fn.x(this.x, this.y);
this.y = this.fn.y(this.y, this.y);
this.r = this.fn.r(this.r);
if (this.x > window.innerWidth || this.x < 0 || this.y > window.innerHeight || this.y < 0) {
this.r = getRandom('fnr');
if (Math.random() > 0.4) {
this.x = getRandom('x');
this.y = 0;
this.s = getRandom('s');
this.r = getRandom('r');
} else {
this.x = window.innerWidth;
this.y = getRandom('y');
this.s = getRandom('s');
this.r = getRandom('r');
}
}
}
FloatList = function() {
this.list = [];
}
FloatList.prototype.push = function(float) {
this.list.push(float);
}
FloatList.prototype.update = function() {
for (var i = 0, len = this.list.length; i < len; i++) {
this.list[i].update();
}
}
FloatList.prototype.draw = function(cxt) {
for (var i = 0, len = this.list.length; i < len; i++) {
this.list[i].draw(cxt);
}
}
FloatList.prototype.get = function(i) {
return this.list[i];
}
FloatList.prototype.size = function() {
return this.list.length;
}
function getRandom(option) {
var ret, random;
switch (option) {
case 'x':
ret = Math.random() * window.innerWidth;
break;
case 'y':
ret = Math.random() * window.innerHeight;
break;
case 's':
ret = Math.random();
break;
case 'r':
ret = Math.random() * 6;
break;
case 'fnx':
random = -0.5 + Math.random() * 1;
ret = function(x, y) {
return x + 0.5 * random - 0.6;
//x 轴速度
}
;
break;
case 'fny':
random = 0.8 + Math.random() * 0.7
//y 轴速度
ret = function(x, y) {
return y + random;
}
;
break;
case 'fnr':
random = Math.random() * 0.03;
ret = function(r) {
return r + random;
}
;
break;
}
return ret;
}
function startFloat() {
requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || window.oRequestAnimationFrame;
var canvas = document.createElement('canvas'), cxt;
staticx = true;
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
canvas.setAttribute('style', 'position: fixed;left: 0;top: 0;pointer-events: none;');
canvas.setAttribute('id', 'canvas_float');
document.getElementsByTagName('body')[0].appendChild(canvas);
cxt = canvas.getContext('2d');
var floatList = new FloatList();
for (var i = 0; i < 10; i++) {
// 漂浮物数量
var float, randomX, randomY, randomS, randomR, randomFnx, randomFny;
randomX = getRandom('x');
randomY = getRandom('y');
randomR = getRandom('r');
randomS = getRandom('s');
randomFnx = getRandom('fnx');
randomFny = getRandom('fny');
randomFnR = getRandom('fnr');
float = new Float(randomX,randomY,randomS,randomR,{
x: randomFnx,
y: randomFny,
r: randomFnR
});
float.draw(cxt);
floatList.push(float);
}
stop = requestAnimationFrame(function() {
cxt.clearRect(0, 0, canvas.width, canvas.height);
floatList.update();
floatList.draw(cxt);
stop = requestAnimationFrame(arguments.callee);
})
}
window.onresize = function() {
if(!isAllowFloat)return;
var canvasSnow = document.getElementById('canvas_float');
canvasSnow.width = window.innerWidth;
canvasSnow.height = window.innerHeight;
}
function stopp(e) {
if (!e && document.getElementById("canvas_float")) {
var child = document.getElementById("canvas_float");
child.parentNode.removeChild(child);
window.cancelAnimationFrame(stop);
} else if (e && !document.getElementById("canvas_float")) {
decide();
}
}
window.addEventListener("DOMContentLoaded",decide);