构建一个简约博皮的过程
2020-05-29 18:04
44 查看
前置
由于之前构建的皮肤 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 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.js
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 headerInnerPost 1044 Title = () => { 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.js
// 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 } fr 564 om '@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
- simple/index.js
唯一需要写的样板代码:
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 分离,在文章开头的配置介绍中提供了这个选项。最后推送上去就能在博客园切换到新皮肤了。
链接
相关文章推荐
- Builder 解析 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
- 译:Apache Maven-简化java的构建过程--比apache ant更多(2) 创建一个简单的maven项目
- W. :BUILD-MAX-HEAP,构建一个最大堆.6-3中描述的子过程,P76.
- 分步讲解构建一个网站的全过程
- 构建一个高性能的网页抓取器
- Linux中编译一个简单的源文件的过程
- 基于 vue2 + vuex 构建一个具有 45 个页面的大型单页面应用
- 从零开始构建一个centos+jdk7+tomcat7的镜像文件
- Java创建一个对象的过程
- 一个消息调度框架构建
- 如何用Maven构建一个project(一)—eclipse工具包配置
- 浅谈一个网页打开的全过程(涉及DNS、CDN、Nginx负载均衡等)
- 一个new失败问题的查找过程
- UIAPPlicationMain() 函数详解 (一个应用程序的启动过程)
- 用 JFace和SWT 构建一个简单的应用程序
- 用Elasticsearch构建电商搜索平台,一个极有代表性的基础技术架构和算法实践案例
- 通过两个数字构建一个连续的数组
- 使用React、Node.js、MongoDB、Socket.IO开发一个角色投票应用的学习过程(三)
- 一个采集系统的构建
- 一个小项目(爬虫)的诞生全过程