Skip to content

Real Dark Mode util

Really dark (◐‿◑)

Inspired by this work.

Tech Keywords

NameDescription
Babylon.js3D engine
Canvas ShaderWritten in GLSL and executed directly on the GPU, faster than the Canvas 2D API but harder to master
Post-processingEnhances rendering results with effects such as lens blur and color splitting
Pointer EventsDetects pointer movement, clicks, hovers, and more, providing coordinates and target information
JS AnimationJavaScript-driven animation for more complex, precise control; popular libraries include GSAP and anime.js
Vue DirectiveCustom Vue directives that encapsulate DOM manipulation logic and repeated behaviors

Examples

Basic Usage

The light follows your cursor. When you press and hold, the light stops moving and points at the mouse position.

Use the v-util-real-dark directive to mark the elements that should cast shadows.

甚麼是暗黑模式?

暗黑模式(Dark Mode)一種把介面背景變暗、文字變亮的顯示方式。

主要目的是為了在低光源環境下減少螢幕刺眼的亮度、降低視覺疲勞,在部分裝置上還能稍微省點電。

另外的好處是當你半夜睡不著覺,想把心情哼成歌,在黑暗中滑開手機時,避免手機變成閃光彈。

開啟暗黑模式之後,你會發現:

  • 看起來比較專業,但依然無法提早下班
  • 滑社群時比較不會眼睛痠,但還是會被閃照閃瞎鈦合金狗眼

以上,現在你知道我們說的黑是甚麼黑了吧?(⌐■_■)✧

#Canvas #Shader

Bystander: I'm pretty sure your "dark" isn't the "dark" of dark mode Σ(ˊДˋ;)

View the example source code
vue
<template>
  <div class="example-wrap w-full p-4">
    <div
      v-util-real-dark
      class="w-full rounded-lg"
    >
      <base-checkbox
        v-model="enable"
        :label="t('enableRealDarkMode')"
        class="example-ctrl p-4"
      />
    </div>

    <div class="w-full py-4">
      <div class="mb-3 text-2xl font-bold">
        {{ t('whatIsDarkMode') }}
      </div>

      <div class="text-base leading-relaxed space-y-3">
        <p>
          {{ t('darkModeDescription') }}
        </p>

        <p>
          {{ t('darkModePurpose') }}
        </p>

        <p>
          {{ t('darkModeBenefit') }}
        </p>

        <p>
          {{ t('darkModeAfterEnable') }}
        </p>

        <ul class="list-disc list-inside space-y-1">
          <li>{{ t('darkModeAfterEnableList1') }}</li>
          <li>{{ t('darkModeAfterEnableList2') }}</li>
        </ul>

        <p>
          {{ t('darkModeAfterEnableConclusion') }}
        </p>
      </div>

      <!-- tags -->
      <div class="mt-4 flex gap-2">
        <span
          v-util-real-dark
          class="border border-gray-200 rounded-full p-2 px-4 text-sm text-gray-500 dark:text-gray-400"
        >
          #Canvas
        </span>
        <span
          v-util-real-dark
          class="border border-gray-200 rounded-full p-2 px-4 text-sm text-gray-500 dark:text-gray-400"
        >
          #Shader
        </span>
      </div>
    </div>

    <util-real-dark
      class="fixed left-0 top-0 z-[100] h-full w-full"
      :disabled="!enable"
    />
  </div>
</template>

<script setup lang="ts">
import { useData } from 'vitepress'
import { onBeforeUnmount, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import BaseCheckbox from '../../base-checkbox.vue'
import UtilRealDark from '../util-real-dark.vue'
import { vUtilRealDark } from '../v-util-real-dark'

const { isDark } = useData()
const oriValue = isDark.value

const { t } = useI18n()
const enable = ref(false)

watch(enable, (value) => {
  if (value) {
    isDark.value = false
  }
  else {
    isDark.value = oriValue
  }
})

onBeforeUnmount(() => {
  isDark.value = oriValue
})
</script>

How it works

I originally implemented it with drawing-related libraries like p5.js, but recreating effects like light projection and bloom/glow was just too hard, so this component ended up sitting idle for quite a while...(›´ω`‹ )

One day I suddenly had an idea: “Don’t 3D engines already have light sources built in?” So I switched to implementing it with Babylon.js.

The key is using a shader to set alpha based on grayscale values—making brighter areas more transparent—to achieve the light-beam effect.

Finally, after adding the moving light source effect, it worked! ( •̀ ω •́ )✧

Source Code

API

Props

interface Props {
  disabled?: boolean;
  /** 平滑係數。越小越慢,延遲越明顯 */
  ease?: number;
  /** 甩尾強度。越大越明顯 */
  offsetStrength?: number;
  /** 燈光尺寸 */
  size?: number;
  lightColor?: string;
  /** 透明度 @default 0.9 */
  opacity?: number;
}

v0.69.0