Appearance
hangul-typist
ManualTypist
문자(한글) 입력기
ts
import { typingMatrix } from '@jood/helpdesk-module/hangul-core';
import { ManualTypist } from '@jood/helpdesk-module/hangul-typist';
const source = `안녕하세요. Hello~ 😎
타이핑 애니메이션 입니다.`;
const manual = new ManualTypist();
manual.observe().subscribe((evt) => {
console.log(evt);
});
manual.setSourceMatrix(typingMatrix(source));
manual.setTypingSpeed(40, 60);
manual.addDelayByEqual('\n', 120);
manual.addDelayByEqual('.', 100);
manual.start();
// manual.pause();
// manual.resume();
// manual.destroy();
0 / 0
vue
<template>
<div class="typist-container">
<div class="actions">
<button class="action" @click="onStart">start</button>
<button class="action" @click="onPause">pause</button>
<button class="action" @click="onResume">resume</button>
</div>
<div class="output-area">
<div class="count">{{ state.typingIndex }} / {{ state.typingTotal }}</div>
<div class="output">{{ state.output }}</div>
</div>
<div class="input-area">
<textarea class="input" v-model="source"></textarea>
</div>
<div class="options">
<dl class="opt">
<dt class="dt">입력 속도</dt>
<dd class="dd">
<input class="speed-put" type="number" :min="0" :max="state.max" v-model="state.min" /> ~
<input class="speed-put" type="number" :min="state.min" :max="9000" v-model="state.max" />
</dd>
</dl>
</div>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import { typingMatrix } from '@jood/helpdesk-module/hangul-core';
import { ManualTypist } from '@jood/helpdesk-module/hangul-typist';
const source = ref(`안녕하세요. Hello~ 😎
타이핑 애니메이션 입니다.`);
const state = reactive({
output: '',
typingIndex: 0,
typingTotal: 0,
min: 40,
max: 60,
});
const manual = new ManualTypist();
// manual.addDelayByEqual('\n', 120);
// manual.addDelayByEqual('.', 100);
manual.observe().subscribe((evt) => {
const { value, typingTotal, typingIndex } = evt;
state.output = value;
state.typingTotal = typingTotal;
state.typingIndex = typingIndex;
});
const onStart = () => {
manual.setSourceMatrix(typingMatrix(source.value));
manual.setTypingSpeed(state.min, state.max);
manual.start();
};
const onPause = () => {
manual.pause();
};
const onResume = () => {
manual.resume();
};
</script>
<style lang="scss" scoped>
.typist-container {
margin: 0 auto;
padding: 10px 10px 60px 10px;
max-width: 760px;
}
.input-area {
padding: 10px;
box-sizing: border-box;
.input {
display: block;
padding: 10px;
width: 100%;
min-height: 90px;
font-size: 14px;
color: #333;
box-sizing: border-box;
border: 1px solid #ddd;
}
}
.output-area {
padding: 10px;
word-break: break-all;
border-top: 3px double #e0e0e0;
.count {
font-size: 16px;
font-weight: bold;
text-align: right;
}
.output {
font-size: 24px;
font-weight: bold;
line-height: 1.6;
text-align: center;
white-space: pre-line;
}
}
.options {
margin-top: -20px;
display: flex;
justify-content: flex-end;
.opt {
display: flex;
align-items: center;
padding: 2px 12px;
.dt {
padding-right: 10px;
font-size: 14px;
font-weight: bold;
}
.dd {
display: flex;
align-items: center;
}
.speed-put {
display: block;
padding: 0;
height: 32px;
width: 80px;
text-align: center;
border: 1px solid #ddd;
appearance: none;
}
}
}
.actions {
display: flex;
align-items: center;
justify-content: center;
padding: 10px;
.action {
margin: 2px;
background-color: #ffffff;
border: 1px solid rgb(209, 213, 219);
border-radius: 0.5rem;
box-sizing: border-box;
color: #111827;
font-size: 0.875rem;
font-weight: 600;
line-height: 1.25rem;
padding: 0.75rem 1rem;
text-align: center;
text-decoration: none #d1d5db solid;
text-decoration-thickness: auto;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
cursor: pointer;
user-select: none;
-webkit-user-select: none;
touch-action: manipulation;
&:hover {
background-color: rgb(249, 250, 251);
}
&:focus {
outline: 2px solid transparent;
outline-offset: 2px;
}
&:focus-visible {
box-shadow: none;
}
}
}
</style>