Kaynağa Gözat

暗黑模式切换效果优化

RuoYi 5 ay önce
ebeveyn
işleme
c6f3d8b455

+ 1 - 1
src/assets/icons/svg/size.svg

@@ -1 +1 @@
-<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 54.857h54.796v18.286H36.531V128H18.265V73.143H0V54.857zm127.857-36.571H91.935V128H72.456V18.286H36.534V0h91.326l-.003 18.286z"/></svg>
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path fill="#5a5e66" d="M0 54.857h54.796v18.286H36.531V128H18.265V73.143H0V54.857zm127.857-36.571H91.935V128H72.456V18.286H36.534V0h91.326l-.003 18.286z"/></svg>

+ 31 - 0
src/assets/styles/transition.scss

@@ -47,3 +47,34 @@
 .breadcrumb-leave-active {
   position: absolute;
 }
+
+/* 黑暗模式下过渡效果 */
+::view-transition-new(root), ::view-transition-old(root) {
+  animation: none !important;
+  backface-visibility: hidden;
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+}
+
+.dark::view-transition-old(root) {
+  z-index: 2147483646;
+  background: var(--bg-color-dark); 
+}
+
+.dark::view-transition-new(root) {
+  z-index: 1;
+  background: var(--bg-color);
+}
+
+::view-transition-old(root) {
+  z-index: 1;
+  background: var(--bg-color);
+}
+
+::view-transition-new(root) {
+  z-index: 2147483646;
+  background: var(--bg-color-dark);
+}

+ 30 - 0
src/assets/styles/variables.module.scss

@@ -89,6 +89,9 @@ html.dark {
   --el-text-color-regular: #d0d0d0;
   --el-border-color: #434343;
   --el-border-color-light: #434343;
+  
+  /* primary */
+  --primary-bg: #18212b;
 
   /* 侧边栏 */
   --sidebar-bg: #141414;
@@ -173,6 +176,33 @@ html.dark {
     }
   }
 
+  /* 按钮样式覆盖 */
+  .el-button--primary.is-plain {
+    background-color: var(--primary-bg);
+    border: 1px solid var(--el-color-primary-light-2);
+    color: var(--el-color-primary-light-2);
+
+    &:hover {
+      background-color: var(--el-button-hover-bg-color);
+      border-color: var(--el-button-hover-border-color);
+      color: var(--el-button-hover-text-color);
+    }
+
+    &.is-disabled {
+      background-color: var(--link-active-bg-color);
+      border-color: var(--el-color-primary-light-3);
+      color: var(--el-color-primary-light-3);
+      opacity: 0.5;
+    }
+  }
+
+  /* primary tag 样式覆盖 */
+  .el-tag--primary {
+    background-color: var(--primary-bg);
+    border: 1px solid var(--el-border-color-light);
+    color: var(--el-color-primary);
+  }
+
   /* 表格样式覆盖 */
   .el-table {
     --el-table-header-bg-color: var(--el-bg-color-overlay) !important;

+ 38 - 2
src/layout/components/Navbar.vue

@@ -111,8 +111,44 @@ function setLayout() {
   emits('setLayout')
 }
 
-function toggleTheme() {
-  settingsStore.toggleTheme()
+async function toggleTheme(event) {
+  const x = event?.clientX || window.innerWidth / 2
+  const y = event?.clientY || window.innerHeight / 2
+  const wasDark = settingsStore.isDark
+
+  const isReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches
+  const isSupported = document.startViewTransition && !isReducedMotion
+
+  if (!isSupported) {
+    settingsStore.toggleTheme()
+    return
+  }
+
+  try {
+    const transition = document.startViewTransition(async () => {
+      await new Promise((resolve) => setTimeout(resolve, 10))
+      settingsStore.toggleTheme()
+      await nextTick()
+    })
+    await transition.ready
+
+    const endRadius = Math.hypot(Math.max(x, window.innerWidth - x), Math.max(y, window.innerHeight - y))
+    const clipPath = [`circle(0px at ${x}px ${y}px)`, `circle(${endRadius}px at ${x}px ${y}px)`]
+    document.documentElement.animate(
+      {
+        clipPath: !wasDark ? [...clipPath].reverse() : clipPath
+      }, {
+        duration: 650,
+        easing: "cubic-bezier(0.4, 0, 0.2, 1)",
+        fill: "forwards",
+        pseudoElement: !wasDark ? "::view-transition-old(root)" : "::view-transition-new(root)"
+      }
+    )
+    await transition.finished
+  } catch (error) {
+    console.warn("View transition failed, falling back to immediate toggle:", error)
+    settingsStore.toggleTheme()
+  }
 }
 </script>