Skip to content

Every little fish swimming around is a visitor on this page right now, and one of them is you ( ´ ▽ ` )ノ If they get in the way of reading, feel free to turn them off.

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"
      />
    </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.93.1