接上一个文章,来对这个js做一些简单的解析。
原理其实很简单,因为我懒得去一个一个css的去适配所以采用了一个很简单的方法,直接把所有的白色元素替换成黑的就可以了,然后对文字颜色进行反转,但是这样就会出现一个问题那就是子元素没办法即使替换,本来想过10ms替换一下但这样会导致站点原有的动画不停循环所以我写的是鼠标点击一次就进行一次替换以及在页面第一次加载也进行一次替换。
Script==<br>// @name 喵御宅深色模式-Mfuns Dark Mode<br>// @namespace https://moongs.asia/<br>// @version 0.01<br>// @description 安装后自动将Mfuns变为深色模式<br>// @author Sakurazuki<br>// @match https://www.mfuns.net/*<br>// @match https://mfuns.net/*<br>// @grant none<br>// @run-at document-start<br>// ==/UserScript==<br><br>(function() {<br> 'use strict';<br> const CONFIG = {<br> bgDark: '#0d0d0d',<br> bgCard: '#1a1a1a',<br> bgHover: '#242424',<br> bgInput: '#2a2a2a',<br> textPrimary: '#e8e8e8',<br> textSecondary: '#888888',<br> textMuted: '#666666',<br> borderColor: '#2a2a2a',<br> accentColor: '#4a9eff',<br> dangerColor: '#e88080',<br> };<br> function getColorBrightness(color) {<br> if (!color || color === 'transparent' || color === 'rgba(0, 0, 0, 0)') return -1;<br> const match = color.match(/\d+/g);<br> if (!match) return -1;<br> const r = parseInt(match[0]);<br> const g = parseInt(match[1]);<br> const b = parseInt(match[2]);<br> return (r * 299 + g * 587 + b * 114) / 1000;<br> }<br> function isLightColor(color) {<br> const brightness = getColorBrightness(color);<br> return brightness > 160;<br> }<br> function getPageElements() {<br> const allElements = document.querySelectorAll('*');<br> const elements = [];<br> const seen = new Set();<br> allElements.forEach(el => {<br> if (el.offsetWidth === 0 && el.offsetHeight === 0) return;<br> const tag = el.tagName.toLowerCase();<br> if (['video', 'img', 'svg', 'canvas', 'iframe', 'script', 'style'].includes(tag)) return;<br> const classes = el.className ? String(el.className).split(/\s+/) : [];<br> const id = el.id || '';<br> let selector = '';<br> if (id) {<br> selector = `#${CSS.escape(id)}`;<br> } else if (classes.length) {<br> const validClasses = classes.filter(c => c && !c.startsWith('__') && !c.match(/^[0-9]/));<br> if (validClasses.length) {<br> selector = '.' + validClasses.join('.');<br> }<br> }<br> if (!selector || seen.has(selector)) return;<br> seen.add(selector);<br> const computed = window.getComputedStyle(el);<br> const bg = computed.backgroundColor;<br> const color = computed.color;<br> const border = computed.borderColor;<br> const isLightBg = bg && bg !== 'transparent' && bg !== 'rgba(0, 0, 0, 0)' && isLightColor(bg);<br> const hasBorder = border && border !== 'rgba(0, 0, 0, 0)' && border !== 'transparent';<br> if (isLightBg || hasBorder) {<br> elements.push({<br> selector: selector,<br> tag: tag,<br> bg: bg,<br> color: color,<br> border: border,<br> isLightBg: isLightBg,<br> hasBorder: hasBorder,<br> bgBrightness: getColorBrightness(bg),<br> });<br> }<br> });<br> return elements;<br> }<br> function generateStyles(elements) {<br> const { bgDark, bgCard, bgHover, bgInput, textPrimary, textSecondary, textMuted, borderColor, accentColor, dangerColor } = CONFIG;<br> let css = `<br> html, body, [id="m-app"], .m-layout, .m-layout-page,<br> .container, .container--full, .m-home-page,<br> .m-account-center__container {<br> background-color: ${bgDark} !important;<br> color: ${textPrimary} !important;<br> }<br> `;<br> const processed = new Set();<br> elements.forEach(el => {<br> const selector = el.selector;<br> if (processed.has(selector)) return;<br> if (selector.includes('video') || selector.includes('img') || selector.includes('svg')) return;<br> processed.add(selector);<br> let styles = [];<br> if (el.isLightBg) {<br> const isCard = /card|item|list|post|feed|menu|panel|box|container|section|article/.test(selector);<br> const isHeader = /header|nav|footer/.test(selector);<br> const isButton = /button|btn/.test(selector);<br> const isInput = /input|search|text/.test(selector);<br> if (isCard) {<br> styles.push(`background-color: ${bgCard} !important`);<br> } else if (isHeader) {<br> styles.push(`background-color: ${bgDark} !important`);<br> } else if (isButton) {<br> styles.push(`background-color: ${bgHover} !important`);<br> } else if (isInput) {<br> styles.push(`background-color: ${bgInput} !important`);<br> } else {<br> styles.push(`background-color: transparent !important`);<br> }<br> }<br> if (el.hasBorder) {<br> styles.push(`border-color: ${borderColor} !important`);<br> }<br> if (el.color && isLightColor(el.color)) {<br> const isSecondary = /secondary|sub|meta|time|count|info|desc/.test(selector);<br> const isMuted = /muted|placeholder|hint/.test(selector);<br> if (isSecondary) {<br> styles.push(`color: ${textSecondary} !important`);<br> } else if (isMuted) {<br> styles.push(`color: ${textMuted} !important`);<br> } else {<br> styles.push(`color: ${textPrimary} !important`);<br> }<br> }<br> if (styles.length) {<br> css += `\n ${selector} {\n ${styles.join(';\n ')};\n }`;<br> }<br> });<br> css += `<br> .m-header, .m-header__container, .m-header__container--mobile,<br> .m-nav, .m-nav__container {<br> background-color: ${bgCard} !important;<br> border-bottom: 1px solid ${borderColor} !important;<br> }<br> .m-header__logo, .m-header__logo--mini {<br> filter: brightness(0) invert(1) !important;<br> }<br> .m-input-search__placeholder {<br> background-color: ${bgInput} !important;<br> color: ${textSecondary} !important;<br> border-color: ${borderColor} !important;<br> }<br> .m-nav__link { color: ${textSecondary} !important; }<br> .m-nav__link--active { color: ${accentColor} !important; }<br> .m-nav__link:hover { color: ${textPrimary} !important; }<br> .m-post-card, .m-recommend__list__item {<br> background-color: ${bgCard} !important;<br> border: 1px solid ${borderColor} !important;<br> border-radius: 12px !important;<br> }<br> .m-post-card:hover {<br> background-color: ${bgHover} !important;<br> }<br> .m-post-card__title-text { color: ${textPrimary} !important; }<br> .m-post-card__title-text:hover { color: ${accentColor} !important; }<br> .m-post-card__info-text .text { color: ${textSecondary} !important; }<br> .m-post-card__info-text .icon { color: ${textMuted} !important; }<br> .m-post-card__duration {<br> background: rgba(0,0,0,0.7) !important;<br> color: #fff !important;<br> }<br> .m-home-page__posts h2, .m-home-page__hots h2 {<br> color: ${textPrimary} !important;<br> border-bottom: 2px solid ${borderColor} !important;<br> }<br> .m-home-page__hots {<br> background-color: ${bgCard} !important;<br> border: 1px solid ${borderColor} !important;<br> border-radius: 12px !important;<br> }<br> .m-home-page__hots .m-post-card {<br> background: transparent !important;<br> border: none !important;<br> }<br> .m-home-page__hots .m-post-card:hover {<br> background: ${bgHover} !important;<br> }<br> .m-home-swiper .title {<br> background: rgba(0,0,0,0.6) !important;<br> color: #fff !important;<br> }<br> .m-side-menu {<br> background-color: ${bgCard} !important;<br> border: 1px solid ${borderColor} !important;<br> border-radius: 12px !important;<br> }<br> .m-menu__list {<br> background-color: transparent !important;<br> padding: 8px 0 !important;<br> margin: 0 !important;<br> list-style: none !important;<br> }<br> .m-menu__list li {<br> background-color: transparent !important;<br> margin: 2px 6px !important;<br> border-radius: 8px !important;<br> list-style: none !important;<br> }<br> .m-menu__list .item {<br> display: flex !important;<br> align-items: center !important;<br> gap: 10px !important;<br> padding: 10px 14px !important;<br> color: ${textSecondary} !important;<br> border-radius: 8px !important;<br> text-decoration: none !important;<br> transition: all 0.2s ease !important;<br> font-size: 14px !important;<br> }<br> .m-menu__list .item .icon {<br> color: ${textMuted} !important;<br> font-size: 18px !important;<br> flex-shrink: 0 !important;<br> transition: color 0.2s !important;<br> }<br> .m-menu__list .item:hover {<br> background-color: ${bgHover} !important;<br> color: ${textPrimary} !important;<br> }<br> .m-menu__list .item:hover .icon {<br> color: ${textPrimary} !important;<br> }<br> .m-menu__list .item.active {<br> background-color: ${bgHover} !important;<br> color: ${accentColor} !important;<br> }<br> .m-menu__list .item.active .icon {<br> color: ${accentColor} !important;<br> }<br> .m-menu__list .item.danger {<br> color: ${dangerColor} !important;<br> }<br> .m-menu__list .item.danger .icon {<br> color: ${dangerColor} !important;<br> }<br> .m-menu__list .item.danger:hover {<br> background-color: rgba(232, 128, 128, 0.12) !important;<br> color: #ff6b6b !important;<br> }<br> .m-menu__list .item.danger:hover .icon {<br> color: #ff6b6b !important;<br> }<br> .m-menu__list .item.disabled {<br> color: #555 !important;<br> cursor: not-allowed !important;<br> opacity: 0.5 !important;<br> }<br> .m-menu__list .item.disabled .icon {<br> color: #555 !important;<br> }<br> .m-menu__list .item.disabled:hover {<br> background-color: transparent !important;<br> color: #555 !important;<br> }<br> .m-menu__list .item.disabled:hover .icon {<br> color: #555 !important;<br> }<br> .m-menu__list .n-badge {<br> background-color: transparent !important;<br> }<br> .m-menu__list .n-badge .n-badge-sup {<br> background-color: ${accentColor} !important;<br> color: #fff !important;<br> }<br> .m-side-menu .n-scrollbar-rail__scrollbar {<br> background-color: #444 !important;<br> }<br> .m-side-menu .n-scrollbar-rail__scrollbar:hover {<br> background-color: #666 !important;<br> }<br> .m-account-center__header {<br> background-color: ${bgCard} !important;<br> border: 1px solid ${borderColor} !important;<br> border-radius: 16px !important;<br> overflow: hidden !important;<br> }<br> .m-account-center__header-banner {<br> background-color: #161616 !important;<br> }<br> .m-account-center__header-banner_mask {<br> background: linear-gradient(to bottom, transparent 50%, rgba(0,0,0,0.7) 100%) !important;<br> }<br> .m-account-center__header-banner_user-info .username {<br> color: ${textPrimary} !important;<br> }<br> .m-account-center__header-banner_user-info-detail > div:last-child {<br> color: ${textSecondary} !important;<br> }<br> .m-account-center__header-user {<br> background-color: ${bgCard} !important;<br> padding: 16px 20px !important;<br> }<br> .m-account-center__header-user-bio { color: #ccc !important; }<br> .m-account-center__header-user hr {<br> border-color: ${borderColor} !important;<br> opacity: 1 !important;<br> }<br> .m-account-center__header-user-count { background-color: transparent !important; }<br> .m-account-center__header-user-count > div { background-color: transparent !important; }<br> .m-account-center__header-user-count .number { color: ${textPrimary} !important; }<br> .m-account-center__header-user-count .label { color: ${textSecondary} !important; }<br> .m-follow-button {<br> background-color: ${accentColor} !important;<br> color: #fff !important;<br> border: none !important;<br> border-radius: 20px !important;<br> }<br> .m-follow-button:hover { background-color: #6bb3ff !important; }<br> .m-container.m-feed-list {<br> background-color: ${bgCard} !important;<br> border: 1px solid ${borderColor} !important;<br> border-radius: 16px !important;<br> }<br> .m-load span { color: ${textSecondary} !important; }<br> .m-account-center__side { background-color: transparent !important; }<br> .m-account-center__side-section {<br> background-color: ${bgCard} !important;<br> border: 1px solid ${borderColor} !important;<br> border-radius: 12px !important;<br> padding: 16px !important;<br> }<br> .m-account-center__side-section h4 {<br> color: ${textPrimary} !important;<br> border-bottom: 1px solid ${borderColor} !important;<br> padding-bottom: 8px !important;<br> }<br> .m-account-center__side-section h4 .icon { color: ${accentColor} !important; }<br> .m-account-center__side-section .content a { color: ${textSecondary} !important; }<br> .m-account-center__side-section .content a:hover { color: ${accentColor} !important; }<br> .m-user-card {<br> background-color: ${bgCard} !important;<br> border: 1px solid ${borderColor} !important;<br> border-radius: 16px !important;<br> color: ${textPrimary} !important;<br> }<br> .m-user-card hr { border-color: ${borderColor} !important; opacity: 1 !important; }<br> .m-user-card__premium .m-text:last-child {<br> color: ${accentColor} !important;<br> font-weight: 500 !important;<br> }<br> .m-user-card__fans .m-text.secondary { color: ${textSecondary} !important; }<br> .m-user-card__action:hover { background-color: ${bgHover} !important; }<br> .m-user-card .n-progress-graph-line-rail { background-color: ${bgInput} !important; }<br> .m-user-card .n-progress-graph-line-fill { background-color: #a78bfa !important; }<br> .m-footer, .m-footer__container {<br> background-color: ${bgDark} !important;<br> border-top: 1px solid ${borderColor} !important;<br> }<br> .m-footer__section .content a { color: ${textSecondary} !important; }<br> .m-footer__section .content a:hover { color: ${accentColor} !important; }<br> .m-footer__copyright { color: ${textMuted} !important; }<br> .m-footer__copyright a { color: ${textSecondary} !important; }<br> p, h1, h2, h3, h4, span, div, a, li, label {<br> color: ${textPrimary} !important;<br> }<br> a:hover { color: ${accentColor} !important; }<br> input, textarea, select {<br> background-color: ${bgInput} !important;<br> color: ${textPrimary} !important;<br> border-color: ${borderColor} !important;<br> }<br> ::-webkit-scrollbar { width: 8px; height: 8px; }<br> ::-webkit-scrollbar-track { background: ${bgCard}; }<br> ::-webkit-scrollbar-thumb { background: #444; border-radius: 4px; }<br> ::-webkit-scrollbar-thumb:hover { background: #555; }<br> video, .video-player, [class*="player"] {<br> background-color: #000 !important;<br> }<br> img, svg, canvas, .m-img {<br> background-color: transparent !important;<br> }<br> .n-skeleton {<br> --n-color-start: ${bgHover} !important;<br> --n-color-end: ${bgInput} !important;<br> }<br> .m-badge .m-badge__item {<br> background-color: transparent !important;<br> }<br> .m-account-center__header-banner_user .m-avatar {<br> background-color: ${bgInput} !important;<br> border: 3px solid #333 !important;<br> }<br> .m-side-qualification .m-text.secondary { color: ${textSecondary} !important; }<br> .m-side-qualification .m-text.tertiary { color: ${textMuted} !important; }<br> .m-side-qualification hr { border-color: ${borderColor} !important; opacity: 1 !important; }<br> .m-side-qualification a { color: ${accentColor} !important; }<br> .m-side-qualification .lic-img { filter: brightness(0.8) !important; }<br> `;<br> return css;<br> }<br> function applyDarkMode() {<br> const oldStyle = document.getElementById('mfuns-dark-mode-auto');<br> if (oldStyle) {<br> oldStyle.remove();<br> }<br> const elements = getPageElements();<br> console.log(`find ${elements.length} elements`);<br> const css = generateStyles(elements);<br> const style = document.createElement('style');<br> style.id = 'mfuns-dark-mode-auto';<br> style.textContent = css;<br> document.head.appendChild(style);<br> document.documentElement.classList.add('mfuns-dark-active');<br> console.log('darkmode=1');<br> }<br> function debounce(func, wait) {<br> let timeout;<br> return function(...args) {<br> clearTimeout(timeout);<br> timeout = setTimeout(() => func.apply(this, args), wait);<br> };<br> }<br> function init() {<br> if (document.readyState === 'complete') {<br> applyDarkMode();<br> } else {<br> document.addEventListener('DOMContentLoaded', applyDarkMode);<br> window.addEventListener('load', applyDarkMode);<br> }<br> const debouncedApply = debounce(applyDarkMode, 300);<br> document.addEventListener('click', function(e) {<br> if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.tagName === 'SELECT') {<br> return;<br> }<br> if (e.target.tagName === 'VIDEO') {<br> return;<br> }<br> debouncedApply();<br> }, true);<br> const observer = new MutationObserver(debounce(applyDarkMode, 500));<br> observer.observe(document.body, {<br> childList: true,<br> subtree: true,<br> attributes: true,<br> attributeFilter: ['class', 'style'],<br> });<br> console.log('done');<br> }<br> if (document.readyState === 'loading') {<br> document.addEventListener('DOMContentLoaded', init);<br> } else {<br> init();<br> }<br>})();
当然肯定是不会那么简单的,因为站点原来是紫色色调,直接反转会有点不协调,所以我参照一个bilibili的美化插件改成了蓝色并且给很多需要突出的元素比如侧边栏,视频这些都做了一个圆角边框的处理,这样显着也挺好看的我感觉。
但是这还是有许多的bug毕竟有的css原来就是实时刷新的比如说投稿的时候那个自动保存,这就会带动其他元素一起刷新导致黑色失效,目前我也不怎么喜欢投稿所以这个问题我现在也懒得修,只能说等有时间了去针对这些特别的页面去专门做适配。