mirror of
https://github.com/jerryc127/hexo-theme-butterfly.git
synced 2026-06-05 21:01:46 +08:00
feat: 不蒜子可配置 CDN closed #1132
feat: gallery 標簽外掛增加圖片懶加載,增加 lazyload rowHeight 和 limit 屬性配置 feat: 可設置固定導航欄 closed #1150 fix: 修復開啟懶加載後,再使用 flink_url 的方式引入友鏈數據,友鏈頭像有可能不顯示的 bug closed #1146 fix: 修復閲讀模式下,代碼塊的背景顏色仍顯示彩色背景的 bug (自定義代碼塊) closed #1139 improvement: 搜索結果換行時不會拆分單詞顯示 improvement: 優化搜索結果顯示滾動條位置 improvement: css/js 優化
This commit is contained in:
+73
-47
@@ -232,14 +232,42 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
* justified-gallery 圖庫排版
|
||||
*/
|
||||
const runJustifiedGallery = function (ele) {
|
||||
ele.forEach(item => {
|
||||
const $imgList = item.querySelectorAll('img')
|
||||
|
||||
$imgList.forEach(i => {
|
||||
const dataLazySrc = i.getAttribute('data-lazy-src')
|
||||
if (dataLazySrc) i.src = dataLazySrc
|
||||
btf.wrap(i, 'div', { class: 'fj-gallery-item' })
|
||||
const htmlStr = arr => {
|
||||
let str = ''
|
||||
const replaceDq = str => str.replace(/"/g, '"') // replace double quotes to "
|
||||
arr.forEach(i => {
|
||||
const alt = i.alt ? `alt="${replaceDq(i.alt)}"` : ''
|
||||
const title = i.title ? `title="${replaceDq(i.title)}"` : ''
|
||||
str += `<div class="fj-gallery-item"><img src="${i.url}" ${alt + title}"></div>`
|
||||
})
|
||||
return str
|
||||
}
|
||||
|
||||
const lazyloadFn = (i, arr) => {
|
||||
const loadItem = i.getAttribute('data-limit')
|
||||
const arrLength = arr.length
|
||||
if (arrLength > loadItem) i.insertAdjacentHTML('beforeend', htmlStr(arr.splice(0, loadItem)))
|
||||
else {
|
||||
i.insertAdjacentHTML('beforeend', htmlStr(arr))
|
||||
i.classList.remove('lazyload')
|
||||
}
|
||||
return arrLength > loadItem ? loadItem : arrLength
|
||||
}
|
||||
|
||||
ele.forEach(item => {
|
||||
const arr = JSON.parse(item.querySelector('.gallery-data').textContent)
|
||||
if (!item.classList.contains('lazyload')) item.innerHTML = htmlStr(arr)
|
||||
else {
|
||||
lazyloadFn(item, arr)
|
||||
const limit = item.getAttribute('data-limit')
|
||||
const clickBtnFn = () => {
|
||||
const lastItemLength = lazyloadFn(item, arr)
|
||||
fjGallery(item, 'appendImages', item.querySelectorAll(`.fj-gallery-item:nth-last-child(-n+${lastItemLength})`))
|
||||
btf.loadLightbox(item.querySelectorAll('img'))
|
||||
lastItemLength < limit && item.nextElementSibling.removeEventListener('click', clickBtnFn)
|
||||
}
|
||||
item.nextElementSibling.addEventListener('click', clickBtnFn)
|
||||
}
|
||||
})
|
||||
|
||||
if (window.fjGallery) {
|
||||
@@ -258,13 +286,12 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
* rightside scroll percent
|
||||
*/
|
||||
const rightsideScrollPercent = currentTop => {
|
||||
const perNum = btf.getScrollPercent(currentTop,document.body)
|
||||
const perNum = btf.getScrollPercent(currentTop, document.body)
|
||||
const $goUp = document.getElementById('go-up')
|
||||
if ( perNum < 95 ) {
|
||||
if (perNum < 95) {
|
||||
$goUp.classList.add('show-percent')
|
||||
$goUp.querySelector('.scroll-percent').textContent = perNum
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$goUp.classList.remove('show-percent')
|
||||
}
|
||||
}
|
||||
@@ -297,41 +324,40 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
const isShowPercent = GLOBAL_CONFIG.percent.rightside
|
||||
|
||||
const scrollTask = btf.throttle(() => {
|
||||
const currentTop = window.scrollY || document.documentElement.scrollTop
|
||||
const isDown = scrollDirection(currentTop)
|
||||
if (currentTop > 56) {
|
||||
if (isDown) {
|
||||
if ($header.classList.contains('nav-visible')) $header.classList.remove('nav-visible')
|
||||
if (isChatBtnShow && isChatShow === true) {
|
||||
chatBtnHide()
|
||||
isChatShow = false
|
||||
}
|
||||
} else {
|
||||
if (!$header.classList.contains('nav-visible')) $header.classList.add('nav-visible')
|
||||
if (isChatBtnHide && isChatShow === false) {
|
||||
chatBtnShow()
|
||||
isChatShow = true
|
||||
}
|
||||
}
|
||||
$header.classList.add('nav-fixed')
|
||||
if (window.getComputedStyle($rightside).getPropertyValue('opacity') === '0') {
|
||||
$rightside.style.cssText = 'opacity: 0.8; transform: translateX(-58px)'
|
||||
const currentTop = window.scrollY || document.documentElement.scrollTop
|
||||
const isDown = scrollDirection(currentTop)
|
||||
if (currentTop > 56) {
|
||||
if (isDown) {
|
||||
if ($header.classList.contains('nav-visible')) $header.classList.remove('nav-visible')
|
||||
if (isChatBtnShow && isChatShow === true) {
|
||||
chatBtnHide()
|
||||
isChatShow = false
|
||||
}
|
||||
} else {
|
||||
if (currentTop === 0) {
|
||||
$header.classList.remove('nav-fixed', 'nav-visible')
|
||||
if (!$header.classList.contains('nav-visible')) $header.classList.add('nav-visible')
|
||||
if (isChatBtnHide && isChatShow === false) {
|
||||
chatBtnShow()
|
||||
isChatShow = true
|
||||
}
|
||||
$rightside.style.cssText = "opacity: ''; transform: ''"
|
||||
}
|
||||
|
||||
isShowPercent && rightsideScrollPercent(currentTop)
|
||||
|
||||
if (document.body.scrollHeight <= innerHeight) {
|
||||
$header.classList.add('nav-fixed')
|
||||
if (window.getComputedStyle($rightside).getPropertyValue('opacity') === '0') {
|
||||
$rightside.style.cssText = 'opacity: 0.8; transform: translateX(-58px)'
|
||||
}
|
||||
} else {
|
||||
if (currentTop === 0) {
|
||||
$header.classList.remove('nav-fixed', 'nav-visible')
|
||||
}
|
||||
$rightside.style.cssText = "opacity: ''; transform: ''"
|
||||
}
|
||||
|
||||
isShowPercent && rightsideScrollPercent(currentTop)
|
||||
|
||||
if (document.body.scrollHeight <= innerHeight) {
|
||||
$rightside.style.cssText = 'opacity: 0.8; transform: translateX(-58px)'
|
||||
}
|
||||
}, 200)
|
||||
|
||||
}, 200)
|
||||
|
||||
window.scrollCollect = scrollTask
|
||||
|
||||
window.addEventListener('scroll', scrollCollect)
|
||||
@@ -444,14 +470,14 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
}
|
||||
|
||||
// main of scroll
|
||||
window.tocScrollFn = btf.throttle(()=>{
|
||||
const currentTop = window.scrollY || document.documentElement.scrollTop
|
||||
if (isToc && GLOBAL_CONFIG.percent.toc) {
|
||||
$tocPercentage.textContent = btf.getScrollPercent(currentTop,$article)
|
||||
}
|
||||
findHeadPosition(currentTop)
|
||||
}, 100)
|
||||
|
||||
window.tocScrollFn = btf.throttle(() => {
|
||||
const currentTop = window.scrollY || document.documentElement.scrollTop
|
||||
if (isToc && GLOBAL_CONFIG.percent.toc) {
|
||||
$tocPercentage.textContent = btf.getScrollPercent(currentTop, $article)
|
||||
}
|
||||
findHeadPosition(currentTop)
|
||||
}, 100)
|
||||
|
||||
window.addEventListener('scroll', tocScrollFn)
|
||||
}
|
||||
|
||||
|
||||
+13
-13
@@ -56,7 +56,7 @@ window.addEventListener('load', () => {
|
||||
post = '...'
|
||||
}
|
||||
|
||||
let matchContent = pre + content.substring(start, end) + post
|
||||
const matchContent = pre + content.substring(start, end) + post
|
||||
return matchContent
|
||||
}
|
||||
|
||||
@@ -68,10 +68,11 @@ window.addEventListener('load', () => {
|
||||
|
||||
const search = instantsearch({
|
||||
indexName: algolia.indexName,
|
||||
/* global algoliasearch */
|
||||
searchClient: algoliasearch(algolia.appId, algolia.apiKey),
|
||||
searchFunction(helper) {
|
||||
searchFunction (helper) {
|
||||
helper.state.query && helper.search()
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
const configure = instantsearch.widgets.configure({
|
||||
@@ -89,16 +90,16 @@ window.addEventListener('load', () => {
|
||||
const hits = instantsearch.widgets.hits({
|
||||
container: '#algolia-hits',
|
||||
templates: {
|
||||
item(data) {
|
||||
item (data) {
|
||||
const link = data.permalink ? data.permalink : (GLOBAL_CONFIG.root + data.path)
|
||||
const result = data._highlightResult
|
||||
const content = result.contentStripTruncate
|
||||
? cutContent(result.contentStripTruncate.value)
|
||||
: result.contentStrip
|
||||
? cutContent(result.contentStrip.value)
|
||||
: result.content
|
||||
? cutContent(result.content.value)
|
||||
: ''
|
||||
? cutContent(result.contentStripTruncate.value)
|
||||
: result.contentStrip
|
||||
? cutContent(result.contentStrip.value)
|
||||
: result.content
|
||||
? cutContent(result.content.value)
|
||||
: ''
|
||||
return `
|
||||
<a href="${link}" class="algolia-hit-item-link">
|
||||
${result.title.value || 'no-title'}
|
||||
@@ -130,7 +131,7 @@ window.addEventListener('load', () => {
|
||||
})
|
||||
|
||||
const powerBy = instantsearch.widgets.poweredBy({
|
||||
container: '#algolia-info > .algolia-poweredBy',
|
||||
container: '#algolia-info > .algolia-poweredBy'
|
||||
})
|
||||
|
||||
const pagination = instantsearch.widgets.pagination({
|
||||
@@ -144,8 +145,7 @@ window.addEventListener('load', () => {
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
search.addWidgets([configure,searchBox,hits,stats,powerBy,pagination]) // add the widgets to the instantsearch instance
|
||||
search.addWidgets([configure, searchBox, hits, stats, powerBy, pagination]) // add the widgets to the instantsearch instance
|
||||
|
||||
search.start()
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ window.addEventListener('load', () => {
|
||||
const res = await response.text()
|
||||
const t = await new window.DOMParser().parseFromString(res, 'text/xml')
|
||||
const a = await t
|
||||
data = [...a.querySelectorAll('entry')].map(item =>{
|
||||
data = [...a.querySelectorAll('entry')].map(item => {
|
||||
return {
|
||||
title: item.querySelector('title').textContent,
|
||||
content: item.querySelector('content') && item.querySelector('content').textContent,
|
||||
@@ -154,7 +154,7 @@ window.addEventListener('load', () => {
|
||||
// highlight all keywords
|
||||
keywords.forEach(keyword => {
|
||||
let regexStr = keyword
|
||||
const specialRegex = new RegExp("[^\\w\\s]+") // match special characters
|
||||
const specialRegex = /[^\w\s]+/ // match special characters
|
||||
if (keyword.length === 1 && specialRegex.test(keyword)) {
|
||||
regexStr = `\\${keyword}`
|
||||
}
|
||||
|
||||
+11
-11
@@ -62,10 +62,10 @@ const btf = {
|
||||
const { position, bgLight, bgDark } = GLOBAL_CONFIG.Snackbar
|
||||
const bg = document.documentElement.getAttribute('data-theme') === 'light' ? bgLight : bgDark
|
||||
Snackbar.show({
|
||||
text: text,
|
||||
text,
|
||||
backgroundColor: bg,
|
||||
showAction: showAction,
|
||||
duration: duration,
|
||||
showAction,
|
||||
duration,
|
||||
pos: position,
|
||||
customClass: 'snackbar-css'
|
||||
})
|
||||
@@ -88,7 +88,7 @@ const btf = {
|
||||
const minuteCount = dateDiff / minute
|
||||
|
||||
if (monthCount > 12) {
|
||||
result = datePost.toISOString().slice(0,10)
|
||||
result = datePost.toISOString().slice(0, 10)
|
||||
} else if (monthCount >= 1) {
|
||||
result = parseInt(monthCount) + ' ' + GLOBAL_CONFIG.date_suffix.month
|
||||
} else if (dayCount >= 1) {
|
||||
@@ -186,12 +186,12 @@ const btf = {
|
||||
* @param {*} options object key: value
|
||||
*/
|
||||
wrap: (selector, eleType, options) => {
|
||||
const creatEle = document.createElement(eleType)
|
||||
const createEle = document.createElement(eleType)
|
||||
for (const [key, value] of Object.entries(options)) {
|
||||
creatEle.setAttribute(key, value)
|
||||
createEle.setAttribute(key, value)
|
||||
}
|
||||
selector.parentNode.insertBefore(creatEle, selector)
|
||||
creatEle.appendChild(selector)
|
||||
selector.parentNode.insertBefore(createEle, selector)
|
||||
createEle.appendChild(selector)
|
||||
},
|
||||
|
||||
unwrap: el => {
|
||||
@@ -255,7 +255,7 @@ const btf = {
|
||||
if (!btf.isHidden(i)) {
|
||||
fjGallery(i, {
|
||||
itemSelector: '.fj-gallery-item',
|
||||
rowHeight: 220,
|
||||
rowHeight: i.getAttribute('data-rowHeight'),
|
||||
gutter: 4,
|
||||
onJustify: function () {
|
||||
this.$container.style.opacity = '1'
|
||||
@@ -271,12 +271,12 @@ const btf = {
|
||||
const title = GLOBAL_CONFIG_SITE.title
|
||||
window.history.replaceState({
|
||||
url: location.href,
|
||||
title: title
|
||||
title
|
||||
}, title, anchor)
|
||||
}
|
||||
},
|
||||
|
||||
getScrollPercent: (currentTop,ele) => {
|
||||
getScrollPercent: (currentTop, ele) => {
|
||||
const docHeight = ele.clientHeight
|
||||
const winHeight = document.documentElement.clientHeight
|
||||
const headerHeight = ele.offsetTop
|
||||
|
||||
Reference in New Issue
Block a user