Greasy Fork

自动加载css.js

加载远程/本地css样式文件

// ==UserScript==
// @name         自动加载css.js
// @namespace    http://tampermonkey.net/
// @version      2025-04-24-2
// @description  加载远程/本地css样式文件
// @author       You
// @include *
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// @license MIT
// ==/UserScript==

(function() {
  // 默认的远程地址
  let remoteCssUrl = localStorage.getItem('remoteCssUrl') || 'https://localhost/site-resources/dynamic-css.css';

  let autoLoad = false;
  let intervalId = null;
  let lastCss = '';
  const borderCss = `
.border-animate{
    position: relative;
}
.border-animate::before {
    pointer-events: none;
    opacity: 0.5;
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 999999999;
    padding: 3px;
    background: linear-gradient(90deg, red, orange, yellow, green, blue, indigo, violet, red);
    background-size: 200% 200%;
    /*border-radius: 12px;*/
    -webkit-mask:
            linear-gradient(#fff 0 0) content-box,
            linear-gradient(#fff 0 0);
    -webkit-mask-composite: xor;
    mask-composite: exclude;
    animation: borderSlide 3s linear infinite;
}

@keyframes borderSlide {
    0% {
        background-position: 0% 50%;
    }
    100% {
        background-position: 100% 50%;
    }
}
  `
  // 添加控制面板
  const ctrlDiv = document.createElement('div');
  ctrlDiv.id = 'css-controller';
  /*  ctrlDiv.style.cssText = `
      position: fixed;
      top: 0;
      left: 0;
      z-index: 999999999999;
      background: rgba(255,255,255,0.9);
      padding: 4px;
      font-size: 12px;
      display: flex;
      gap: 4px;
    `;*/
  ctrlDiv.innerHTML = `
<style>
@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
.spin {
  box-shadow: 0 0px 8px rgb(67 217 169 / 80%), inset 0 3px 1px rgb(41 229 160 / 54%) !important;
  animation: spin 3s linear infinite;
}
#css-fab-wrapper {
  position: fixed;
  top: 100px;
  left: 20px;
  z-index: 9999;
  display: flex;
  gap: 10px;
  /*transition: left 0.1s ease; !* 平滑吸附动画 *!*/
}

#css-fab {
  opacity: 0.75;
  background: rgba(227,227,227,0.1);
  width: 48px;
  height: 48px;
  border-radius: 50%;
  cursor: grab;
  box-shadow: 0 2px 10px rgba(0,0,0,0.2);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.3s;
  user-select: none;
  font-size: 2.5em;
}

#css-fab-wrapper:hover #css-fab {
  font-size: 2.8em;
  transition: font-size 0.1s;
}

.fab-btns {
  top: 0;
  left: 60px;
  display: none;
  flex-direction: column;
  gap: 6px;
}
#css-fab-wrapper:hover .fab-btns {
  display: flex;
}
.fab-btns button {
  padding: 4px 8px;
  font-size: 12px;
  background-color: white;
  border: 1px solid #ccc;
  border-radius: 6px;
  cursor: pointer;
  white-space: nowrap;
  box-shadow: 0 1px 4px rgba(0,0,0,0.1);
}
</style>

<div id="css-fab-wrapper">
  <div id="css-fab">⚙️</div>
  <div class="fab-btns">
    <button id="auto-load-btn">自动加载CSS</button>
    <button id="clear-css-btn">清除CSS</button>
    <button id="manual-css-btn">手动载入CSS</button>
    <button id="set-css-url-btn">设置 CSS 地址</button>
  </div>
</div>
  `;
  document.body.appendChild(ctrlDiv);

  function saveFabPosition(left, top) {
    localStorage.setItem('fabPosition', JSON.stringify({ left, top }));
  }

  function loadFabPosition() {
    const pos = JSON.parse(localStorage.getItem('fabPosition'));
    if (pos && typeof pos.left === 'number' && typeof pos.top === 'number') {
      wrapper.style.left = `${pos.left}px`;
      wrapper.style.top = `${pos.top}px`;
    }
  }

  const getDocument = () => {
    const iframe = document.querySelector(".lc-simulator-content-frame")
    return iframe?.contentDocument || iframe?.contentWindow?.document || document;
  }

  // 样式标签处理
  const getStyleTag = () => {
    const doc = getDocument();
    let tag = doc.querySelector('#my-dynamic-style');
    if (!tag) {
      const header = doc.querySelector('head')
      tag = document.createElement('style');
      tag.id = 'my-dynamic-style';
      header.appendChild(tag);
    }
    return tag;
  };

  // 拉取并应用 CSS
  async function fetchAndApplyCSS() {
    try {
      const res = await fetch(remoteCssUrl);
      const css = await res.text();
      if (css !== lastCss) {
        getStyleTag().innerHTML = borderCss + css;
        lastCss = css;
        console.log('[CSS] 样式已更新');
      }
    } catch (err) {
      console.warn('[CSS] 加载失败:', err);
      window.Next?.Message?.error?.(`[CSS] 加载失败, 请检查设置的css地址: ${err} ${remoteCssUrl}`);
    }
  }

  const fabIcon = document.getElementById('css-fab');

  const stopAutoLoad = () => {
    clearInterval(intervalId);
    intervalId = null;
    autoLoad = false;
    fabIcon.classList.remove('spin');
    document.getElementById('auto-load-btn').innerText = '自动加载CSS';
    console.log('[CSS] 自动加载已关闭');
  }

  function highLightHtml(times = 3) {
    if (times <= 0){
      return
    }
    getDocument()?.querySelector('html')?.classList?.add('border-animate')
    setTimeout(() => {
      getDocument()?.querySelector('html')?.classList?.remove('border-animate')
      setTimeout(() => {
        highLightHtml(times - 1)
      }, 300)
    }, 700)
  }

  document.getElementById('auto-load-btn').addEventListener('click', () => {
    if (autoLoad) {
      stopAutoLoad()
    } else {
      fetchAndApplyCSS();
      highLightHtml()
      intervalId = setInterval(fetchAndApplyCSS, 1000);
      autoLoad = true;
      fabIcon.classList.add('spin');
      document.getElementById('auto-load-btn').innerText = '关闭自动加载';
      console.log('[CSS] 自动加载已开启');
    }
  });
  // 清除按钮逻辑
  document.getElementById('clear-css-btn').addEventListener('click', () => {
    stopAutoLoad()
    const tag = getStyleTag();
    if (tag) tag.remove();
    lastCss = '';
    console.log('[CSS] 样式已清除');
    window.Next?.Message?.success?.('[CSS] 样式已清除')
  });

  // 手动载入按钮逻辑
  document.getElementById('manual-css-btn').addEventListener('click', () => {
    fetchAndApplyCSS();
    highLightHtml()
    console.log('[CSS] 手动载入完成');
    window.Next?.Message?.success?.('[CSS] 手动载入完成')
  });

  // 设置按钮事件
  document.getElementById('set-css-url-btn').addEventListener('click', () => {
    const input = prompt('请输入新的远程 CSS 地址:', remoteCssUrl);
    if (input && input.trim()) {
      remoteCssUrl = input.trim();
      localStorage.setItem('remoteCssUrl', remoteCssUrl);
      console.log('已更新 CSS 地址');
      window.Next?.Message?.success?.('已更新 CSS 地址');
    }
  });

  // 拖动逻辑
  const wrapper = document.getElementById('css-fab-wrapper');
  const fab = document.getElementById('css-fab');
  let isDragging = false, offsetX = 0, offsetY = 0;

  loadFabPosition();

  fab.addEventListener('mousedown', e => {
    isDragging = true;
    offsetX = e.clientX - wrapper.offsetLeft;
    offsetY = e.clientY - wrapper.offsetTop;
    fab.style.cursor = 'grabbing';
  });

  document.addEventListener('mousemove', e => {
    if (isDragging) {
      const halfWidth = wrapper.offsetWidth / 2;
      const halfHeight = wrapper.offsetHeight / 2;

      const maxLeft = window.innerWidth - halfWidth;
      const maxTop = window.innerHeight - halfHeight;

      let newLeft = e.clientX - offsetX;
      let newTop = e.clientY - offsetY;

      newLeft = Math.max(-halfWidth, Math.min(newLeft, maxLeft));
      newTop = Math.max(-halfHeight, Math.min(newTop, maxTop));

      wrapper.style.left = `${newLeft}px`;
      wrapper.style.top = `${newTop}px`;

      saveFabPosition(newLeft, newTop); // 保存位置
    }
  });

  document.addEventListener('mouseup', () => {
    isDragging = false;
    fab.style.cursor = 'grab';
  });
})();