解碼輸入框 input
輸入文字時會有酷酷的解碼效果。(⌐■_■)✧
使用範例
基本用法
不管是輸入還是貼上,都會有解碼效果,而且完全不會干擾輸入過程。
查看範例原始碼
vue
<template>
<div class="w-full flex flex-col items-center gap-4 border border-gray-300 p-6">
<input-decoding
v-model="text"
class="input-decoding px-3 py-2"
/>
<span class="break-all border-b px-3 pb-1">
{{ text }}
</span>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import InputDecoding from '../input-decoding.vue'
const text = ref('')
</script>
<style scoped lang="sass">
.input-decoding
border: 1px solid #ccc
border-inline: 2px solid #AAA
</style>
自定義解碼表
自定義解碼表,玩出更多變化。
查看範例原始碼
vue
<template>
<div class="w-full flex flex-col items-center gap-4 border border-gray-300 p-6">
<input-decoding
v-model="text"
class="input-decoding px-3 py-2"
:charset
:decode-interval="40"
/>
<span class="break-all border-b px-3 pb-1">
{{ text }}
</span>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import InputDecoding from '../input-decoding.vue'
const text = ref('')
const charset = [
/** 數字 */
(char: string) => char.match(/\d/)
? '¥$¢€£'
: undefined,
/** 中文 */
(char: string) => char.match(/[\u4E00-\u9FA5]/)
? 'ㄅㄆㄇㄈㄉㄊㄋㄌㄍㄎㄏㄐㄑㄒㄓㄔㄕㄖㄗㄘㄙㄚㄛㄜㄝㄞㄟㄠㄡㄢㄣㄤㄥㄦ'
: undefined,
/** emoji */
(char: string) => char.match(/\p{Emoji}/u)
? '♩♪♫♬𝄞'
: undefined,
/** 其他 */
() => 'ᚠᚢᚦᚨᚱᚳᚷᚹᚻᚾᛁᛂᛇᛈᛉᛊᛏᛒᛖᛗᛚᛝᛟᛡᛢᛣᛤᛥᛦᛧᛨ',
]
</script>
<style scoped lang="sass">
.input-decoding
border: 1px solid #ccc
border-inline: 2px solid #AAA
</style>
科幻輸入框
配合科幻卡片元件,打造未來感十足的輸入框。
codlin
查看範例原始碼
vue
<template>
<div class="w-full flex flex-col items-center gap-4 border border-gray-300 p-6">
<div class="flex flex-col gap-4 border rounded">
<base-checkbox
v-model="visible"
label="顯示輸入框"
class="p-4"
/>
</div>
<div class="flex flex-col items-center gap-4 py-6">
<card-futuristic
:visible
v-bind="cardParams"
>
<input-decoding
v-model="text"
class="font-orbitron px-3 py-2"
:class="{ 'pointer-events-none': !visible }"
charset="ᚠᚢᚦᚨᚱᚳᚷᚹᚻᚾᛁᛂᛇᛈᛉᛊᛏᛒᛖᛗᛚᛝᛟᛡᛢᛣᛤᛥᛦᛧᛨ"
@focus="cardParams.selected = true"
@blur="cardParams.selected = false"
/>
</card-futuristic>
<div class="font-orbitron break-all">
{{ text }}
</div>
</div>
</div>
</template>
<script setup lang="ts">
import type { Writable } from 'type-fest'
import type { ExtractComponentProps } from '../../../types'
import { ref } from 'vue'
import BaseCheckbox from '../../base-checkbox.vue'
import CardFuturistic from '../../card-futuristic/card-futuristic.vue'
import InputDecoding from '../input-decoding.vue'
type CardParams = Writable<ExtractComponentProps<typeof CardFuturistic>>
const visible = ref(false)
const text = ref('codlin')
const cardParams = ref<CardParams>({
selected: false,
corner: {
type: 'square',
size: 4,
},
bg: null,
content: {
type: 'typical',
class: 'p-1',
},
border: {
type: 'typical',
color: '#BBB',
},
animeSequence: {
visible: {
corner: { delay: 0 },
border: { delay: 400 },
content: { delay: 500 },
},
hidden: {
corner: { delay: 400 },
border: { delay: 0 },
content: { delay: 0 },
},
},
})
</script>
<style lang="sass">
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+TC:[email protected]&family=Orbitron:[email protected]&family=Oxanium:[email protected]&display=swap')
.font-orbitron
font-family: "Orbitron", sans-serif
</style>
原理
透過 input
輸入事件偵測每個輸入文字,轉換每個字元為動態解碼效果。
中文拼字怎麼處理?
開發這個元件讓我長了新知識,就是 input
的 compositionstart
和 compositionend
事件,可以確保中文正常輸入,拼字的時候不會被誤判成多次輸入。
輸入時,每個字元會被轉換為 useChar
產生的物件,根據設定產生隨機變化的效果,使字元看起來像是被「解碼」一樣逐漸顯示。
說起來簡單,實際上還要考慮各種輸入情境,包括:
輸入、貼上、拖動文字
都是插入文字,但是有些微差別,需要分別在
onInput
或onBeforeInput
處理。選取
第 1 點的操作加上選取,需特別注意 caret 位置或隱含的刪除行為。
刪除
有分
Forward
與Backward
兩個方向。維持插入點(caret)
input
內容變化時,插入點位置會被重置到最尾端,所以解碼動畫播放時,也要不斷維持插入點位置。拼字與插入點(caret)
中文拼字時,
onInput
與onBeforeInput
中的input.selectionStart
與input.selectionEnd
與英文輸入不同,需要特別處理。emoji
1 個 emoji 為 2 字元,需要謹慎處理。
已知 Bug
若文字內有 emoji,反白後編輯與段落中插入文字,都會有錯位問題。
主因是因為 emoji 會讓 selectionStart
與 selectionEnd
與實際文字長度不同,
目前還想不到甚麼好辦法。( ´•̥̥̥ ω •̥̥̥` )
原始碼
API
Props
interface Props {
modelValue?: string;
/** 解碼效果的字元集
*
* 可以根據 char 來決定字元集,依矩陣順序判斷
*
* @default 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
*/
charset?: string | Array<(char: string) => string | undefined>;
/** 字符變化間隔毫秒數
*
* @default 20
*/
decodeInterval?: number;
/** 解碼次數
*
* @default 10
*/
decodeTimes?: number;
/** 每個字符的延遲毫秒數
*
* @default 20
*/
perCharDecodeDelay?: number;
/** 最大延遲毫秒數
*
* @default 1000
*/
maxDecodeDelay?: number;
}
Emits
interface Emits {
'update:modelValue': [value: Props['modelValue']];
}
Slots
interface Slots {
default?: () => unknown;
}