Files
TypeWords/src/App.vue
zyronon 6d4e1680ac save
2023-08-23 19:32:23 +08:00

317 lines
8.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import {computed, onMounted, onUnmounted, provide, watch} from "vue"
import 快速打字的机械键盘声音Mp3 from './assets/sound/key-sounds/快速打字的机械键盘声音.mp3'
import 键盘快速打字的声音Mp3 from './assets/sound/key-sounds/键盘快速打字的声音.mp3'
import 电话打字的声音Mp3 from './assets/sound/key-sounds/电话打字的声音.mp3'
import 老式机械 from './assets/sound/key-sounds/老式机械.mp3'
import 机械0 from './assets/sound/key-sounds/jixie/机械0.mp3'
import 机械1 from './assets/sound/key-sounds/jixie/机械1.mp3'
import 机械2 from './assets/sound/key-sounds/jixie/机械2.mp3'
import 机械3 from './assets/sound/key-sounds/jixie/机械3.mp3'
import beep from './assets/sound/beep.wav'
import correct from './assets/sound/correct.wav'
import {$ref} from "vue/macros"
import {useSound} from "@/hooks/useSound.ts"
import {useBaseStore} from "@/stores/base.ts"
import {DictType, SaveKey, Word} from "./types";
import WordList from "./components/WordList.vue";
import Side from "@/components/Side.vue"
import {usePlayWordAudio} from "@/hooks/usePlayWordAudio.ts"
import DictModal from "@/components/Modal/DictModal.vue"
import Backgorund from "@/components/Backgorund.vue"
import Statistics from "@/components/Modal/Statistics.vue";
import useThemeColor from "@/hooks/useThemeColor";
import Tooltip from "@/components/Tooltip.vue";
import Toolbar from "@/components/Toolbar/Toolbar.vue"
import {KeyboardOne} from "@icon-park/vue-next";
import BaseButton from "@/components/BaseButton.vue";
let input = $ref('')
let wrong = $ref('')
let showFullWord = $ref(false)
let isDictation = $ref(true)
let activeIndex = $ref(-1)
const store = useBaseStore()
// const [playKeySound, setAudio] = useSound([机械0, 机械1, 机械2, 机械3], 1)
// const [playKeySound, setAudio] = useSound([老式机械], 3)
const [playKeySound, setAudio] = useSound([电话打字的声音Mp3], 3)
const [playBeep] = useSound([beep], 1)
const [playCorrect] = useSound([correct], 1)
const [playAudio] = usePlayWordAudio()
const keyMap = {
Show: 'Escape',
Ignore: 'Tab',
Remove: '`',
Collect: 'Enter',
}
const restWord = $computed(() => {
return store.word.name.slice(input.length + wrong.length)
})
onMounted(() => {
store.init()
window.addEventListener('keydown', onKeyDown)
window.addEventListener('keyup', onKeyUp)
})
onUnmounted(() => {
// console.log('onUnmounted')
window.removeEventListener('keydown', onKeyDown)
window.removeEventListener('keyup', onKeyUp)
})
watch(store.$state, (n) => {
localStorage.setItem(SaveKey, JSON.stringify(n))
})
function next() {
if (store.currentDict.wordIndex === store.chapter.length - 1) {
if (store.currentDict.chapterIndex !== store.currentDict.chapterList.length - 1) {
store.currentDict.wordIndex = 0
store.currentDict.chapterIndex++
console.log('这一章节完了')
} else {
console.log('这本书完了')
return
}
} else {
store.currentDict.wordIndex++
// var msg = new SpeechSynthesisUtterance();
// // msg.text = store.word.name
// msg.text = 'Hawaii wildfires burn historic town of Lahaina to the ground'
// msg.rate = 0.8;
// msg.pitch = 1;
// msg.lang = 'en-US';
// window.speechSynthesis.speak(msg);
console.log('这个词完了')
}
if ([DictType.custom, DictType.inner].includes(store.currentDictType.name) && store.skipWordNames.includes(store.word.name)) {
next()
}
wrong = input = ''
}
function onKeyUp(e: KeyboardEvent) {
showFullWord = false
}
async function onKeyDown(e: KeyboardEvent) {
//TODO 还有横杠
if ((e.keyCode >= 65 && e.keyCode <= 90) || e.code === 'Space') {
let letter = e.key
if ((input + letter).toLowerCase() === store.word.name.toLowerCase().slice(0, input.length + 1)) {
input += letter
wrong = ''
playKeySound()
} else {
wrong = letter
playKeySound()
playBeep()
setTimeout(() => {
wrong = ''
// wrong = input = ''
}, 500)
}
if (input.toLowerCase() === store.word.name.toLowerCase()) {
playCorrect()
setTimeout(next, 300)
}
} else {
// console.log('e', e)
switch (e.key) {
case 'Backspace':
if (wrong) {
wrong = ''
} else {
input = input.slice(0, -1)
}
break
case keyMap.Collect:
if (!store.newWordDict.wordList.find((v: Word) => v.name === store.word.name)) {
store.newWordDict.wordList.push(store.word)
}
activeIndex = 1
break
case keyMap.Remove:
if (!store.skipWordNames.includes(store.word.name)) {
store.skipWordDict.wordList.push(store.word)
}
activeIndex = 0
next()
break
case keyMap.Ignore:
e.preventDefault()
activeIndex = 2
next()
break
case keyMap.Show:
showFullWord = true
break
}
setTimeout(() => {
activeIndex = -1
}, 200)
}
}
onMounted(() => {
})
const show = $ref(false)
const {appearance, toggle} = useThemeColor()
</script>
<template>
<!-- <Backgorund/>-->
<div class="main-page">
<div class="center">
<Toolbar/>
<div class="content">
<div class="type-word">
<div class="translate">{{ store.word.trans.join('') }}</div>
<div class="word-wrapper">
<div class="word" :class="wrong&&'is-wrong'">
<span class="input" v-if="input">{{ input }}</span>
<span class="wrong" v-if="wrong">{{ wrong }}</span>
<template v-if="isDictation">
<span class="letter" v-if="!showFullWord"
@mouseenter="showFullWord = true">{{ restWord.split('').map(v => '_').join('') }}</span>
<span class="letter" v-else @mouseleave="showFullWord = false">{{ restWord }}</span>
</template>
<span class="letter" v-else>{{ restWord }}</span>
</div>
<div class="audio" @click="playAudio(store.word.name)">播放</div>
</div>
<div class="phonetic">{{ store.word.usphone }}</div>
<div class="options">
<BaseButton keyboard="`" :active="activeIndex === 0">
忽略
</BaseButton>
<BaseButton keyboard="Enter" :active="activeIndex === 1">
收藏
</BaseButton>
<BaseButton keyboard="Tab" :active="activeIndex === 2">
下一个
</BaseButton>
</div>
</div>
</div>
<Side/>
</div>
<Statistics></Statistics>
<DictModal/>
</div>
</template>
<style scoped lang="scss">
@import "@/assets/css/colors.scss";
.main-page {
position: absolute;
z-index: 1;
width: 100vw;
height: 100%;
overflow: hidden;
font-size: 14rem;
display: flex;
justify-content: center;
background-color: var(--color-main-bg);
.center {
width: 70vw;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
//border: 1px solid gray;
position: relative;
.content {
position: relative;
flex: 1;
display: flex;
align-items: center;
justify-content: center;
.type-word {
display: flex;
//display: none;
align-items: center;
justify-content: center;
flex-direction: column;
font-size: 14rem;
color: gray;
gap: 2rem;
.options {
margin-top: 10rem;
display: flex;
gap: 15rem;
font-size: 18rem;
}
.phonetic, .translate {
font-size: 20rem;
}
.word-wrapper {
display: flex;
align-items: center;
gap: 10rem;
.word {
font-size: 48rem;
line-height: 1;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
letter-spacing: 5rem;
.input {
color: rgba(74, 222, 128, 0.8);
}
.wrong {
color: rgba(red, 0.6);
}
&.is-wrong {
animation: shake 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
}
}
}
}
}
}
}
@keyframes shake {
10%,
90% {
transform: translate3d(-1px, 0, 0);
}
20%,
80% {
transform: translate3d(2px, 0, 0);
}
30%,
50%,
70% {
transform: translate3d(-4px, 0, 0);
}
40%,
60% {
transform: translate3d(4px, 0, 0);
}
}
</style>