UNPKG

vitepress-theme-async

Version:

<h1 align="center">vitepress-theme-async</h1>

336 lines (310 loc) 8.96 kB
import { stringFormat } from '../shared'; /** * 窗体已滚动距离 * @returns */ export const getScrollTop = () => { const supportPageOffset = window.pageXOffset !== undefined; const isCSS1Compat = (document.compatMode || '') === 'CSS1Compat'; return supportPageOffset ? window.pageYOffset : isCSS1Compat ? document.documentElement.scrollTop : document.body.scrollTop; }; /** * 窗体高度 * @returns */ export const getViewPortHeight = () => { return window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; }; /** * 判断是否在可视范围内 * @param el * @returns */ export const isInViewPortOfOne = (el: HTMLElement) => { const viewPortHeight = getViewPortHeight(); const scrollTop = getScrollTop(); const viewPortBottom = scrollTop + viewPortHeight; const { bottom, top, height } = el.getBoundingClientRect(); const isIn = bottom > 0 && top < viewPortBottom; if (isIn) { return { is: true, ratio: top < 0 ? Math.abs(top / height) : 0, }; } else { return { is: false, }; } }; /** * 设置移动端-状态栏主题 * @param colorVal */ export const setThemeColor = (colorVal = '--theme-bg-color') => { const themeColor = getComputedStyle(document.documentElement).getPropertyValue(colorVal); if (themeColor) { const metaDom = document.querySelector<HTMLMetaElement>('meta[name="theme-color"]'); if (metaDom) { metaDom.content = themeColor; } else { const meta = document.createElement('meta'); meta.name = 'theme-color'; meta.content = themeColor; document.head.appendChild(meta); } } }; /** * 主题切换动画 * @param wait * @returns */ export const themeLoading = (wait: number = 600): Promise<void> => { return new Promise<void>(resolve => { document.documentElement.classList.add('theme-animating'); setTimeout(() => { setTimeout(() => { document.documentElement.classList.remove('theme-animating'); }, wait); resolve(); }, wait); }); }; /** * 阅读模式切换 */ export const switchReadMode = () => { const $body = document.body; type Flag = { el: HTMLElement; ratio?: number } | void; const getScrollFlag = () => { return new Promise<Flag>(resolve => { const article = document.getElementById('article-container') as HTMLDivElement; if (article) { const list = Array.from(article.children); for (let i = 0; i < list.length; i++) { const el = <HTMLElement>list[i]; const flag = isInViewPortOfOne(el); if (flag.is) { resolve({ el, ratio: flag.ratio, }); return; } } resolve(); } else { resolve(); } }); }; const setScroll = (data: Flag) => { if (data && data.ratio && data.ratio > 0) { const { top, height } = data.el.getBoundingClientRect(); const elementTop = data.ratio * height + top + getScrollTop(); window.scrollTo({ top: elementTop }); } }; getScrollFlag().then(data => { $body.classList.toggle('trm-read-mode'); setScroll(data); }); }; /** * 切换单双栏 */ export const switchSingleColumn = () => document.body.classList.toggle('trm-single-column'); /** * */ export const backTop = (smooth?: boolean) => window.scrollTo({ top: 0, behavior: smooth ? 'smooth' : 'auto' }); /** * 动态获取脚本 * @param url * @param condition 是否存在对应实例,判断是否加载脚本 * @returns */ export const loadScript = (url: string, condition: boolean) => { return new Promise<void>((resolve, reject) => { if (condition) { resolve(); } else { const script = document.createElement('script'); script.src = url; script.setAttribute('async', 'false'); script.onerror = reject; script.onload = () => resolve(); document.head.appendChild(script); } }); }; /** * 包裹元素 * @param el * @param wrapper * @param options */ export const wrap = (el: HTMLElement, wrapper: string | HTMLElement, options = {}) => { if (typeof wrapper === 'string') { wrapper = document.createElement(wrapper); for (const [key, value] of Object.entries(options)) { wrapper.setAttribute(key, value as string); } } el?.parentNode?.insertBefore(wrapper, el); wrapper.appendChild(el); }; /** * 初始化图库排版 * @param url fjGallery CDN */ export const initJustifiedGallery = (url: string) => { const gallerys = document.querySelectorAll('.fj-gallery'); if (gallerys.length && url) { gallerys.forEach(item => { const imgList = item.querySelectorAll('img'); imgList.forEach((i: HTMLImageElement) => { i.loading = 'eager'; wrap(i, 'div', { class: 'fj-gallery-item', 'data-src': i.dataset.src || i.src, 'data-fancybox': 'gallery', }); }); }); loadScript(url, window.fjGallery).then(() => { gallerys.forEach(selector => { window.fjGallery(selector, { itemSelector: '.fj-gallery-item', rowHeight: 220, gutter: 4, onJustify: function () { this.$container.style.opacity = '1'; }, }); }); }); } }; /** * 处理文章图片 * @param url Fancybox CDN */ export const initPictures = (url: string) => { const picts = () => { document.querySelectorAll<HTMLImageElement>('#article-container img:not(.no-fancybox)').forEach(img => { if (!img?.parentElement?.dataset?.fancybox) { let fancybox = 'article'; if (img.classList.contains('trm-light-icon')) { fancybox = 'light'; } else if (img.classList.contains('trm-dark-icon')) { fancybox = 'dark'; } wrap(<HTMLImageElement>img, 'div', { 'data-src': img.dataset.src || img.src, 'data-fancybox': fancybox, }); } }); }; if (window.Fancybox) { picts(); } else { loadScript(url, window.Fancybox).then(() => { window?.Fancybox.bind('[data-fancybox]'); window?.Fancybox.bind('[data-fancybox="dark"],[data-fancybox="article"]', { groupAll: true }); window?.Fancybox.bind('[data-fancybox="light"],[data-fancybox="article"]', { groupAll: true }); window.Fancybox.defaults.Hash = false; picts(); }); } }; let intersectionObserver: IntersectionObserver | undefined; /** * 滚动动画 */ export const initScrollAnimation = () => { intersectionObserver?.disconnect(); intersectionObserver = new IntersectionObserver( entries => { entries.forEach(({ isIntersecting, target }) => { if (isIntersecting) { target.classList.add('trm-active-el'); intersectionObserver?.unobserve(target); } }); }, { threshold: [0, 1], rootMargin: '0px 0px -40px 0px' }, ); document.querySelectorAll('.trm-scroll-animation').forEach(element => element && intersectionObserver?.observe(element)); }; /** * 文章破图显示替换图片 * @param url 破图时显示图片 * @returns */ export const initPostErrorImg = (url: string) => { const imgs = document.querySelectorAll<HTMLImageElement>('#article-container img'); imgs.forEach(img => img.setAttribute('onerror', `this.onerror=null;this.src="${url}";`)); return imgs.length > 0; }; /** * 页面失焦时标题东湖 * @param failure 失焦时的图标 * @param showText 重新获取焦点时显示文案 * @param hideText 失焦时显示文案 */ export const initVisibilitychange = (failure: string, showText: string, hideText: string) => { const originTitle = document.title; const iconEls = Array.from<HTMLLinkElement>(document.querySelectorAll('[rel="icon"]')); const icons = iconEls.map(item => item.href); let titleTime: any; document.addEventListener('visibilitychange', function () { if (document.hidden) { iconEls.forEach(item => { item.href = failure; }); document.title = hideText ?? ''; clearTimeout(titleTime); } else { iconEls.forEach((item, index) => { item.href = icons[index]; }); document.title = showText + originTitle; titleTime = setTimeout(function () { document.title = originTitle; }, 2000); } }); }; /** * 复制时 - 添加版权信息 * @param config */ export const initClipboard = (config: { author?: string; license?: string; text: string }) => { document.addEventListener('copy', function (event: ClipboardEvent) { const clipboardData = event.clipboardData; if (!clipboardData) { return; } let author = config.author || ''; const text = window.getSelection()?.toString() || ''; if (text) { event.preventDefault(); const authorEl = document.getElementById('post-author'); if (authorEl) { author = authorEl.innerText.replace('\n', ''); } const license = config.license || 'by-nc-sa'; const ccVersion = config.license == 'zero' ? '1.0' : '4.0'; let originalLink = location.href; const originalLinkEl = document.getElementById('original-link'); if (originalLinkEl) { originalLink = originalLinkEl.innerText.replace('\n', ''); } const copyrightText = stringFormat(config.text, author, originalLink, license.toUpperCase(), ccVersion); clipboardData.setData('text/plain', text + copyrightText); } }); };