From 309f7e8215a025c15099b7d88f2b715990978bd3 Mon Sep 17 00:00:00 2001 From: "tangly1024.com" Date: Thu, 20 Jul 2023 17:33:41 +0800 Subject: [PATCH] =?UTF-8?q?=E7=B2=BE=E5=87=8F=20markjs=20=EF=BC=8C?= =?UTF-8?q?=E7=B2=BE=E5=87=8F=20animate.css?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/Mark.js | 31 ++ components/NotionPage.js | 2 +- package.json | 2 - pages/_app.js | 2 +- styles/animate.css | 503 +++++++++++++++++++++ themes/example/index.js | 25 +- themes/fukasawa/index.js | 19 +- themes/heo/components/BlogPostCard.js | 4 +- themes/heo/index.js | 37 +- themes/hexo/components/BlogPostCardInfo.js | 6 +- themes/hexo/index.js | 25 +- themes/matery/index.js | 25 +- themes/medium/index.js | 22 +- themes/next/components/BlogPostCard.js | 4 +- themes/next/index.js | 23 +- themes/nobelium/index.js | 30 +- themes/plog/index.js | 25 +- themes/simple/index.js | 24 +- 18 files changed, 654 insertions(+), 155 deletions(-) create mode 100644 components/Mark.js create mode 100644 styles/animate.css diff --git a/components/Mark.js b/components/Mark.js new file mode 100644 index 00000000..1defca32 --- /dev/null +++ b/components/Mark.js @@ -0,0 +1,31 @@ +import { loadExternalResource } from '@/lib/utils' + +/** + * 将搜索结果的关键词高亮 + */ +export default async function replaceSearchResult({ doms, search, target }) { + if (!doms || !search || !target) { + return + } + + try { + const url = await loadExternalResource('https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/mark.min.js', 'js') + console.log('markjs 加载成功', url, window.Mark) + console.log('------', doms) + + const Mark = window.Mark + if (doms instanceof HTMLCollection) { + for (const container of doms) { + const re = new RegExp(search, 'gim') + const instance = new Mark(container) + instance.markRegExp(re, target) + } + } else { + const re = new RegExp(search, 'gim') + const instance = new Mark(doms) + instance.markRegExp(re, target) + } + } catch (error) { + console.error('markjs 加载失败', error) + } +} diff --git a/components/NotionPage.js b/components/NotionPage.js index e559bdbb..897a84ee 100644 --- a/components/NotionPage.js +++ b/components/NotionPage.js @@ -5,9 +5,9 @@ import React, { useEffect, useRef } from 'react' // import { Code } from 'react-notion-x/build/third-party/code' import TweetEmbed from 'react-tweet-embed' +import BLOG from '@/blog.config' import 'katex/dist/katex.min.css' import { mapImgUrl } from '@/lib/notion/mapImage' -import BLOG from '@/blog.config' import { isBrowser } from '@/lib/utils' const Code = dynamic(() => diff --git a/package.json b/package.json index 26d06eae..8ea3be05 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,6 @@ "@headlessui/react": "^1.7.15", "@next/bundle-analyzer": "^12.1.1", "@vercel/analytics": "^1.0.0", - "animate.css": "^4.1.1", "animejs": "^3.2.1", "aos": "^3.0.0-beta.6", "axios": ">=0.21.1", @@ -35,7 +34,6 @@ "js-md5": "^0.7.3", "localStorage": "^1.0.4", "lodash.throttle": "^4.1.1", - "mark.js": "^8.11.1", "memory-cache": "^0.2.0", "mongodb": "^4.6.0", "next": "13.3.1", diff --git a/pages/_app.js b/pages/_app.js index 38e16c99..d20f396c 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -1,6 +1,6 @@ import { useEffect } from 'react' -import 'animate.css' +import '@/styles/animate.css' // @see https://animate.style/ import '@/styles/globals.css' import '@/styles/nprogress.css' import '@/styles/utility-patterns.css' diff --git a/styles/animate.css b/styles/animate.css new file mode 100644 index 00000000..43f253c1 --- /dev/null +++ b/styles/animate.css @@ -0,0 +1,503 @@ +@charset "UTF-8";/*! + * animate.css - https://animate.style/ + * Version - 4.1.1 + * Licensed under the MIT license - http://opensource.org/licenses/MIT + * https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css + * 这里做了精减,后续不再使用animate.css,因为占用体积太大,不如手写动画 + * Copyright (c) 2020 Animate.css + */ +:root { + --animate-duration: 1s; + --animate-delay: 1s; + --animate-repeat: 1; +} +.animate__animated { + -webkit-animation-duration: 1s; + animation-duration: 1s; + -webkit-animation-duration: var(--animate-duration); + animation-duration: var(--animate-duration); + -webkit-animation-fill-mode: both; + animation-fill-mode: both; +} + + +.animate__animated.animate__faster { + -webkit-animation-duration: calc(1s / 2); + animation-duration: calc(1s / 2); + -webkit-animation-duration: calc(var(--animate-duration) / 2); + animation-duration: calc(var(--animate-duration) / 2); +} +.animate__animated.animate__fast { + -webkit-animation-duration: calc(1s * 0.8); + animation-duration: calc(1s * 0.8); + -webkit-animation-duration: calc(var(--animate-duration) * 0.8); + animation-duration: calc(var(--animate-duration) * 0.8); +} + + +@media print, (prefers-reduced-motion: reduce) { + .animate__animated { + -webkit-animation-duration: 1ms !important; + animation-duration: 1ms !important; + -webkit-transition-duration: 1ms !important; + transition-duration: 1ms !important; + -webkit-animation-iteration-count: 1 !important; + animation-iteration-count: 1 !important; + } + + .animate__animated[class*='Out'] { + opacity: 0; + } +} + + +@-webkit-keyframes shakeX { + from, + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 10%, + 30%, + 50%, + 70%, + 90% { + -webkit-transform: translate3d(-10px, 0, 0); + transform: translate3d(-10px, 0, 0); + } + + 20%, + 40%, + 60%, + 80% { + -webkit-transform: translate3d(10px, 0, 0); + transform: translate3d(10px, 0, 0); + } +} +@keyframes shakeX { + from, + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 10%, + 30%, + 50%, + 70%, + 90% { + -webkit-transform: translate3d(-10px, 0, 0); + transform: translate3d(-10px, 0, 0); + } + + 20%, + 40%, + 60%, + 80% { + -webkit-transform: translate3d(10px, 0, 0); + transform: translate3d(10px, 0, 0); + } +} +.animate__shakeX { + -webkit-animation-name: shakeX; + animation-name: shakeX; +} +@-webkit-keyframes shakeY { + from, + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 10%, + 30%, + 50%, + 70%, + 90% { + -webkit-transform: translate3d(0, -10px, 0); + transform: translate3d(0, -10px, 0); + } + + 20%, + 40%, + 60%, + 80% { + -webkit-transform: translate3d(0, 10px, 0); + transform: translate3d(0, 10px, 0); + } +} +@keyframes shakeY { + from, + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 10%, + 30%, + 50%, + 70%, + 90% { + -webkit-transform: translate3d(0, -10px, 0); + transform: translate3d(0, -10px, 0); + } + + 20%, + 40%, + 60%, + 80% { + -webkit-transform: translate3d(0, 10px, 0); + transform: translate3d(0, 10px, 0); + } +} +.animate__shakeY { + -webkit-animation-name: shakeY; + animation-name: shakeY; +} +@-webkit-keyframes headShake { + 0% { + -webkit-transform: translateX(0); + transform: translateX(0); + } + + 6.5% { + -webkit-transform: translateX(-6px) rotateY(-9deg); + transform: translateX(-6px) rotateY(-9deg); + } + + 18.5% { + -webkit-transform: translateX(5px) rotateY(7deg); + transform: translateX(5px) rotateY(7deg); + } + + 31.5% { + -webkit-transform: translateX(-3px) rotateY(-5deg); + transform: translateX(-3px) rotateY(-5deg); + } + + 43.5% { + -webkit-transform: translateX(2px) rotateY(3deg); + transform: translateX(2px) rotateY(3deg); + } + + 50% { + -webkit-transform: translateX(0); + transform: translateX(0); + } +} +@keyframes headShake { + 0% { + -webkit-transform: translateX(0); + transform: translateX(0); + } + + 6.5% { + -webkit-transform: translateX(-6px) rotateY(-9deg); + transform: translateX(-6px) rotateY(-9deg); + } + + 18.5% { + -webkit-transform: translateX(5px) rotateY(7deg); + transform: translateX(5px) rotateY(7deg); + } + + 31.5% { + -webkit-transform: translateX(-3px) rotateY(-5deg); + transform: translateX(-3px) rotateY(-5deg); + } + + 43.5% { + -webkit-transform: translateX(2px) rotateY(3deg); + transform: translateX(2px) rotateY(3deg); + } + + 50% { + -webkit-transform: translateX(0); + transform: translateX(0); + } +} +.animate__headShake { + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + -webkit-animation-name: headShake; + animation-name: headShake; +} + + +@keyframes jello { + from, + 11.1%, + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 22.2% { + -webkit-transform: skewX(-12.5deg) skewY(-12.5deg); + transform: skewX(-12.5deg) skewY(-12.5deg); + } + + 33.3% { + -webkit-transform: skewX(6.25deg) skewY(6.25deg); + transform: skewX(6.25deg) skewY(6.25deg); + } + + 44.4% { + -webkit-transform: skewX(-3.125deg) skewY(-3.125deg); + transform: skewX(-3.125deg) skewY(-3.125deg); + } + + 55.5% { + -webkit-transform: skewX(1.5625deg) skewY(1.5625deg); + transform: skewX(1.5625deg) skewY(1.5625deg); + } + + 66.6% { + -webkit-transform: skewX(-0.78125deg) skewY(-0.78125deg); + transform: skewX(-0.78125deg) skewY(-0.78125deg); + } + + 77.7% { + -webkit-transform: skewX(0.390625deg) skewY(0.390625deg); + transform: skewX(0.390625deg) skewY(0.390625deg); + } + + 88.8% { + -webkit-transform: skewX(-0.1953125deg) skewY(-0.1953125deg); + transform: skewX(-0.1953125deg) skewY(-0.1953125deg); + } +} +.animate__jello { + -webkit-animation-name: jello; + animation-name: jello; + -webkit-transform-origin: center; + transform-origin: center; +} + + + +@-webkit-keyframes bounceInRight { + from, + 60%, + 75%, + 90%, + to { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + } + + from { + opacity: 0; + -webkit-transform: translate3d(3000px, 0, 0) scaleX(3); + transform: translate3d(3000px, 0, 0) scaleX(3); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(-25px, 0, 0) scaleX(1); + transform: translate3d(-25px, 0, 0) scaleX(1); + } + + 75% { + -webkit-transform: translate3d(10px, 0, 0) scaleX(0.98); + transform: translate3d(10px, 0, 0) scaleX(0.98); + } + + 90% { + -webkit-transform: translate3d(-5px, 0, 0) scaleX(0.995); + transform: translate3d(-5px, 0, 0) scaleX(0.995); + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} +@keyframes bounceInRight { + from, + 60%, + 75%, + 90%, + to { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + } + + from { + opacity: 0; + -webkit-transform: translate3d(3000px, 0, 0) scaleX(3); + transform: translate3d(3000px, 0, 0) scaleX(3); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(-25px, 0, 0) scaleX(1); + transform: translate3d(-25px, 0, 0) scaleX(1); + } + + 75% { + -webkit-transform: translate3d(10px, 0, 0) scaleX(0.98); + transform: translate3d(10px, 0, 0) scaleX(0.98); + } + + 90% { + -webkit-transform: translate3d(-5px, 0, 0) scaleX(0.995); + transform: translate3d(-5px, 0, 0) scaleX(0.995); + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} +.animate__bounceInRight { + -webkit-animation-name: bounceInRight; + animation-name: bounceInRight; +} + + +/* Fading entrances */ +@-webkit-keyframes fadeIn { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} +@keyframes fadeIn { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} +.animate__fadeIn { + -webkit-animation-name: fadeIn; + animation-name: fadeIn; +} + + +/* Fading exits */ +/* 删除 */ + +/* Flippers */ +/* 删除 */ + +/* Lightspeed */ +/* 删除 */ + +/* Rotating exits */ +/* 删除 */ + +/* Zooming entrances */ +/* 删除 */ + +/* Sliding entrances */ + +@-webkit-keyframes slideInLeft { + from { + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + visibility: visible; + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} +@keyframes slideInLeft { + from { + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + visibility: visible; + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} +.animate__slideInLeft { + -webkit-animation-name: slideInLeft; + animation-name: slideInLeft; +} +@-webkit-keyframes slideInRight { + from { + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + visibility: visible; + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} +@keyframes slideInRight { + from { + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + visibility: visible; + } + + to { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} +.animate__slideInRight { + -webkit-animation-name: slideInRight; + animation-name: slideInRight; +} + + +@keyframes slideOutLeft { + from { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + to { + visibility: hidden; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } +} +.animate__slideOutLeft { + -webkit-animation-name: slideOutLeft; + animation-name: slideOutLeft; +} +@-webkit-keyframes slideOutRight { + from { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + to { + visibility: hidden; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } +} +@keyframes slideOutRight { + from { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + to { + visibility: hidden; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } +} +.animate__slideOutRight { + -webkit-animation-name: slideOutRight; + animation-name: slideOutRight; +} diff --git a/themes/example/index.js b/themes/example/index.js index 77ad1b3a..d14bc570 100644 --- a/themes/example/index.js +++ b/themes/example/index.js @@ -19,7 +19,7 @@ import NotionPage from '@/components/NotionPage' import Comment from '@/components/Comment' import ShareBar from '@/components/ShareBar' import SearchInput from './components/SearchInput' -import Mark from 'mark.js' +import replaceSearchResult from '@/components/Mark' import { isBrowser } from '@/lib/utils' import BlogListGroupByDate from './components/BlogListGroupByDate' import CategoryItem from './components/CategoryItem' @@ -175,21 +175,20 @@ const LayoutSearch = props => { const slotTop =
const router = useRouter() useEffect(() => { - setTimeout(() => { - if (isBrowser()) { - // 高亮搜索到的结果 - const container = document.getElementById('posts-wrapper') - console.log('container', container, keyword) - if (keyword && container) { - const re = new RegExp(keyword, 'gim') - const instance = new Mark(container) - instance.markRegExp(re, { + if (isBrowser()) { + // 高亮搜索到的结果 + const container = document.getElementById('posts-wrapper') + if (keyword && container) { + replaceSearchResult({ + doms: container, + search: keyword, + target: { element: 'span', className: 'text-red-500 border-b border-dashed' - }) - } + } + }) } - }, 500) + } }, [router]) return diff --git a/themes/fukasawa/index.js b/themes/fukasawa/index.js index aab7d3a0..52b55b5c 100644 --- a/themes/fukasawa/index.js +++ b/themes/fukasawa/index.js @@ -20,9 +20,9 @@ import { Transition } from '@headlessui/react' import dynamic from 'next/dynamic' import { AdSlot } from '@/components/GoogleAdsense' import { Style } from './style' +import replaceSearchResult from '@/components/Mark' const Live2D = dynamic(() => import('@/components/Live2D')) -const Mark = dynamic(() => import('mark.js')) // 主题全局状态 const ThemeGlobalFukasawa = createContext() @@ -148,17 +148,16 @@ const LayoutSearch = props => { const { keyword } = props const router = useRouter() useEffect(() => { - setTimeout(() => { - const container = isBrowser() && document.getElementById('posts-wrapper') - if (container && container.innerHTML) { - const re = new RegExp(keyword, 'gim') - const instance = new Mark(container) - instance.markRegExp(re, { + if (isBrowser()) { + replaceSearchResult({ + doms: document.getElementById('posts-wrapper'), + search: keyword, + target: { element: 'span', className: 'text-red-500 border-b border-dashed' - }) - } - }, 300) + } + }) + } }, [router]) return } diff --git a/themes/heo/components/BlogPostCard.js b/themes/heo/components/BlogPostCard.js index add783ea..56ca1fbf 100644 --- a/themes/heo/components/BlogPostCard.js +++ b/themes/heo/components/BlogPostCard.js @@ -54,8 +54,8 @@ const BlogPostCard = ({ index, post, showSummary, siteInfo }) => { {/* 搜索结果 */} {post.results && (

- {post.results.map(r => ( - {r} + {post.results.map((r, index) => ( + {r} ))}

)} diff --git a/themes/heo/index.js b/themes/heo/index.js index 5c0a472d..d111b3db 100644 --- a/themes/heo/index.js +++ b/themes/heo/index.js @@ -7,12 +7,10 @@ import SideRight from './components/SideRight' import NavBar from './components/NavBar' import { useGlobal } from '@/lib/global' import BLOG from '@/blog.config' -import { isBrowser, loadExternalResource } from '@/lib/utils' import BlogPostListPage from './components/BlogPostListPage' import BlogPostListScroll from './components/BlogPostListScroll' import Hero from './components/Hero' import { useRouter } from 'next/router' -import Mark from 'mark.js' import SearchNav from './components/SearchNav' import BlogPostArchive from './components/BlogPostArchive' import { ArticleLock } from './components/ArticleLock' @@ -31,6 +29,7 @@ import { NoticeBar } from './components/NoticeBar' import { HashTag } from '@/components/HeroIcons' import LatestPostsGroup from './components/LatestPostsGroup' import FloatTocButton from './components/FloatTocButton' +import replaceSearchResult from '@/components/Mark' /** * 基础布局 采用上中下布局,移动端使用顶部侧边导航栏 @@ -42,11 +41,6 @@ const LayoutBase = props => { const { children, headerSlot, slotTop, slotRight, meta, siteInfo, className } = props const { onLoading } = useGlobal() - // 加载主题样式 - if (isBrowser()) { - loadExternalResource('/css/theme-hexo.css', 'css') - } - return (
{/* 网页SEO */} @@ -168,23 +162,20 @@ const LayoutSearch = props => { useEffect(() => { - setTimeout(() => { - if (currentSearch) { - const targets = document.getElementsByClassName('replace') - for (const container of targets) { - if (container && container.innerHTML) { - const re = new RegExp(currentSearch, 'gim') - const instance = new Mark(container) - instance.markRegExp(re, { - element: 'span', - className: 'text-red-500 border-b border-dashed' - }) + // 高亮搜索结果 + if (currentSearch) { + setTimeout(() => { + replaceSearchResult({ + doms: document.getElementsByClassName('replace'), + search: currentSearch, + target: { + element: 'span', + className: 'text-red-500 border-b border-dashed' } - } - } - }, 100) - }) - + }) + }, 100) + } + }, []) return (
diff --git a/themes/hexo/components/BlogPostCardInfo.js b/themes/hexo/components/BlogPostCardInfo.js index 4b7feee6..ffdf0777 100644 --- a/themes/hexo/components/BlogPostCardInfo.js +++ b/themes/hexo/components/BlogPostCardInfo.js @@ -51,9 +51,9 @@ export const BlogPostCardInfo = ({ post, showPreview, showPageCover, showSummary {/* 搜索结果 */} {post.results && (

- {post.results.map(r => ( - {r} - ))} + {post.results.map((r, index) => ( + {r} + ))}

)} {/* 预览 */} diff --git a/themes/hexo/index.js b/themes/hexo/index.js index 26bb631b..b60b7c4d 100644 --- a/themes/hexo/index.js +++ b/themes/hexo/index.js @@ -12,7 +12,6 @@ import BlogPostListPage from './components/BlogPostListPage' import BlogPostListScroll from './components/BlogPostListScroll' import Hero from './components/Hero' import { useRouter } from 'next/router' -import Mark from 'mark.js' import Card from './components/Card' import RightFloatArea from './components/RightFloatArea' import SearchNav from './components/SearchNav' @@ -33,6 +32,7 @@ import Link from 'next/link' import SlotBar from './components/SlotBar' import { Transition } from '@headlessui/react' import { Style } from './style' +import replaceSearchResult from '@/components/Mark' /** * 基础布局 采用左右两侧布局,移动端使用顶部导航栏 @@ -139,21 +139,16 @@ const LayoutSearch = props => { const currentSearch = keyword || router?.query?.s useEffect(() => { - setTimeout(() => { - if (currentSearch) { - const targets = document.getElementsByClassName('replace') - for (const container of targets) { - if (container && container.innerHTML) { - const re = new RegExp(currentSearch, 'gim') - const instance = new Mark(container) - instance.markRegExp(re, { - element: 'span', - className: 'text-red-500 border-b border-dashed' - }) - } + if (currentSearch) { + replaceSearchResult({ + doms: document.getElementsByClassName('replace'), + search: keyword, + target: { + element: 'span', + className: 'text-red-500 border-b border-dashed' } - } - }, 100) + }) + } }) return ( diff --git a/themes/matery/index.js b/themes/matery/index.js index 8c6331a9..46f214b1 100644 --- a/themes/matery/index.js +++ b/themes/matery/index.js @@ -8,7 +8,6 @@ import Footer from './components/Footer' import { useEffect } from 'react' import RightFloatButtons from './components/RightFloatButtons' import { useRouter } from 'next/router' -import Mark from 'mark.js' import SearchNave from './components/SearchNav' import BlogPostListPage from './components/BlogPostListPage' import BlogPostListScroll from './components/BlogPostListScroll' @@ -31,6 +30,7 @@ import JumpToCommentButton from './components/JumpToCommentButton' import BlogListBar from './components/BlogListBar' import { Transition } from '@headlessui/react' import { Style } from './style' +import replaceSearchResult from '@/components/Mark' /** * 基础布局 @@ -140,21 +140,16 @@ const LayoutSearch = props => { const currentSearch = keyword || router?.query?.s useEffect(() => { - setTimeout(() => { - if (currentSearch) { - const targets = document.getElementsByClassName('replace') - for (const container of targets) { - if (container && container.innerHTML) { - const re = new RegExp(currentSearch, 'gim') - const instance = new Mark(container) - instance.markRegExp(re, { - element: 'span', - className: 'text-red-500 border-b border-dashed' - }) - } + if (currentSearch) { + replaceSearchResult({ + doms: document.getElementsByClassName('replace'), + search: keyword, + target: { + element: 'span', + className: 'text-red-500 border-b border-dashed' } - } - }, 100) + }) + } }) return ( diff --git a/themes/medium/index.js b/themes/medium/index.js index 039652ea..ae1c34f2 100644 --- a/themes/medium/index.js +++ b/themes/medium/index.js @@ -22,7 +22,6 @@ import { ArticleLock } from './components/ArticleLock' import TagGroups from './components/TagGroups' import CategoryGroup from './components/CategoryGroup' import { isBrowser } from '@/lib/utils' -import Mark from 'mark.js' import BlogArchiveItem from './components/BlogArchiveItem' import BlogPostBar from './components/BlogPostBar' import NotionPage from '@/components/NotionPage' @@ -35,6 +34,7 @@ import ShareBar from '@/components/ShareBar' import Link from 'next/link' import { Transition } from '@headlessui/react' import { Style } from './style' +import replaceSearchResult from '@/components/Mark' // 主题全局状态 const ThemeGlobalMedium = createContext() @@ -202,18 +202,18 @@ const LayoutSearch = (props) => { const currentSearch = keyword || router?.query?.s useEffect(() => { - setTimeout(() => { - const container = isBrowser() && document.getElementById('posts-wrapper') - if (container && container.innerHTML) { - const re = new RegExp(currentSearch, 'gim') - const instance = new Mark(container) - instance.markRegExp(re, { + if (isBrowser()) { + replaceSearchResult({ + doms: document.getElementById('posts-wrapper'), + search: keyword, + target: { element: 'span', className: 'text-red-500 border-b border-dashed' - }) - } - }, 100) - }) + } + }) + } + }, []) + return {/* 搜索导航栏 */} diff --git a/themes/next/components/BlogPostCard.js b/themes/next/components/BlogPostCard.js index 4459cff3..96a7668e 100644 --- a/themes/next/components/BlogPostCard.js +++ b/themes/next/components/BlogPostCard.js @@ -86,8 +86,8 @@ const BlogPostCard = ({ post, showSummary }) => { {/* 搜索结果 */} {post.results && (

- {post.results.map(r => ( - {r} + {post.results.map((r, index) => ( + {r} ))}

)} diff --git a/themes/next/index.js b/themes/next/index.js index 621dd912..3380b4b1 100644 --- a/themes/next/index.js +++ b/themes/next/index.js @@ -16,7 +16,6 @@ import BlogPostListScroll from './components/BlogPostListScroll' import BlogPostListPage from './components/BlogPostListPage' import StickyBar from './components/StickyBar' import { isBrowser } from '@/lib/utils' -import Mark from 'mark.js' import TocDrawerButton from './components/TocDrawerButton' import TocDrawer from './components/TocDrawer' import { ArticleLock } from './components/ArticleLock' @@ -28,6 +27,7 @@ import Link from 'next/link' import BlogListBar from './components/BlogListBar' import { Transition } from '@headlessui/react' import { Style } from './style' +import replaceSearchResult from '@/components/Mark' /** * 基础布局 采用左中右三栏布局,移动端使用顶部导航栏 @@ -161,17 +161,20 @@ const LayoutPostList = (props) => { const LayoutSearch = (props) => { const { locale } = useGlobal() const { posts, keyword } = props - setTimeout(() => { - const container = isBrowser() && document.getElementById('posts-wrapper') - if (container && container.innerHTML) { - const re = new RegExp(keyword, 'gim') - const instance = new Mark(container) - instance.markRegExp(re, { - element: 'span', - className: 'text-red-500 border-b border-dashed' + + useEffect(() => { + if (isBrowser()) { + replaceSearchResult({ + doms: document.getElementById('posts-wrapper'), + search: keyword, + target: { + element: 'span', + className: 'text-red-500 border-b border-dashed' + } }) } - }, 200) + }, []) + return ( diff --git a/themes/nobelium/index.js b/themes/nobelium/index.js index ffbd7f75..b58b2250 100644 --- a/themes/nobelium/index.js +++ b/themes/nobelium/index.js @@ -1,3 +1,4 @@ +import BLOG from '@/blog.config' import CONFIG from './config' import CommonHead from '@/components/CommonHead' import React, { useEffect, useState } from 'react' @@ -6,15 +7,9 @@ import { Footer } from './components/Footer' import JumpToTopButton from './components/JumpToTopButton' import Live2D from '@/components/Live2D' import { useGlobal } from '@/lib/global' - -import BLOG from '@/blog.config' import Announcement from './components/Announcement' import { BlogListPage } from './components/BlogListPage' import { BlogListScroll } from './components/BlogListScroll' - -import { useRouter } from 'next/router' - -import Mark from 'mark.js' import { deepClone, isBrowser } from '@/lib/utils' import SearchNavBar from './components/SearchNavBar' import BlogArchiveItem from './components/BlogArchiveItem' @@ -28,6 +23,7 @@ import Link from 'next/link' import BlogListBar from './components/BlogListBar' import { Transition } from '@headlessui/react' import { Style } from './style' +import replaceSearchResult from '@/components/Mark' /** * 基础布局 采用左右两侧布局,移动端使用顶部导航栏 @@ -135,22 +131,18 @@ const LayoutPostList = props => { */ const LayoutSearch = props => { const { keyword } = props - const router = useRouter() - useEffect(() => { - setTimeout(() => { - const container = isBrowser() && document.getElementById('posts-wrapper') - if (container && container.innerHTML) { - const re = new RegExp(keyword, 'gim') - const instance = new Mark(container) - instance.markRegExp(re, { + if (isBrowser()) { + replaceSearchResult({ + doms: document.getElementById('posts-wrapper'), + search: keyword, + target: { element: 'span', className: 'text-red-500 border-b border-dashed' - }) - } - }, 100) - }, [router.events]) - + } + }) + } + }, []) return } /> } diff --git a/themes/plog/index.js b/themes/plog/index.js index 98ef1e66..d5f15608 100644 --- a/themes/plog/index.js +++ b/themes/plog/index.js @@ -7,10 +7,6 @@ import { useGlobal } from '@/lib/global' import BLOG from '@/blog.config' import { BlogListPage } from './components/BlogListPage' import { BlogListScroll } from './components/BlogListScroll' - -import { useRouter } from 'next/router' - -import Mark from 'mark.js' import { isBrowser } from '@/lib/utils' import SearchNavBar from './components/SearchNavBar' import BlogArchiveItem from './components/BlogArchiveItem' @@ -26,6 +22,7 @@ import BottomNav from './components/BottomNav' import { saveDarkModeToCookies } from '@/themes/theme' import Modal from './components/Modal' import { Style } from './style' +import replaceSearchResult from '@/components/Mark' // 主题全局状态 const ThemeGlobalPlog = createContext() @@ -133,21 +130,19 @@ const LayoutPostList = props => { */ const LayoutSearch = props => { const { keyword } = props - const router = useRouter() useEffect(() => { - setTimeout(() => { - const container = isBrowser() && document.getElementById('posts-wrapper') - if (container && container.innerHTML) { - const re = new RegExp(keyword, 'gim') - const instance = new Mark(container) - instance.markRegExp(re, { + if (isBrowser()) { + replaceSearchResult({ + doms: document.getElementById('posts-wrapper'), + search: keyword, + target: { element: 'span', className: 'text-red-500 border-b border-dashed' - }) - } - }, 100) - }, [router.events]) + } + }) + } + }, []) return } /> } diff --git a/themes/simple/index.js b/themes/simple/index.js index 9676254b..04a4ba51 100644 --- a/themes/simple/index.js +++ b/themes/simple/index.js @@ -1,9 +1,7 @@ import CONFIG from './config' import { BlogListPage } from './components/BlogListPage' import { BlogListScroll } from './components/BlogListScroll' -import { useRouter } from 'next/router' import { useEffect } from 'react' -import Mark from 'mark.js' import { isBrowser, loadExternalResource } from '@/lib/utils' import BlogArchiveItem from './components/BlogArchiveItem' import { ArticleLock } from './components/ArticleLock' @@ -26,6 +24,7 @@ import { useGlobal } from '@/lib/global' import SearchInput from './components/SearchInput' import { Transition } from '@headlessui/react' import { Style } from './style' +import replaceSearchResult from '@/components/Mark' /** * 基础布局 @@ -120,20 +119,19 @@ const LayoutPostList = props => { */ const LayoutSearch = props => { const { keyword } = props - const router = useRouter() + useEffect(() => { - setTimeout(() => { - const container = isBrowser() && document.getElementById('posts-wrapper') - if (container && container.innerHTML) { - const re = new RegExp(keyword, 'gim') - const instance = new Mark(container) - instance.markRegExp(re, { + if (isBrowser()) { + replaceSearchResult({ + doms: document.getElementById('posts-wrapper'), + search: keyword, + target: { element: 'span', className: 'text-red-500 border-b border-dashed' - }) - } - }, 100) - }, [router]) + } + }) + } + }, []) return } /> }