Set activeKey by click
Some people don't have a physical keyboard, but only a screen one. And they also want to learn letters with our cool app.
To add @click event to Key we need first to encapsulate key activation into a method.
Method
Add a new method to Keyboard.js.
Keyboard.js methods:
setActiveKey(keyContent) {
this.activeKey = keyContent
clearTimeout(this.timeout)
this.timeout = setTimeout(() => (this.activeKey = { code: '' }), 1000)
}
And call this new method from mounted()
Keyboard.js
mounted() {
this.getKeyboardData(this.currentLang)
window.addEventListener('keydown', event => {
event.preventDefault()
const { code, key, shiftKey } = event
this.setActiveKey({ code, key, shiftKey })
})
},
Event
Send a new method setActiveKey from the Keyboard to the Key:
Keyboard.js template:
<vue-keyboard ... :setActiveKey="setActiveKey" />
In Key.js receive this method (add it to props), and add its call to the template.
Key.js props:
props: {
...,
setActiveKey: Function
}
Key.js template
<div
:class="['key', {active: activeKey.code === keyContent.code}]"
@click="setActiveKey(keyContent)"
>
<div class="main">{{main}}</div>
<div class="shifted">{{shifted}}</div>
</div>
Now we see, that key became active also by mouse click (or tap from phone).
The key full info
On the previous gif animation you can notice, that activeKey is different for keydown and @click. E.g. for q:
activeKey on keydown: { code: KeyQ, shiftKey: false }
activeKey on @click: { code: KeyQ, main: "q", shifted:"Q" }
That's because on keydown we assign to the activeKey an object {code, shiftKey} that we get from keyboard event.
And on @click we set activeKey from our data keyboardData/en.js -- which we filled with useful data before.
It is easy to get this data by @click in Key component -- because it is a prop keyContent. But keydown event doesn't contain these data.
We should add some code to extract keyContent from keyboardData by keydown event code.
Keyboard.js mounted
...
window.addEventListener('keydown', event => {
event.preventDefault()
const { code, shiftKey } = event
const keyContent = this.keyboardData
.flat()
.find(elem => elem.code === code)
this.setActiveKey(keyContent)
})
...
keyboardData is 2D array (array with arrays). So we did it flat -- 1D, and find full key info by code. Then pass it to the method setActiveKey.
You can test it out: keydown and @click now returns almost the same value.
Move shiftKey to state
For now, we get shiftKey only with keydown, can't get shiftKey on @click. To make keydown and @click events equivalent, let's create a new keyboard state: shiftKey. So we'll have the ability to manipulate shiftKey not only from keyboard keydown, but also from mouse/tap screen events.
Add to the end of each keyboardData/lang.js a new row with 2 buttons:
en.js, ru.js, ar.js
...,
[
{
code: 'ShiftLeft',
label: 'Shift'
},
{
code: 'ShiftRight',
label: 'Shift'
}
]
Result
Add a new state to
Keyboard.js data()
{
...
shiftKey: false
}
Add 2 keyboard event listeners, that change the new app state
Keyboard.js mounted()
...
window.addEventListener('keydown', event => {
if (event.key === 'Shift') {
this.shiftKey = true
}
})
window.addEventListener('keyup', event => {
if (event.key === 'Shift') {
this.shiftKey = false
}
})
Add shiftKey state to template, to test how it works
Keyboard.js template
...
<div>shiftKey: {{shiftKey}}</div>
...
Result
When we hold shift on keyboard, state shiftKey is true even when activeKey faded. When we @click shift by mouse, shiftKey is false, even when shift is the activeKey.
We can't hold shift on the screen as on physical keyboard. So we need to set shiftKey by click on the screen button, and the app will think that we hold shift key. On the second click on shift the app will think, that we released the button.
For that, in Keyboard.js add a new method toggleShiftKey and pass it down to Key
Keyboard.js methods:
toggleShiftKey(){
this.shiftKey = !this.shiftKey
}
Keyboard.js template
<vue-key ... :toggleShiftKey="toggleShiftKey" />
Key.js props
{
...
toggleShiftKey: Function
}
Now @click should call multiple methods, not only one @click="setActiveKey(keyContent)" as before. So we need to create an additional method calling all these methods. And call it from the template @click.
Key.js methods
methods: {
keyClick(keyContent) {
this.setActiveKey(keyContent)
if (keyContent.code.includes('Shift')) {
this.toggleShiftKey()
}
}
}
In Key.js template replace @click="setActiveKey(keyContent)" with @click="keyClick(keyContent)"
Key.js template
<div
:class="['key', {active: activeKey.code === keyContent.code}]"
@click="keyClick(keyContent)"
>
<div class="main">{{main}}</div>
<div class="shifted">{{shifted}}</div>
</div>
Result
shiftKey state works fine with keydown and @click. But we don't see that shift is holding on the keyboard.
Holding shift style
Add to styles.css an especial style for pressed (not released) shift buttons.
styles.css
.key.shiftKeyPressed {
color: red;
font-weight: bold;
}
To do that we need in the Key component the prop shiftKey. Pass it from Keyboard to Key
Keyboard.js template
<vue-key ... :shiftKey="shiftKey" />
Key.js props
props: {
...
shiftKey: Boolean,
},
Add to computed 2 methods:
Key.js computed
isActive() {
return this.activeKey.code === this.keyContent.code
},
isShift() {
return this.keyContent.code.includes('Shift')
}
Then use these new computed values in a template:
Key.js template
<div
:class="[
'key',
keyContent.code,
{ active: isActive },
{ shiftKeyPressed: isShift && shiftKey && !isActive }
]"
@click="keyClick(keyContent)"
></div>
Style shiftKeyPressed will be applied to key only if:
- it is a
shiftkey (with code: ShiftLeft or ShiftRight), - keyboard state
shiftKeyistrue-- the key is holding, - key is not active (we can't see red text on red background)
Result





Top comments (0)