因爲以前構建的皮膚 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 分離,在文章開頭的配置介紹中提供了這個選項。最後推送上去就能在博客園切換到新皮膚了。