style: 打字练习改为标准键盘提示并增加特殊字符输入说明
This commit is contained in:
+22
-5
@@ -170,6 +170,7 @@
|
||||
gap: 0.35rem;
|
||||
}
|
||||
.kb-key {
|
||||
flex: 0 0 auto;
|
||||
min-width: 34px;
|
||||
height: 40px;
|
||||
padding: 0 0.4rem;
|
||||
@@ -179,8 +180,14 @@
|
||||
color: var(--text);
|
||||
font-size: 0.9rem;
|
||||
font-family: var(--font-mono);
|
||||
cursor: pointer;
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.kb-key.wide { min-width: 64px; }
|
||||
.kb-key.altgr { min-width: 64px; }
|
||||
.kb-key.space { flex: 1; max-width: 220px; }
|
||||
.kb-key.hint {
|
||||
border-color: var(--accent);
|
||||
@@ -188,6 +195,13 @@
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
}
|
||||
#special-hint {
|
||||
min-height: 1.6rem;
|
||||
margin-top: 0.75rem;
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-muted);
|
||||
line-height: 1.5;
|
||||
}
|
||||
.finish-box {
|
||||
text-align: center;
|
||||
padding: 2rem 0;
|
||||
@@ -205,10 +219,13 @@
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.kb-key {
|
||||
min-width: 28px;
|
||||
height: 38px;
|
||||
font-size: 0.8rem;
|
||||
padding: 0 0.25rem;
|
||||
min-width: 20px;
|
||||
height: 34px;
|
||||
font-size: 0.65rem;
|
||||
padding: 0 0.1rem;
|
||||
}
|
||||
.kb-key.wide, .kb-key.altgr {
|
||||
min-width: 44px;
|
||||
}
|
||||
.kb-row { gap: 0.25rem; }
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
<input id="typing-input" type="text" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false" placeholder="">
|
||||
<div id="next-key"></div>
|
||||
<div id="keyboard"></div>
|
||||
<div id="special-hint"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -5,13 +5,62 @@ var typingStats = { correct: 0, errors: 0 };
|
||||
var typed = '';
|
||||
|
||||
var KEY_ROWS = [
|
||||
['1','2','3','4','5','6','7','8','9','0'],
|
||||
['q','w','e','r','t','y','u','i','o','p'],
|
||||
['a','s','d','f','g','h','j','k','l','ñ'],
|
||||
['z','x','c','v','b','n','m',',','.',';'],
|
||||
['á','é','í','ó','ú','ü','¿','¡',':','?','!',' ']
|
||||
['1','2','3','4','5','6','7','8','9','0','-','='],
|
||||
['q','w','e','r','t','y','u','i','o','p','[',']','\\'],
|
||||
['a','s','d','f','g','h','j','k','l',';','\''],
|
||||
['Shift','z','x','c','v','b','n','m',',','.','/','Shift'],
|
||||
['Ctrl','Alt','Space','AltGr','Ctrl']
|
||||
];
|
||||
|
||||
var SPECIAL_HINTS = {
|
||||
'á': 'Windows 美式国际:先按 \' 再按 a;macOS:Option + e 再按 a;手机长按 a',
|
||||
'é': 'Windows 美式国际:先按 \' 再按 e;macOS:Option + e 再按 e;手机长按 e',
|
||||
'í': 'Windows 美式国际:先按 \' 再按 i;macOS:Option + e 再按 i;手机长按 i',
|
||||
'ó': 'Windows 美式国际:先按 \' 再按 o;macOS:Option + e 再按 o;手机长按 o',
|
||||
'ú': 'Windows 美式国际:先按 \' 再按 u;macOS:Option + e 再按 u;手机长按 u',
|
||||
'ü': 'Windows 美式国际:先按 " 再按 u;macOS:Option + u 再按 u;手机长按 u',
|
||||
'ñ': 'Windows:AltGr + n 或先按 ~ 再按 n;macOS:Option + n 再按 n;手机长按 n',
|
||||
'¿': 'Windows:AltGr + ?;macOS:Option + Shift + ?;Linux:Compose ? ?',
|
||||
'¡': 'Windows:AltGr + 1;macOS:Option + 1;Linux:Compose ! !',
|
||||
'?': 'Shift + /',
|
||||
'!': 'Shift + 1',
|
||||
':': 'Shift + ;'
|
||||
};
|
||||
|
||||
function standardKeyForChar(ch) {
|
||||
if (ch === ' ') return 'Space';
|
||||
var lower = ch.toLowerCase();
|
||||
if (/[a-z0-9]/.test(lower)) return lower;
|
||||
var map = {
|
||||
'á':'a','é':'e','í':'i','ó':'o','ú':'u','ü':'u','ñ':'n',
|
||||
'?':'/','¿':'/','!':'1','¡':'1',
|
||||
',':',','.':'.',';':';',':':';','-':'-','=':'=',
|
||||
'[':'[',']':']','\\':'\\',"'":"'",'\"':"'"
|
||||
};
|
||||
return map[ch] || '';
|
||||
}
|
||||
|
||||
function renderSpecialHint(ch) {
|
||||
var el = document.getElementById('special-hint');
|
||||
if (!ch) {
|
||||
el.textContent = '';
|
||||
return;
|
||||
}
|
||||
if (SPECIAL_HINTS[ch]) {
|
||||
el.textContent = '输入提示:' + SPECIAL_HINTS[ch];
|
||||
return;
|
||||
}
|
||||
if (ch === ' ') {
|
||||
el.textContent = '输入提示:按空格键';
|
||||
return;
|
||||
}
|
||||
if (ch !== ch.toLowerCase() && ch === ch.toUpperCase() && /[a-z]/i.test(ch)) {
|
||||
el.textContent = '输入提示:先按 Shift,再按 ' + ch.toLowerCase();
|
||||
return;
|
||||
}
|
||||
el.textContent = '输入提示:直接按标准键盘上的 ' + (standardKeyForChar(ch) || ch) + ' 键';
|
||||
}
|
||||
|
||||
function escTyping(s) {
|
||||
var div = document.createElement('div');
|
||||
div.textContent = s == null ? '' : String(s);
|
||||
@@ -95,9 +144,10 @@ function renderPractice() {
|
||||
|
||||
function renderNextKey(target) {
|
||||
var next = target[typed.length];
|
||||
var label = next === ' ' ? '空格' : next;
|
||||
document.getElementById('next-key').innerHTML = typed.length >= target.length
|
||||
? '已完成'
|
||||
: '下一键:<b>' + escTyping(next) + '</b>';
|
||||
: '下一键:<b>' + escTyping(label) + '</b>';
|
||||
}
|
||||
|
||||
function renderTypingAccuracy() {
|
||||
@@ -109,18 +159,25 @@ function renderTypingAccuracy() {
|
||||
|
||||
function renderKeyboard(target) {
|
||||
var next = target[typed.length] || '';
|
||||
var base = standardKeyForChar(next);
|
||||
var shiftNeeded = next && next !== next.toLowerCase() && next === next.toUpperCase() && /[a-z]/i.test(next);
|
||||
var specialNeeded = next && /[áéíóúüñ¿¡]/.test(next);
|
||||
var shiftPunctuation = next === '?' || next === '!' || next === ':';
|
||||
var container = document.getElementById('keyboard');
|
||||
container.innerHTML = KEY_ROWS.map(function(row) {
|
||||
return '<div class="kb-row">' + row.map(function(key) {
|
||||
var label = key === ' ' ? '␣' : key;
|
||||
var isHint = next && (
|
||||
key === next ||
|
||||
key.toLowerCase() === next.toLowerCase() ||
|
||||
(next === ' ' && key === ' ')
|
||||
);
|
||||
return '<button type="button" class="kb-key' + (isHint ? ' hint' : '') + (key === ' ' ? ' space' : '') + '" data-key="' + escTyping(key) + '">' + escTyping(label) + '</button>';
|
||||
var isHint = key.toLowerCase() === String(base || '').toLowerCase() ||
|
||||
(key === 'Shift' && (shiftNeeded || shiftPunctuation)) ||
|
||||
(key === 'AltGr' && specialNeeded);
|
||||
var cls = ['kb-key'];
|
||||
if (isHint) cls.push('hint');
|
||||
if (key === 'Shift' || key === 'Ctrl' || key === 'Alt') cls.push('wide');
|
||||
if (key === 'AltGr') cls.push('altgr');
|
||||
if (key === 'Space') cls.push('space');
|
||||
return '<div class="' + cls.join(' ') + '">' + escTyping(key) + '</div>';
|
||||
}).join('') + '</div>';
|
||||
}).join('');
|
||||
renderSpecialHint(next);
|
||||
}
|
||||
|
||||
function handleTypingInput() {
|
||||
@@ -200,18 +257,5 @@ document.querySelectorAll('.mode-select button').forEach(function(btn) {
|
||||
document.getElementById('btn-start').addEventListener('click', startTyping);
|
||||
document.getElementById('btn-restart').addEventListener('click', startTyping);
|
||||
document.getElementById('typing-input').addEventListener('input', handleTypingInput);
|
||||
document.getElementById('keyboard').addEventListener('click', function(e) {
|
||||
var btn = e.target.closest('.kb-key');
|
||||
if (!btn) return;
|
||||
var input = document.getElementById('typing-input');
|
||||
var key = btn.dataset.key;
|
||||
var next = typingItems[typingIndex] && typingItems[typingIndex].es[typed.length];
|
||||
if (next && key.toLowerCase() === next.toLowerCase() && key !== next) {
|
||||
key = next;
|
||||
}
|
||||
input.value += key;
|
||||
handleTypingInput();
|
||||
input.focus();
|
||||
});
|
||||
|
||||
renderCategories();
|
||||
|
||||
Reference in New Issue
Block a user