由于之前构建的皮肤 reacg 偏二次元风,尽管提供了大量配置(包括几乎任何颜色、插件等的配置),依然有人吐槽花里胡哨,遂重新构建了一款简约风格的博客园皮肤, 正如你所见。下文我将从零介绍它的构建过程,构建它最快花费一个小时到几个小时。由于之前做了大量工作,所以现在按照流程走一遍就完事了。
准备工作环境:node & npm
git clone https://gitee.com/guangzan/awescnb.git
运行 npm install 安装依赖
在 themes 文件夹下新建 simple 文件夹、simple/index.js
配置config/options.js,这是 webpack 的配置:
module.exports = { themeName: 'simple', template: 'index', eslint: true, sourceMap: false, openAnalyzer: true, cssExtract: false, openBrowser: false, // ... }看个人所需,我在这里简单配置如下:
themeName: 'simple' 对应 themes/simple 文件夹
template: 运行 npm start 打开博客园首页模板
开启 eslint
开启 Analyzer,运行 npm run build 时我需要检查最终构建的体积大小
cssExtract 先不分离 css,构建完成直接推送到远端,直接切换线上版本
开始simple/style 下存放样式文件
index.scss
index.m.scss
markdown.scss
reset.scss
tools.scss
最终只在入口文件 index.js 中导入 index.scss,其他 scss 由 index.scss 引入,由于之前编好了样式代码,所以需要新编写的样式极少。
simple/plugins/index.js
引入构建好的博客园插件,不需要写任何功能代码,及其样式。
import footer from '@plugins/footer' import highlight from '@plugins/highlight' import copy from '@plugins/copy' import linenumbers from '@plugins/linenumbers' import imagebox from '@plugins/imagebox' import commentsAvatars from '@plugins/commentsAvatars' import dragMenu from '@plugins/dragMenu' import donation from '@plugins/donation' import emoji from '@plugins/emoji' import player from '@plugins/player' import postMessage from '@plugins/postMessage' import postSignature from '@plugins/postSignature' import postTopimage from '@plugins/postTopimage' import notice from '@plugins/notice' const plugins = () => { footer() highlight() copy() linenumbers() imagebox() commentsAvatars() donation() dragMenu() emoji() player() postMessage() postSignature() postTopimage() notice() } module.exports = plugins构建 footer 链接
mac 样式代码高亮
代码行号
图片灯箱
显示评论区头像
捐增二维码
按钮工具条(返回顶部,推荐,收藏。。。)
评论框表情
播放器
在文章头部构建文章信息
构建文章签名信息
文章头图
通知功能
由于这个皮肤的基调是简约,所以只引入一些常用的功能模块。花里胡哨的就不考虑了。
simple/build/index.js
index.js 引入了一些其他 JavaScript 用来做一些调整,例如 simple/build 文件夹下我还写了
catalog(文章目录)
header(头部导航逻辑)
scroll(滚动控制)
side (侧边栏逻辑)
catalog/index.js
代码有些长,我将它折叠import './index.scss' import { pageName, userAgent, hasPostTitle, getClientRect, throttle, } from '@tools' const { enable } = window.opts.catalog // 构建目录 function build() { let $catalogContainer = $( `<div> <div><h3>目录</h3></div> </div>`, ) const $ulContainer = $('<ul></ul>') const titleRegExp = /^h[1-3]$/ $('#cnblogs_post_body') .children() .each(function() { if (titleRegExp.test(this.tagName.toLowerCase())) { if ($(this).text().length === 0) return // 如果标题为空 只有 # let id let text if (this.id !== '') { id = this.id text = this.childNodes.length === 2 ? this.childNodes[1].nodeValue : this.childNodes[0].nodeValue } else { if (this.childNodes.length === 2) { const value = this.childNodes[1].nodeValue text = value ? value : $(this.childNodes[1]).text() } else { const value = this.childNodes[0].nodeValue text = value ? value : $(this.childNodes[0]).text() // 处理标题被 span 包裹的情况 } id = text.trim() $(this).attr('id', id) } const title = ` <li> <a href='#${id}'>${text}</a> </li> ` $ulContainer.append(title) } }) const $catalog = $($catalogContainer.append($ulContainer)) $('#sidebar_news').after($catalog) } function noCatalog() { if (pageName() !== 'post') return // to do something } // 设置目录活跃标题样式 function setActiveCatalogTitle() { $(window).scroll( throttle( function() { for (let i = $('#catalog ul li').length - 1; i >= 0; i--) { const titleId = $($('#catalog ul li')[i]) .find('a') .attr('href') .replace(/[#]/g, '') const postTitle = document.querySelector( `#cnblogs_post_body [id='${titleId}']`, ) if (getClientRect(postTitle).top <= 10) { if ( $($('#catalog ul li')[i]).hasClass('catalog-active') ) return $($('#catalog ul li')[i]).addClass('catalog-active') $($('#catalog ul li')[i]) .siblings() .removeClass('catalog-active') return } } }, 50, 1000 / 60, ), ) } function setCatalogToggle() { $(window).scroll( throttle( function() { if ($('#catalog ul').css('display') === 'none') return const bottom = getClientRect( document.querySelector('#sideBarMain'), ).bottom if (bottom <= 0) { $('#catalog').addClass('catalog-sticky') } else { $('#catalog').removeClass('catalog-sticky') } }, 50, 1000 / 60, ), ) } function toggle() { $('.catalog-title').click(function() { $('#catalog ul').toggle('fast', 'linear', function() { $(this).css('display') === 'none' ? $('.catalog-title').removeClass('is-active') : $('.catalog-title').addClass('is-active') }) }) } function catalog() { if ( enable && hasPostTitle() && pageName() === 'post' && userAgent() === 'pc' ) { build() setActiveCatalogTitle() setCatalogToggle() toggle() } else { noCatalog() } } module.exports = catalog