因为以前构建的皮肤 reacg 偏二次元风,尽管提供了大量配置(包括几乎任何颜色、插件等的配置),依然有人吐槽花里胡哨,遂从新构建了一款简约风格的博客园皮肤, 正如你所见。下文我将从零介绍它的构建过程,构建它最快花费一个小时到几个小时。因为以前作了大量工做,因此如今按照流程走一遍就完事了。css
npm install
安装依赖config/options.js,这是 webpack 的配置:node
module.exports = { themeName: 'simple', template: 'index', eslint: true, sourceMap: false, openAnalyzer: true, cssExtract: false, openBrowser: false, // ... }
看我的所需,我在这里简单配置以下:webpack
最终只在入口文件 index.js 中导入 index.scss,其余 scss 由 index.scss 引入,因为以前编好了样式代码,因此须要新编写的样式极少。git
引入构建好的博客园插件,不须要写任何功能代码,及其样式。github
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
因为这个皮肤的基调是简约,因此只引入一些经常使用的功能模块。花里胡哨的就不考虑了。web
index.js 引入了一些其余 JavaScript 用来作一些调整,例如 simple/build 文件夹下我还写了npm
catalog/index.jsmarkdown
import './index.scss' import { pageName, userAgent, hasPostTitle, getClientRect, throttle, } from '@tools' const { enable } = window.opts.catalog // 构建目录 function build() { let $catalogContainer = $( `<div id="catalog"> <div class='catalog-title'><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 class='${this.nodeName.toLowerCase()}-list'> <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
header/index.jsapp
import './index.scss' import { pageName, userAgent } from '@tools' // header右侧按钮容器 const buildHeader = () => { const gitee = window.opts.gitee $('#navList').after(`<div class="navbar-end"></div>`) $('#blog_nav_newpost').appendTo('.navbar-end') $( `<a href="https://guangzan.gitee.io/awescnb-docs/" id="header-awescnb">构建新皮肤</a>`, ).appendTo('.navbar-end') $(`<a href="${gitee.url}" id="header-gitee">开源主页</a>`).appendTo( '.navbar-end', ) } // 构建header昵称 const headerNickname = () => { $('#Header1_HeaderTitle').text($('#profile_block a:first').text()) } // header头像 const buildAva = () => { const { avatar } = window.opts.theme $('#blogLogo').attr('src', `${avatar}`) } // 随笔页构建文章题目 const headerInnerPostTitle = () => { if (pageName() !== 'post') return if (userAgent() !== 'pc') return let title = $('.post .postTitle') .text() .replace(/\s*/g, '') const titleLength = title.length let offset = '' if (0 <= titleLength && titleLength < 10) offset = '-180%' if (10 <= titleLength && titleLength < 15) offset = '-140%' if (15 <= titleLength && titleLength < 20) offset = '-100%' if (20 <= titleLength && titleLength < 25) offset = '-65%' if (25 <= titleLength && titleLength < 28) offset = '-60%' if (titleLength >= 28) { title = title.substring(0, 28) + '...' offset = '-60%' } $('#navList').append(`<span class='header-posttitle'>${title}</span>`) $('head').append( `<style> .header-posttitle {transform: translate3d(${offset}, 300%, 0);} #header.is-active .header-posttitle {transform: translate3d(${offset}, 0, 0);} </style>`, ) } // header移动端菜单 const headerBtn = () => { const ele = `<div id="navbarBurger" class="navbar-burger burger" data-target="navMenuMore"> <span></span> <span></span> <span></span> </div>` $('#blogTitle').append(ele) $('#navbarBurger').click(function() { $(this).toggleClass('is-active') $('#navigator').toggleClass('is-active') }) } // 建立自定义图标容器及其图标 const customLinks = () => { const github = window.opts.github // wrap $('.navbar-end').prepend(`<div class="custom-links"></div>`) $('#blogTitle h2').after(`<div class="custom-links"></div>`) // github icon if (github.enable) { $('.custom-links').append(`<a class="github" href="${github.url}"></a>`) } // qq // $('.custom-links').append(`<a class="qq"></a>`) // 知乎 $('.custom-links').append(`<a class="zhihu"></a>`) } // 首页 header 不要上下翻滚 const preventHeaderChange = () => { if (pageName() !== 'index') return $('#header').addClass('navlist-fix') } const header = () => { headerNickname() buildHeader() buildAva() headerBtn() customLinks() headerInnerPostTitle() preventHeaderChange() } module.exports = header
scroll/index.jside
// import './index.scss' import { userAgent } from '@tools' // 只触发一次向上或向下 // 若是又从新反向滚动则再触发一次 function scrollOnce() { function scrollFunc() { let scrollDirection if (!scrollAction) { scrollAction = window.pageYOffset } let diff = scrollAction - window.pageYOffset if (diff < 0) { scrollDirection = 'down' } else if (diff > 0) { scrollDirection = 'up' } else { // First scroll event } scrollAction = window.pageYOffset return scrollDirection } let scrollAction, originalDir $(window).scroll(function() { if (userAgent() !== 'pc') return let direction = scrollFunc() if (direction && originalDir != direction) { if (direction == 'down') { $('#header').addClass('is-active') $('#catalog').addClass('catalog-scroll-up') $('#catalog').removeClass('catalog-scroll-down') } else { $('#header').removeClass('is-active') $('#catalog').removeClass('catalog-scroll-up') $('#catalog').addClass('catalog-scroll-down') } originalDir = direction } }) } function scroll() { scrollOnce() // ... } module.exports = scroll
side/index.js
import './index.scss' import { poll } from '@tools' import { actions } from '@constants/element' const sideItemToggle = () => { for (const { title, content } of actions) { if (!title.length) continue $(title).click(function() { $(content).toggle('fast', 'linear', function() { $(this).css('display') === 'none' ? $(title).removeClass('is-active') : $(title).addClass('is-active') }) }) } } const addCalendarTitle = () => { $('#blog-calendar').prepend(`<div id="blog-calendar-title">博客日历</div>`) } const side = () => { addCalendarTitle() setTimeout(() => { poll($('#blog-sidecolumn').length, sideItemToggle) }, 0) } module.exports = side
惟一须要写的样板代码:
import './style/index.scss' import AwesCnb from '@awescnb' class Simple extends AwesCnb { constructor() { super() super.init(this.init) } init() { require('./build')() require('./plugins')() } } new Simple()
npm start
分别对首页、随笔详情页、标签页等调整npm run build
打包皮肤 simple 全部样式和逻辑加起来有 130+kb,没有异常。若是想拥有更好的体验能够将 css 分离,在文章开头的配置介绍中提供了这个选项。最后推送上去就能在博客园切换到新皮肤了。