跳至主要內容

使用 VueUse 暗黑模式無法正常顯示轉場動畫特效

Pamis Wang大约 3 分鐘前端VueVue 3Vue.jsVue 3.js

使用 VueUse 暗黑模式無法正常顯示轉場動畫特效

官方範例

以下是官方 useDark 的官方範例

import { useDark, useToggle } from "@vueuse/core";

const isDark = useDark();
const toggleDark = useToggle(isDark);

如果在 10.2.1 這個版本,會發現轉場動畫特效無法正常顯示,
然而在 9.13.0 這個版本,轉場動畫特效卻可以正常運作。
雖然 github 上有些人提出的解決方式是透過降板來處理,
但為了一探究竟我們將會把兩個版本的源碼做簡單的分析。

源碼查看

透過 VSCode 查看 useDark() 的定義
點開檔案 node_modules@vueuse\core\index.d.ts

index.d.ts

useDark

位於第 965 行

/**
 * Reactive dark mode with auto data persistence.
 *
 * @see https://vueuse.org/useDark
 * @param options
 */
declare function useDark(options?: UseDarkOptions): vue_demi.WritableComputedRef<boolean>;

UseDarkOptions

這裡可以看到 useDark 函式內可以傳入 UseDarkOptions 參數
進一步查看 UseDarkOptions 可以發現是繼承 UseColorModeOptions

位於第 938 行

interface UseDarkOptions extends Omit<UseColorModeOptions<BasicColorSchema>, 'modes' | 'onChanged'> {
    /**
     * Value applying to the target element when isDark=true
     *
     * @default 'dark'
     */
    valueDark?: string;
    /**
     * Value applying to the target element when isDark=false
     *
     * @default ''
     */
    valueLight?: string;
    /**
     * A custom handler for handle the updates.
     * When specified, the default behavior will be overridden.
     *
     * @default undefined
     */
    onChanged?: (isDark: boolean) => void;
}

UseColorModeOptions

這邊就是關鍵!可以看到 9.13.010.2.1 的差異多了 disableTransition 這個選項

對應版本:9.13.0

interface UseColorModeOptions<T extends string = BasicColorSchema> extends UseStorageOptions<T | BasicColorSchema> {
  /**
   * CSS Selector for the target element applying to
   *
   * @default 'html'
   */
  selector?: string;
  /**
   * HTML attribute applying the target element
   *
   * @default 'class'
   */
  attribute?: string;
  /**
   * The initial color mode
   *
   * @default 'auto'
   */
  initialValue?: T | BasicColorSchema;
  /**
   * Prefix when adding value to the attribute
   */
  modes?: Partial<Record<T | BasicColorSchema, string>>;
  /**
   * A custom handler for handle the updates.
   * When specified, the default behavior will be overridden.
   *
   * @default undefined
   */
  onChanged?: (mode: T | BasicColorSchema, defaultHandler: (mode: T | BasicColorSchema) => void) => void;
  /**
   * Custom storage ref
   *
   * When provided, `useStorage` will be skipped
   */
  storageRef?: Ref<T | BasicColorSchema>;
  /**
   * Key to persist the data into localStorage/sessionStorage.
   *
   * Pass `null` to disable persistence
   *
   * @default 'vueuse-color-scheme'
   */
  storageKey?: string | null;
  /**
   * Storage object, can be localStorage or sessionStorage
   *
   * @default localStorage
   */
  storage?: StorageLike;
  /**
   * Emit `auto` mode from state
   *
   * When set to `true`, preferred mode won't be translated into `light` or `dark`.
   * This is useful when the fact that `auto` mode was selected needs to be known.
   *
   * @default undefined
   */
  emitAuto?: boolean;
}

對應版本:10.2.1

interface UseColorModeOptions<T extends string = BasicColorMode> extends UseStorageOptions<T | BasicColorMode> {
  /**
   * CSS Selector for the target element applying to
   *
   * @default 'html'
   */
  selector?: string | MaybeElementRef;
  /**
   * HTML attribute applying the target element
   *
   * @default 'class'
   */
  attribute?: string;
  /**
   * The initial color mode
   *
   * @default 'auto'
   */
  initialValue?: MaybeRefOrGetter<T | BasicColorSchema>;
  /**
   * Prefix when adding value to the attribute
   */
  modes?: Partial<Record<T | BasicColorSchema, string>>;
  /**
   * A custom handler for handle the updates.
   * When specified, the default behavior will be overridden.
   *
   * @default undefined
   */
  onChanged?: (mode: T | BasicColorMode, defaultHandler: (mode: T | BasicColorMode) => void) => void;
  /**
   * Custom storage ref
   *
   * When provided, `useStorage` will be skipped
   */
  storageRef?: Ref<T | BasicColorSchema>;
  /**
   * Key to persist the data into localStorage/sessionStorage.
   *
   * Pass `null` to disable persistence
   *
   * @default 'vueuse-color-scheme'
   */
  storageKey?: string | null;
  /**
   * Storage object, can be localStorage or sessionStorage
   *
   * @default localStorage
   */
  storage?: StorageLike;
  /**
   * Emit `auto` mode from state
   *
   * When set to `true`, preferred mode won't be translated into `light` or `dark`.
   * This is useful when the fact that `auto` mode was selected needs to be known.
   *
   * @default undefined
   * @deprecated use `store.value` when `auto` mode needs to be known
   * @see https://vueuse.org/core/useColorMode/#advanced-usage
   */
  emitAuto?: boolean;
  /**
   * Disable transition on switch
   *
   * @see https://paco.me/writing/disable-theme-transitions
   * @default true
   */
  disableTransition?: boolean;
}

index.cjs

useColorMode

node_modules@vueuse\core\index.cjs
第 1667 行

function useColorMode(options = {}) {
  const {
    selector = "html",
    attribute = "class",
    initialValue = "auto",
    window = defaultWindow,
    storage,
    storageKey = "vueuse-color-scheme",
    listenToStorageChanges = true,
    storageRef,
    emitAuto,
    disableTransition = true
  } = options;

第 1698 行

if (disableTransition) {
  style = window.document.createElement("style");
  const styleString = "*,*::before,*::after{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}";
  style.appendChild(document.createTextNode(styleString));
  window.document.head.appendChild(style);
}

這邊可以看到 disableTransition = true 以及符合條件就會強制清空轉場的效果
所以只要能將 disableTransition = false 傳入 useDark()就可以了

結論

不想看長篇大論直接這樣抄

import { useDark, useToggle } from "@vueuse/core";

const isDark = useDark({ disableTransition: false });
const toggleDark = useToggle(isDark);

參考資料

使用【useDark, useToggle】控制 element-plus Switch 组件切换会导致动画异常open in new window
使用 useDark(), 发现 transition 动画失效?open in new window
Disable transitions on theme toggleopen in new window

上次編輯於:
貢獻者: pamis