Skip to content

Commit

Permalink
Merge pull request #3099 from qixing-jk/feat-spoiler-text-plugin
Browse files Browse the repository at this point in the history
Feat spoiler text plugin 支持spoiler text
  • Loading branch information
tangly1024 authored Jan 1, 2025
2 parents 13131da + adccf62 commit 08ef725
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 1 deletion.
2 changes: 2 additions & 0 deletions blog.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,8 @@ const BLOG = {
WIDGET_PET_SWITCH_THEME:
process.env.NEXT_PUBLIC_WIDGET_PET_SWITCH_THEME || true, // 点击宠物挂件切换博客主题

SPOILER_TEXT_TAG: process.env.NEXT_PUBLIC_SPOILER_TEXT_TAG || '', // Spoiler文本隐藏功能,如Notion中 [sp]希望被spoiler的文字[sp],填入[sp] 即可

// 音乐播放插件
MUSIC_PLAYER: process.env.NEXT_PUBLIC_MUSIC_PLAYER || false, // 是否使用音乐播放插件
MUSIC_PLAYER_VISIBLE: process.env.NEXT_PUBLIC_MUSIC_PLAYER_VISIBLE || true, // 是否在左下角显示播放和切换,如果使用播放器,打开自动播放再隐藏,就会以类似背景音乐的方式播放,无法取消和暂停
Expand Down
18 changes: 17 additions & 1 deletion components/NotionPage.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { siteConfig } from '@/lib/config'
import { compressImage, mapImgUrl } from '@/lib/notion/mapImage'
import { isBrowser } from '@/lib/utils'
import { isBrowser, loadExternalResource } from '@/lib/utils'
import mediumZoom from '@fisch0920/medium-zoom'
import 'katex/dist/katex.min.css'
import dynamic from 'next/dynamic'
Expand All @@ -17,6 +17,7 @@ const NotionPage = ({ post, className }) => {
// 是否关闭数据库和画册的点击跳转
const POST_DISABLE_GALLERY_CLICK = siteConfig('POST_DISABLE_GALLERY_CLICK')
const POST_DISABLE_DATABASE_CLICK = siteConfig('POST_DISABLE_DATABASE_CLICK')
const SPOILER_TEXT_TAG = siteConfig('SPOILER_TEXT_TAG')

const zoom =
isBrowser &&
Expand Down Expand Up @@ -84,6 +85,21 @@ const NotionPage = ({ post, className }) => {
}
}, [post])

useEffect(() => {
// Spoiler文本功能
if (SPOILER_TEXT_TAG) {
import('lodash/escapeRegExp').then(escapeRegExp => {
Promise.all([
loadExternalResource('/js/spoilerText.js', 'js'),
loadExternalResource('/css/spoiler-text.css', 'css')
]).then(() => {
window.textToSpoiler &&
window.textToSpoiler(escapeRegExp.default(SPOILER_TEXT_TAG))
})
})
}
}, [post])

return (
<div
id='notion-article'
Expand Down
23 changes: 23 additions & 0 deletions public/css/spoiler-text.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* Spoiler text styles */
.spoiler-text {
color: transparent; /* 文字透明 */
background-color: #808080; /* 背景为黑色 */
border-color: #808080;
text-decoration-color: #808080;
text-emphasis-color: #808080;
border-radius: 8px;
filter: blur(1px); /* 初始模糊 */
--hide-transition: 0.3s ease-out;
transition: opacity var(--hide-transition),
filter var(--hide-transition);
}

.spoiler-text:hover {
color: inherit; /* 鼠标悬停时恢复文字颜色 */
background-color: inherit; /* 鼠标悬停时恢复背景颜色 */
border-color: inherit;
text-decoration-color: inherit;
text-emphasis-color: inherit;
opacity: 1; /* 鼠标悬停时恢复不透明度 */
filter: blur(0); /* 鼠标悬停时解除模糊 */
}
84 changes: 84 additions & 0 deletions public/js/spoilerText.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/**
* 将Node文本中的指定标签内容转换为带有指定类名的span
* @param regex
* @param node
* @param className
*/
function convertTextToSpoilerSpan(regex, node, className) {
const wholeText = node.wholeText
let outerSpan = document.createElement('span')
const fragments = []
let lastIndex = 0
let match
while ((match = regex.exec(wholeText)) !== null) {
console.log('符合要求的文字' + wholeText)
// 添加前面未匹配的部分
if (match.index > lastIndex) {
outerSpan.appendChild(
document.createTextNode(wholeText.slice(lastIndex, match.index))
)
}

// 创建 span 包裹的内容
const span = document.createElement('span')
span.textContent = match[1] // 提取匹配的内容
if (className) {
span.className = className
}
outerSpan.appendChild(span)
// 设置lastIndex
lastIndex = regex.lastIndex
}
if (outerSpan.childNodes.length) {
// 添加剩余未匹配的部分
if (lastIndex < wholeText.length) {
outerSpan.appendChild(document.createTextNode(wholeText.slice(lastIndex)))
}
node.replaceWith(outerSpan)
}
}

/**
* 收集并处理指定节点下的所有文本节点
* @param root
* @param className
* @param spoilerTag
*/
function processTextNodes(root, className, spoilerTag) {
const regex = new RegExp(`${spoilerTag}(.*?)${spoilerTag}`, 'g')
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
acceptNode: function (node) {
return regex.test(node.wholeText)
? NodeFilter.FILTER_ACCEPT
: NodeFilter.FILTER_REJECT
}
})
const waitProcessNodes = []
while (walker.nextNode()) {
const node = walker.currentNode
waitProcessNodes.push(node)
}
for (const waitProcessNode of waitProcessNodes) {
convertTextToSpoilerSpan(regex, waitProcessNode, className)
}
}

/**
* 定位到目标处理位置,开始进行文本到spoiler的转换
* @param spoilerTag
*/
function textToSpoiler(spoilerTag) {
const intervalID = setInterval(() => {
const articleElement = document.querySelector(
'#article-wrapper #notion-article main'
)
if (articleElement) {
setTimeout(() => {
processTextNodes(articleElement, 'spoiler-text', spoilerTag)
clearInterval(intervalID)
}, 300)
}
}, 1000)
}

window.textToSpoiler = textToSpoiler

0 comments on commit 08ef725

Please sign in to comment.