diff --git a/typing.css b/typing.css
index 17debf2..066e21a 100644
--- a/typing.css
+++ b/typing.css
@@ -185,7 +185,16 @@
display: inline-flex;
align-items: center;
justify-content: center;
+ flex-direction: column;
+ line-height: 1.05;
}
+.kb-key .kb-main { font-size: 1em; }
+.kb-key .kb-sub {
+ font-size: 0.62em;
+ opacity: 0.6;
+ margin-top: 1px;
+}
+.kb-key.hint .kb-sub { opacity: 0.9; }
.kb-key.wide { min-width: 64px; }
.kb-key.altgr { min-width: 64px; }
.kb-key.space { flex: 1; max-width: 220px; }
@@ -202,6 +211,26 @@
color: var(--text-muted);
line-height: 1.5;
}
+.keyboard-guide {
+ margin-top: 1rem;
+ padding: 0.9rem 1rem;
+ background: var(--highlight);
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ font-size: 0.86rem;
+ line-height: 1.7;
+ color: var(--text-muted);
+}
+.keyboard-guide h2 {
+ margin: 0 0 0.5rem;
+ font-size: 0.95rem;
+ color: var(--text);
+}
+.keyboard-guide ol {
+ margin: 0;
+ padding-left: 1.25rem;
+}
+.keyboard-guide li { margin-bottom: 0.25rem; }
.finish-box {
text-align: center;
padding: 2rem 0;
@@ -219,13 +248,19 @@
}
@media (max-width: 640px) {
.kb-key {
- min-width: 20px;
+ flex: 1 1 0;
+ min-width: 0;
height: 34px;
- font-size: 0.65rem;
- padding: 0 0.1rem;
+ font-size: 0.62rem;
+ padding: 0 0.05rem;
}
.kb-key.wide, .kb-key.altgr {
- min-width: 44px;
+ min-width: 40px;
}
+ .kb-key.space { min-width: 56px; max-width: 140px; }
.kb-row { gap: 0.25rem; }
+ .keyboard-guide {
+ font-size: 0.8rem;
+ padding: 0.75rem;
+ }
}
diff --git a/typing.html b/typing.html
index cc3909c..241da1b 100644
--- a/typing.html
+++ b/typing.html
@@ -54,6 +54,25 @@
+
+
西语键盘按键说明
+
+ - 字母 ñ:点击“;”键,即字母 L 右边第一个键。
+ - 字母 á é í ó ú:引号键 + a e i o u,引号键是字母 L 右边第二个键。
+ - 倒问号 ¿:Shift + 加号键。
+ - 问号 ?:Shift + 减号键。
+ - 倒感叹号 ¡:直接按加号键。
+ - 感叹号 !:Shift + 数字 1。
+ - 快速切换键盘:Shift + Alt。
+ - 横线 ——:原键盘上的问号键,单击即可。
+ - 括号 ():Shift + 数字 8、9。
+ - 引号 “”:Shift + 数字 2。
+ - 斜线 /:Shift + 数字 7。
+ - 分号 ;:Shift + 逗号键。
+ - 冒号 ::Shift + 句号键。
+ - ü:Shift + 引号键,松开后输入 u。
+
+
diff --git a/typing.js b/typing.js
index 54a3303..1ff4903 100644
--- a/typing.js
+++ b/typing.js
@@ -5,39 +5,105 @@ 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',';','\''],
- ['Shift','z','x','c','v','b','n','m',',','.','/','Shift'],
- ['Ctrl','Alt','Space','AltGr','Ctrl']
+ [
+ { key: 'º', sub: '`' },
+ { key: '1' }, { key: '2' }, { key: '3' }, { key: '4' }, { key: '5' },
+ { key: '6' }, { key: '7' }, { key: '8' }, { key: '9' }, { key: '0' },
+ { key: "'", sub: '-' }, { key: '¡', sub: '=' }
+ ],
+ [
+ { key: 'q' }, { key: 'w' }, { key: 'e' }, { key: 'r' }, { key: 't' },
+ { key: 'y' }, { key: 'u' }, { key: 'i' }, { key: 'o' }, { key: 'p' },
+ { key: '`', sub: '[' }, { key: '+', sub: ']' }, { key: 'ç', sub: '\\' }
+ ],
+ [
+ { key: 'a' }, { key: 's' }, { key: 'd' }, { key: 'f' }, { key: 'g' },
+ { key: 'h' }, { key: 'j' }, { key: 'k' }, { key: 'l' },
+ { key: 'ñ', sub: ';' }, { key: '´', sub: "'" }
+ ],
+ [
+ { key: 'Shift' }, { key: '<' }, { key: 'z' }, { key: 'x' }, { key: 'c' },
+ { key: 'v' }, { key: 'b' }, { key: 'n' }, { key: 'm' }, { key: ',' },
+ { key: '.' }, { key: '-', sub: '/' }, { key: 'Shift' }
+ ],
+ [
+ { key: 'Ctrl' }, { key: 'Win' }, { key: 'Alt' }, { key: 'Space' },
+ { key: 'AltGr' }, { key: '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',
- '¿': '西班牙语键盘:Shift + 加号键;美式国际键盘:AltGr + ?;macOS:Option + Shift + ?;数字键盘:Alt + 0191',
- '¡': '西班牙语键盘:直接按加号键;美式国际键盘:AltGr + 1;macOS:Option + 1;数字键盘:Alt + 0161',
- '?': '西班牙语键盘:Shift + 减号键;美式国际键盘:Shift + /',
+ 'á': '先按 L 右边的 ´ 键,再按 a',
+ 'é': '先按 L 右边的 ´ 键,再按 e',
+ 'í': '先按 L 右边的 ´ 键,再按 i',
+ 'ó': '先按 L 右边的 ´ 键,再按 o',
+ 'ú': '先按 L 右边的 ´ 键,再按 u',
+ 'Á': '先按 L 右边的 ´ 键,再按 Shift + A',
+ 'É': '先按 L 右边的 ´ 键,再按 Shift + E',
+ 'Í': '先按 L 右边的 ´ 键,再按 Shift + I',
+ 'Ó': '先按 L 右边的 ´ 键,再按 Shift + O',
+ 'Ú': '先按 L 右边的 ´ 键,再按 Shift + U',
+ 'ü': 'Shift + L 右边的 ´ 键,松开后按 u',
+ 'Ü': 'Shift + L 右边的 ´ 键,松开后按 Shift + U',
+ 'ñ': '直接按 L 右边的 ñ 键',
+ 'Ñ': 'Shift + L 右边的 ñ 键',
+ '¿': 'Shift + ¡ 键(标准键盘上是 Shift + 加号键)',
+ '¡': '直接按 ¡ 键(标准键盘上是加号键)',
+ '?': 'Shift + \' 键(标准键盘上是 Shift + 减号键)',
'!': 'Shift + 数字 1',
- ':': 'Shift + ;'
+ ':': 'Shift + 句号键',
+ ';': 'Shift + 逗号键'
};
-function standardKeyForChar(ch) {
- if (ch === ' ') return 'Space';
+function keyPressForChar(ch) {
+ if (!ch) return null;
+ if (ch === ' ') return { keys: ['Space'], shift: false };
var lower = ch.toLowerCase();
- if (/[a-z0-9]/.test(lower)) return lower;
+ if (/[a-z0-9]/.test(lower)) {
+ return { keys: [lower], shift: ch !== lower };
+ }
var map = {
- 'á':'a','é':'e','í':'i','ó':'o','ú':'u','ü':'u','ñ':'n',
- '?':'/','¿':'/','!':'1','¡':'1',
- ',':',','.':'.',';':';',':':';','-':'-','=':'=',
- '[':'[',']':']','\\':'\\',"'":"'",'\"':"'"
+ 'á': { keys: ['´'], shift: false },
+ 'é': { keys: ['´'], shift: false },
+ 'í': { keys: ['´'], shift: false },
+ 'ó': { keys: ['´'], shift: false },
+ 'ú': { keys: ['´'], shift: false },
+ 'Á': { keys: ['´'], shift: false },
+ 'É': { keys: ['´'], shift: false },
+ 'Í': { keys: ['´'], shift: false },
+ 'Ó': { keys: ['´'], shift: false },
+ 'Ú': { keys: ['´'], shift: false },
+ 'ü': { keys: ['´'], shift: true },
+ 'Ü': { keys: ['´'], shift: true },
+ 'ñ': { keys: ['ñ'], shift: false },
+ 'Ñ': { keys: ['ñ'], shift: true },
+ '¿': { keys: ['¡'], shift: true },
+ '¡': { keys: ['¡'], shift: false },
+ '?': { keys: ["'"], shift: true },
+ '!': { keys: ['1'], shift: true },
+ ':': { keys: ['.'], shift: true },
+ ';': { keys: [','], shift: true },
+ '/': { keys: ['7'], shift: true },
+ '"': { keys: ['2'], shift: true },
+ '(': { keys: ['8'], shift: true },
+ ')': { keys: ['9'], shift: true },
+ '_': { keys: ['-'], shift: true },
+ '-': { keys: ['-'], shift: false },
+ "'": { keys: ["'"], shift: false },
+ ',': { keys: [','], shift: false },
+ '.': { keys: ['.'], shift: false },
+ '<': { keys: ['<'], shift: false },
+ '>': { keys: ['<'], shift: true },
+ '`': { keys: ['`'], shift: false },
+ '^': { keys: ['`'], shift: true },
+ '+': { keys: ['+'], shift: false },
+ '*': { keys: ['+'], shift: true },
+ 'º': { keys: ['º'], shift: false },
+ 'ª': { keys: ['º'], shift: true },
+ 'ç': { keys: ['ç'], shift: false },
+ 'Ç': { keys: ['ç'], shift: true }
};
- return map[ch] || '';
+ return map[ch] || null;
}
function renderSpecialHint(ch) {
@@ -54,11 +120,16 @@ function renderSpecialHint(ch) {
el.textContent = '输入提示:按空格键';
return;
}
+ var press = keyPressForChar(ch);
+ if (press && press.shift) {
+ el.textContent = '输入提示:先按 Shift,再按 ' + press.keys.join(' + ') + ' 键';
+ return;
+ }
if (ch !== ch.toLowerCase() && ch === ch.toUpperCase() && /[a-z]/i.test(ch)) {
el.textContent = '输入提示:先按 Shift,再按 ' + ch.toLowerCase();
return;
}
- el.textContent = '输入提示:直接按标准键盘上的 ' + (standardKeyForChar(ch) || ch) + ' 键';
+ el.textContent = '输入提示:直接按 ' + (press ? press.keys.join(' + ') : ch) + ' 键';
}
function escTyping(s) {
@@ -159,22 +230,31 @@ 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 press = keyPressForChar(next);
+ var highlightKeys = {};
+ var highlightShift = false;
+ if (press) {
+ press.keys.forEach(function(key) {
+ highlightKeys[key.toLowerCase()] = true;
+ });
+ highlightShift = press.shift;
+ }
var container = document.getElementById('keyboard');
container.innerHTML = KEY_ROWS.map(function(row) {
- return '' + row.map(function(key) {
- var isHint = key.toLowerCase() === String(base || '').toLowerCase() ||
- (key === 'Shift' && (shiftNeeded || shiftPunctuation)) ||
- (key === 'AltGr' && specialNeeded);
+ return '
' + row.map(function(item) {
+ var key = item.key;
+ var sub = item.sub || '';
+ var isHint = highlightKeys[key.toLowerCase()] ||
+ (key === 'Shift' && highlightShift);
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 '
' + escTyping(key) + '
';
+ var face = sub
+ ? '
' + escTyping(key) + '' + escTyping(sub) + ''
+ : escTyping(key);
+ return '
' + face + '
';
}).join('') + '
';
}).join('');
renderSpecialHint(next);