diff --git a/config.json b/config.json index 19eef0e..9b270ab 100644 --- a/config.json +++ b/config.json @@ -17,7 +17,7 @@ "urlMode":"issue", "showPostSource":0, "needComment":0, - "indexScript":"", "bottomText":"❤️ 转载文章请注明出处,谢谢!❤️", - "script":"", - "allHead":"", + "script":"", + "allHead":"" +} diff --git a/static/assets/articletoc.js b/static/assets/articletoc.js deleted file mode 100644 index 1037e45..0000000 --- a/static/assets/articletoc.js +++ /dev/null @@ -1,158 +0,0 @@ -function loadResource(type, attributes) { - if (type === 'style') { - const style = document.createElement('style'); - style.textContent = attributes.css; - document.head.appendChild(style); - } -} - -function createTOC() { - const tocElement = document.createElement('div'); - tocElement.className = 'toc'; - const contentContainer = document.querySelector('.markdown-body'); - contentContainer.appendChild(tocElement); - - const headings = contentContainer.querySelectorAll('h1, h2, h3, h4, h5, h6'); - headings.forEach(heading => { - if (!heading.id) { - heading.id = heading.textContent.trim().replace(/\s+/g, '-').toLowerCase(); - } - const link = document.createElement('a'); - link.href = '#' + heading.id; - link.textContent = heading.textContent; - link.className = 'toc-link'; - link.style.paddingLeft = `${(parseInt(heading.tagName.charAt(1)) - 1) * 10}px`; - tocElement.appendChild(link); - }); -} - -function toggleTOC() { - const tocElement = document.querySelector('.toc'); - const tocIcon = document.querySelector('.toc-icon'); - if (tocElement) { - tocElement.classList.toggle('show'); - tocIcon.classList.toggle('active'); - tocIcon.textContent = tocElement.classList.contains('show') ? '✖' : '☰'; - } -} - -document.addEventListener("DOMContentLoaded", function() { - createTOC(); - const css = ` - :root { - --toc-bg: #fff; - --toc-border: #e1e4e8; - --toc-text: #24292e; - --toc-hover: #f6f8fa; - --toc-icon-bg: #fff; - --toc-icon-color: #ad6598; - --toc-icon-active-bg: #813c85; - --toc-icon-active-color: #fff; - } - - @media (prefers-color-scheme: dark) { - :root { - --toc-bg: #2d333b; - --toc-border: #444c56; - --toc-text: #adbac7; - --toc-hover: #373e47; - --toc-icon-bg: #22272e; - --toc-icon-color: #ad6598; - --toc-icon-active-bg: #813c85; - --toc-icon-active-color: #adbac7; - } - } - - .toc { - position: fixed; - bottom: 60px; - right: 20px; - width: 250px; - max-height: 70vh; - background-color: var(--toc-bg); - border: 1px solid var(--toc-border); - border-radius: 6px; - padding: 10px; - box-shadow: 0 2px 10px rgba(0,0,0,0.1); - overflow-y: auto; - z-index: 1000; - opacity: 0; - visibility: hidden; - transform: translateY(20px) scale(0.9); - transition: opacity 0.3s ease, transform 0.3s ease, visibility 0.3s; - } - .toc.show { - opacity: 1; - visibility: visible; - transform: translateY(0) scale(1); - } - .toc a { - display: block; - color: var(--toc-text); - text-decoration: none; - padding: 5px 0; - font-size: 14px; - line-height: 1.5; - border-bottom: 1px solid var(--toc-border); - transition: background-color 0.2s ease, padding-left 0.2s ease; - } - .toc a:last-child { - border-bottom: none; - } - .toc a:hover { - background-color: var(--toc-hover); - padding-left: 5px; - } - .toc-icon { - position: fixed; - bottom: 20px; - right: 20px; - cursor: pointer; - font-size: 24px; - background-color: var(--toc-icon-bg); - color: var(--toc-icon-color); - border: 2px solid var(--toc-icon-color); - border-radius: 50%; - width: 40px; - height: 40px; - display: flex; - align-items: center; - justify-content: center; - box-shadow: 0 1px 3px rgba(0,0,0,0.12); - z-index: 1001; - transition: all 0.3s ease; - user-select: none; - -webkit-tap-highlight-color: transparent; - outline: none; - } - .toc-icon:hover { - transform: scale(1.1); - } - .toc-icon:active { - transform: scale(0.9); - } - .toc-icon.active { - background-color: var(--toc-icon-active-bg); - color: var(--toc-icon-active-color); - border-color: var(--toc-icon-active-bg); - transform: rotate(90deg); - } - `; - loadResource('style', {css: css}); - - const tocIcon = document.createElement('div'); - tocIcon.className = 'toc-icon'; - tocIcon.textContent = '☰'; - tocIcon.onclick = (e) => { - e.stopPropagation(); - toggleTOC(); - }; - document.body.appendChild(tocIcon); - - document.addEventListener('click', (e) => { - const tocElement = document.querySelector('.toc'); - if (tocElement && tocElement.classList.contains('show') && !tocElement.contains(e.target) && !e.target.classList.contains('toc-icon')) { - toggleTOC(); - } - }); -}); diff --git a/static/assets/lazyload.js b/static/assets/lazyload.js deleted file mode 100644 index 724416d..0000000 --- a/static/assets/lazyload.js +++ /dev/null @@ -1,25 +0,0 @@ - // lazyload.js - document.addEventListener('DOMContentLoaded', () => { - const images = document.querySelectorAll('img[data-src]'); - - const lazyLoad = target => { - const io = new IntersectionObserver((entries, observer) => { - entries.forEach(entry => { - if (entry.isIntersecting) { - const img = entry.target; - img.src = img.dataset.src; - img.onload = () => { - img.classList.add('loaded'); - }; - observer.disconnect(); - } - }); - }); - - io.observe(target); - }; - - images.forEach(img => { - lazyLoad(img); - }); - }); diff --git a/static/background.webp b/static/background.webp index 2c5b5ba..477c578 100644 Binary files a/static/background.webp and b/static/background.webp differ diff --git a/static/plugins/ArticleTOC.js b/static/plugins/ArticleTOC.js new file mode 100644 index 0000000..b877c48 --- /dev/null +++ b/static/plugins/ArticleTOC.js @@ -0,0 +1,217 @@ +function loadResource(type, attributes) { + if (type === 'style') { + const style = document.createElement('style'); + style.textContent = attributes.css; + document.head.appendChild(style); + } +} + +function createTOC() { + const tocElement = document.createElement('div'); + tocElement.className = 'toc'; + tocElement.classList.add('show'); + + const contentContainer = document.querySelector('.markdown-body'); + contentContainer.appendChild(tocElement); + + const headings = contentContainer.querySelectorAll('h1, h2, h3, h4, h5, h6'); + headings.forEach(heading => { + if (!heading.id) { + heading.id = heading.textContent.trim().replace(/\s+/g, '-').toLowerCase(); + } + const link = document.createElement('a'); + link.href = '#' + heading.id; + link.textContent = heading.textContent; + + link.setAttribute('data-id', heading.id); + + link.className = 'toc-link'; + link.style.paddingLeft = `${(parseInt(heading.tagName.charAt(1)) - 1) * 10}px`; + tocElement.appendChild(link); + }); + + + tocElement.insertAdjacentHTML('beforeend', '返回顶部'); + contentContainer.prepend(tocElement); +} + +function highlightTOC() { + const tocLinks = document.querySelectorAll('.toc-link'); + const fromTop = window.scrollY + 10; + + let currentHeading = null; + + tocLinks.forEach(link => { + const section = document.getElementById(link.getAttribute('data-id')); + if (section && section.offsetTop <= fromTop) { + currentHeading = link; + } + }); + + tocLinks.forEach(link => { + link.classList.remove('active-toc'); + }); + + if (currentHeading) { + currentHeading.classList.add('active-toc'); + + // 确保当前高亮的目录项在可视区域的中间 + currentHeading.scrollIntoView({ + block: 'center', // 确保当前高亮项滚动到视图中间位置 + inline: 'nearest' // 可选,保持水平滚动条不动 + }); + } +} + +function toggleTOC() { + const tocElement = document.querySelector('.toc'); + const tocIcon = document.querySelector('.toc-icon'); + if (tocElement) { + tocElement.classList.toggle('show'); + tocIcon.classList.toggle('active'); + tocIcon.textContent = tocElement.classList.contains('show') ? '✖' : '☰'; + } +} + +document.addEventListener("DOMContentLoaded", function() { + createTOC(); + const css = ` + :root { + --toc-bg: rgba(237, 239, 233, 0.84); + --toc-border: #e1e4e8; + --toc-text: #24292e; + --toc-hover: #8ae9c4; + --toc-icon-bg: #fff; + --toc-icon-color: #ad6598; + --toc-icon-active-bg: #813c85; + --toc-icon-active-color: #fff; + } + + .toc { + position: fixed; + bottom: 60px; + right: 20px; + width: 250px; + max-height: 70vh; + background-color: var(--toc-bg); + border: 1px solid var(--toc-border); + border-radius: 8px; + padding: 10px; + box-shadow: 0 2px 10px rgba(0,0,0,0.1); + overflow-y: auto; + z-index: 1000; + opacity: 0; + visibility: hidden; + transform: translateY(20px) scale(0.9); + transition: opacity 0.3s ease, transform 0.3s ease, visibility 0.3s; + } + .toc.show { + opacity: 1; + visibility: visible; + transform: translateY(0) scale(1); + } + .toc a { + display: block; + border-radius: 8px; + color: var(--toc-text); + text-decoration: none; + padding: 5px 0; + font-size: 14px; + line-height: 1.5; + border-bottom: 1px solid var(--toc-border); + transition: background-color 0.2s ease, padding-left 0.2s ease; + } + .toc a:last-child { + border-bottom: none; + } + .toc a:hover { + background-color: var(--toc-hover); + padding-left: 5px; + border-radius: 8px; + } + .toc-icon { + position: fixed; + bottom: 20px; + right: 20px; + cursor: pointer; + font-size: 24px; + background-color: var(--toc-icon-bg); + color: var(--toc-icon-color); + border: 2px solid var(--toc-icon-color); + border-radius: 50%; + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 1px 3px rgba(0,0,0,0.12); + z-index: 1001; + transition: all 0.3s ease; + user-select: none; + -webkit-tap-highlight-color: transparent; + outline: none; + } + .toc-icon:hover { + transform: scale(0.9); + } + .toc-icon:active { + transform: scale(0.9); + } + .toc-icon.active { + background-color: var(--toc-icon-active-bg); + color: var(--toc-icon-active-color); + border-color: var(--toc-icon-active-bg); + transform: rotate(90deg); + } + + .toc-end { + font-weight: bold; + text-align: center; + cursor: pointer; + visibility: hidden; + background-color: white; + padding: 10px; /* 可选:增加一些内边距,使按钮更易点击 */ + border-radius: 8px; /* 可选:使按钮有圆角 */ + border: 1px solid var(--toc-border); /* 可选:增加边框,使其更明显 */ + } + + .active-toc { + font-weight: bold; + border-radius: 8px; + background-color: var(--toc-hover); /* 根据你的设计,可以定制高亮颜色 */ + padding-left: 5px; /* 可选:增加左边距以突出当前项目 */ + } + `; + loadResource('style', {css: css}); + + const tocIcon = document.createElement('div'); + tocIcon.className = 'toc-icon'; + + tocIcon.classList.add('active'); + + tocIcon.textContent = '✖'; + tocIcon.onclick = (e) => { + e.stopPropagation(); + toggleTOC(); + }; + document.body.appendChild(tocIcon); + + window.onscroll = function() { + const backToTopButton = document.querySelector('.toc-end'); + if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) { + backToTopButton.style="visibility: visible;background-color: white;" + } else { + backToTopButton.style="visibility: hidden;background-color: white;" + } + }; + + document.addEventListener('scroll', highlightTOC); + + document.addEventListener('click', (e) => { + const tocElement = document.querySelector('.toc'); + if (tocElement && tocElement.classList.contains('show') && !tocElement.contains(e.target) && e.target.classList.contains('toc-icon')) { + toggleTOC(); + + } + }); +}); diff --git a/static/assets/yifantheme_index.js b/static/plugins/RonanTheme.js similarity index 94% rename from static/assets/yifantheme_index.js rename to static/plugins/RonanTheme.js index a093299..8a1196a 100644 --- a/static/assets/yifantheme_index.js +++ b/static/plugins/RonanTheme.js @@ -29,7 +29,7 @@ style.innerHTML = ` } html { - background: url('https://bing.img.run/1920x1080.php') no-repeat center center fixed; + background: url('https://blog.freeblock.cn/background.webp') no-repeat center center fixed; background-size: cover; } @@ -76,7 +76,7 @@ document.head.appendChild(style); style.innerHTML = ` html { - background: url('https://wowpb.pages.dev/file/2a84a86f2bb288032e3a1.jpg') no-repeat center center fixed; + background: url('https://blog.freeblock.cn/background.webp') no-repeat center center fixed; background-size: cover; } @@ -148,7 +148,7 @@ document.head.appendChild(style); style.innerHTML = ` html { - background: url('https://wowpb.pages.dev/file/2a84a86f2bb288032e3a1.jpg') no-repeat center center fixed; + background: url('https://blog.freeblock.cn/background.webp') no-repeat center center fixed; background-size: cover; } diff --git a/static/plugins/lightbox.js b/static/plugins/lightbox.js new file mode 100644 index 0000000..b7389ef --- /dev/null +++ b/static/plugins/lightbox.js @@ -0,0 +1,369 @@ +(function() { + // 灯箱插件 + class Lightbox { + constructor(options = {}) { + this.options = Object.assign({ + animationDuration: 300, + closeOnOverlayClick: true, + onOpen: null, + onClose: null, + onNavigate: null + }, options); + + this.images = []; + this.currentIndex = 0; + this.isOpen = false; + this.isZoomed = false; + this.zoomLevel = 1; + this.touchStartX = 0; + this.touchEndX = 0; + this.wheelTimer = null; + + this.init(); + } + + init() { + this.createStyles(); + this.createLightbox(); + this.bindEvents(); + } + + createStyles() { + const style = document.createElement('style'); + style.textContent = ` + .lb-lightbox-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(255, 255, 255, 0.9); + backdrop-filter: blur(5px); + display: flex; + justify-content: center; + align-items: center; + opacity: 0; + transition: opacity ${this.options.animationDuration}ms ease; + pointer-events: none; + } + .lb-lightbox-overlay.active { + pointer-events: auto; + } + .lb-lightbox-content-wrapper { + position: relative; + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + } + .lb-lightbox-container { + max-width: 90%; + max-height: 90%; + position: relative; + transition: transform ${this.options.animationDuration}ms cubic-bezier(0.25, 0.1, 0.25, 1); + } + .lb-lightbox-image { + max-width: 100%; + max-height: 100%; + object-fit: contain; + border-radius: 8px; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); + transition: transform ${this.options.animationDuration}ms cubic-bezier(0.25, 0.1, 0.25, 1), opacity ${this.options.animationDuration}ms ease; + } + .lb-lightbox-nav { + position: absolute; + top: 50%; + transform: translateY(-50%); + background-color: rgba(255, 255, 255, 0.8); + color: #333; + border: none; + border-radius: 50%; + width: 50px; + height: 50px; + font-size: 24px; + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + transition: all 0.3s ease; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + } + .lb-lightbox-nav:hover { + background-color: rgba(255, 255, 255, 1); + transform: translateY(-50%) scale(1.1); + } + .lb-lightbox-nav:active { + transform: translateY(-50%) scale(0.9); + } + .lb-lightbox-prev { + left: 20px; + } + .lb-lightbox-next { + right: 20px; + } + .lb-lightbox-close { + position: absolute; + top: 20px; + right: 20px; + background-color: rgba(255, 255, 255, 0.8); + color: #333; + border: none; + border-radius: 50%; + width: 40px; + height: 40px; + font-size: 24px; + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + transition: all 0.3s ease; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + } + .lb-lightbox-close:hover { + background-color: rgba(255, 255, 255, 1); + transform: scale(1.1); + } + .lb-lightbox-close:active { + transform: scale(0.9); + } + @media (max-width: 768px) { + .lb-lightbox-nav { + width: 40px; + height: 40px; + font-size: 20px; + } + .lb-lightbox-close { + width: 35px; + height: 35px; + font-size: 20px; + } + } + @media (prefers-color-scheme: dark) { + .lb-lightbox-overlay { + background-color: rgba(0, 0, 0, 0.9); + } + .lb-lightbox-nav, + .lb-lightbox-close { + background-color: rgba(50, 50, 50, 0.8); + color: #fff; + } + .lb-lightbox-nav:hover, + .lb-lightbox-close:hover { + background-color: rgba(70, 70, 70, 1); + } + .lb-lightbox-image { + box-shadow: 0 10px 30px rgba(255, 255, 255, 0.1); + } + } + `; + document.head.appendChild(style); + } + + createLightbox() { + this.overlay = document.createElement('div'); + this.overlay.className = 'lb-lightbox-overlay'; + this.overlay.style.zIndex = '-1'; + + this.contentWrapper = document.createElement('div'); + this.contentWrapper.className = 'lb-lightbox-content-wrapper'; + + this.container = document.createElement('div'); + this.container.className = 'lb-lightbox-container'; + + this.image = document.createElement('img'); + this.image.className = 'lb-lightbox-image'; + + this.prevButton = document.createElement('button'); + this.prevButton.className = 'lb-lightbox-nav lb-lightbox-prev'; + this.prevButton.innerHTML = '❮'; + + this.nextButton = document.createElement('button'); + this.nextButton.className = 'lb-lightbox-nav lb-lightbox-next'; + this.nextButton.innerHTML = '❯'; + + this.closeButton = document.createElement('button'); + this.closeButton.className = 'lb-lightbox-close'; + this.closeButton.innerHTML = '×'; + + this.container.appendChild(this.image); + this.contentWrapper.appendChild(this.container); + this.contentWrapper.appendChild(this.prevButton); + this.contentWrapper.appendChild(this.nextButton); + this.contentWrapper.appendChild(this.closeButton); + + this.overlay.appendChild(this.contentWrapper); + + document.body.appendChild(this.overlay); + } + + bindEvents() { + document.addEventListener('click', this.handleImageClick.bind(this), true); + this.overlay.addEventListener('click', this.handleOverlayClick.bind(this)); + this.prevButton.addEventListener('click', this.showPreviousImage.bind(this)); + this.nextButton.addEventListener('click', this.showNextImage.bind(this)); + this.closeButton.addEventListener('click', this.close.bind(this)); + document.addEventListener('keydown', this.handleKeyDown.bind(this)); + this.overlay.addEventListener('wheel', this.handleWheel.bind(this)); + this.overlay.addEventListener('touchstart', this.handleTouchStart.bind(this)); + this.overlay.addEventListener('touchmove', this.handleTouchMove.bind(this)); + this.overlay.addEventListener('touchend', this.handleTouchEnd.bind(this)); + } + + handleImageClick(event) { + const clickedImage = event.target.closest('img'); + if (clickedImage && !this.isOpen) { + event.preventDefault(); + event.stopPropagation(); + this.images = Array.from(document.querySelectorAll('.markdown-body img')); + this.currentIndex = this.images.indexOf(clickedImage); + this.open(); + } + } + + handleOverlayClick(event) { + if (event.target === this.overlay && this.options.closeOnOverlayClick) { + this.close(); + } else if (!event.target.closest('.lb-lightbox-container')) { + const elementBelow = document.elementFromPoint(event.clientX, event.clientY); + if (elementBelow) { + elementBelow.click(); + } + } + } + + handleKeyDown(event) { + if (!this.isOpen) return; + + switch (event.key) { + case 'ArrowLeft': + this.showPreviousImage(); + break; + case 'ArrowRight': + this.showNextImage(); + break; + case 'Escape': + this.close(); + break; + } + } + + handleWheel(event) { + event.preventDefault(); + clearTimeout(this.wheelTimer); + + this.wheelTimer = setTimeout(() => { + const delta = Math.sign(event.deltaY); + if (delta > 0) { + this.showNextImage(); + } else { + this.showPreviousImage(); + } + }, 50); + } + + handleTouchStart(event) { + this.touchStartX = event.touches[0].clientX; + } + + handleTouchMove(event) { + this.touchEndX = event.touches[0].clientX; + } + + handleTouchEnd() { + const difference = this.touchStartX - this.touchEndX; + if (Math.abs(difference) > 50) { + if (difference > 0) { + this.showNextImage(); + } else { + this.showPreviousImage(); + } + } + } + + open() { + this.isOpen = true; + this.overlay.style.zIndex = '10000'; + this.overlay.classList.add('active'); + this.showImage(); + this.overlay.style.opacity = '1'; + document.body.style.overflow = 'hidden'; + if (typeof this.options.onOpen === 'function') { + this.options.onOpen(); + } + } + + close() { + this.isOpen = false; + this.overlay.style.opacity = '0'; + this.overlay.classList.remove('active'); + document.body.style.overflow = ''; + setTimeout(() => { + this.image.style.transform = ''; + this.zoomLevel = 1; + this.isZoomed = false; + this.overlay.style.zIndex = '-1'; + }, this.options.animationDuration); + if (typeof this.options.onClose === 'function') { + this.options.onClose(); + } + } + + showPreviousImage() { + if (this.currentIndex > 0) { + this.currentIndex--; + this.showImage(); + } + } + + showNextImage() { + if (this.currentIndex < this.images.length - 1) { + this.currentIndex++; + this.showImage(); + } + } + + showImage() { + const imgSrc = this.images[this.currentIndex].src; + this.image.style.opacity = '0'; + + const newImage = new Image(); + newImage.src = imgSrc; + newImage.onload = () => { + this.image.src = imgSrc; + this.image.style.opacity = '1'; + }; + + this.prevButton.style.display = this.currentIndex > 0 ? '' : 'none'; + this.nextButton.style.display = this.currentIndex < this.images.length - 1 ? '' : 'none'; + + if (typeof this.options.onNavigate === 'function') { + this.options.onNavigate(this.currentIndex); + } + + this.preloadImages(); + } + + zoom(factor) { + this.zoomLevel += factor; + this.zoomLevel = Math.max(1, Math.min(this.zoomLevel, 3)); + this.image.style.transform = `scale(${this.zoomLevel})`; + this.isZoomed = this.zoomLevel !== 1; + } + + preloadImages() { + const preloadNext = (this.currentIndex + 1) % this.images.length; + const preloadPrev = (this.currentIndex - 1 + this.images.length) % this.images.length; + new Image().src = this.images[preloadNext].src; + new Image().src = this.images[preloadPrev].src; + } + } + + // 将 Lightbox 类添加到全局对象 + window.Lightbox = Lightbox; + + // 自动初始化 + document.addEventListener('DOMContentLoaded', () => { + new Lightbox(); + }); +})(); diff --git a/static/assets/rain.js b/static/plugins/rain.js similarity index 100% rename from static/assets/rain.js rename to static/plugins/rain.js