每條游來游去的小魚,都是正在瀏覽這一頁的訪客,有一條是你自己 ( ´ ▽ ` )ノ 覺得干擾閱讀的話,可以把小魚關掉。
神機錯亂包裝器
wrapper包起來的元素會降低人性損耗,出現神機錯亂徵兆
| 名稱 | 描述 |
|---|---|
| SVG Filter | SVG 濾鏡效果,例如:模糊、扭曲、顏色調整等等 |
| SVG feTurbulence | 以 Perlin 雜訊產生紋理,可分別指定 x、y 方向的頻率,常拿來當位移圖 |
| SVG feDisplacementMap | 依位移圖的色值偏移取樣來源像素,可做出扭曲、凹陷、波動等效果 |
| IntersectionObserver | 偵測元素是否進入或離開視窗 |
包起來就會自己發作,也可以呼叫 glitch() 立即觸發。滑鼠移到按鈕上試試。
<template> <div class="w-full flex flex-col items-center py-16"> <wrapper-cyberpsychosis ref="wrapperRef"> <base-btn :label="t('button')" @pointerenter="wrapperRef?.glitch()" /> </wrapper-cyberpsychosis> </div></template>
<script setup lang="ts">import { useTemplateRef } from 'vue'import { useI18n } from 'vue-i18n'import BaseBtn from '../../base-btn.vue'import WrapperCyberpsychosis from '../wrapper-cyberpsychosis.vue'
const { t } = useI18n()
const wrapperRef = useTemplateRef('wrapperRef')</script>輸入錯誤時呼叫 glitch(),錯越多次越不穩定。
NIGHT CITY ID // SCANR25
<template> <div class="w-full flex justify-center py-12"> <div class="cyber-panel" :class="{ 'is-error': errorCount > 0, 'is-success': succeeded, }" > <div class="panel-header"> <span>NIGHT CITY ID // SCAN</span> <span class="panel-tag">R25</span> </div>
<label class="field-label" :for="inputId" > EMAIL_ADDRESS </label>
<wrapper-cyberpsychosis ref="inputWrapperRef" :intensity="intensity" :enabled="unstable" :interval="[800, 2000]" > <div class="input-frame"> <!-- Chrome 會忽略 autocomplete="off",改給它不認得的值才不會自動填入; data-* 分別讓 1Password、LastPass、Bitwarden 跳過這個欄位 --> <input :id="inputId" v-model="email" class="cyber-input" placeholder="v@nightcity.net" autocomplete="cyber-id-scan" data-1p-ignore data-lpignore="true" data-bwignore @input="succeeded = false" @keydown.enter="submit" > </div> </wrapper-cyberpsychosis>
<div class="status"> <template v-if="message"> {{ succeeded ? '✓ ACCESS_GRANTED ::' : `▲ ERR_0${Math.min(errorCount, 9)} ::` }} {{ message }} </template> </div>
<button class="cyber-button" type="button" @click="submit" > {{ t('submit') }} </button> </div> </div></template>
<script setup lang="ts">import { computed, ref, useId, useTemplateRef } from 'vue'import { useI18n } from 'vue-i18n'import WrapperCyberpsychosis from '../wrapper-cyberpsychosis.vue'
const { t } = useI18n()
const EMAIL_PATTERN = /^[^\s@]+@[^\s@][^\s.@]*\.[^\s@]+$//** errorList 的句數,錯超過就停在最後一句 */const ERROR_MESSAGE_COUNT = 5
const inputId = useId()const inputWrapperRef = useTemplateRef('inputWrapperRef')
const email = ref('')const errorCount = ref(0)const succeeded = ref(false)
/** 錯越多次越不穩定 */const intensity = computed(() => Math.min(1 + errorCount.value * 0.3, 2))/** 錯三次後就開始三不五時自己閃現 */const unstable = computed(() => errorCount.value >= 3)
const message = computed(() => { if (succeeded.value) { return t('success') } if (errorCount.value === 0) { return '' }
const index = Math.min(errorCount.value, ERROR_MESSAGE_COUNT) - 1 return t(`errorList[${index}]`)})
function submit() { if (EMAIL_PATTERN.test(email.value)) { errorCount.value = 0 succeeded.value = true return }
errorCount.value++ inputWrapperRef.value?.glitch(600)}</script>用 SVG 濾鏡把元素複製成幾個原色分身,往四周錯開、疊在本體上;再以 feTurbulence 產生橫條雜訊,交給 feDisplacementMap 做出輕微的切片錯位。
每次發作數百毫秒,每隔幾十毫秒換一格,偶爾恢復正常,訊號才有斷斷續續的感覺。沒發作時直接拿掉濾鏡,系統開啟「減少動態效果」時不會發作。
interface Props { /** * 是否自動隨機發作。關閉後仍可呼叫 `glitch()` 手動觸發。 * * @default true */ enabled?: boolean; /** * 強度,影響分身間距與切片位移量 * * @default 1 */ intensity?: number; /** * 兩次發作的間隔範圍(ms) * * @default [1500, 4500] */ interval?: [number, number]; /** * 每次發作的持續時間範圍(ms) * * @default [200, 700] */ duration?: [number, number];}Methods
Section titled “Methods”interface Expose { /** * 立即發作一次 * * @param duration 持續時間(ms),預設從 `duration` 範圍隨機 */ glitch: (duration?: number) => void;}