|
39 | 39 | align-items: center; |
40 | 40 | background-color: var(--background-color); |
41 | 41 | min-height: 100vh; |
42 | | - /* 調整 padding-bottom 以容納多一行的數字鍵 */ |
| 42 | + /* 調整 padding-bottom 以容納鍵盤 */ |
43 | 43 | padding-bottom: 280px; |
44 | 44 | } |
45 | 45 |
|
|
204 | 204 | font-size: 1rem; |
205 | 205 | font-weight: normal; |
206 | 206 | } |
| 207 | + |
| 208 | + /* 調整功能鍵的寬度比例 */ |
| 209 | + .key[data-code="case_shift"], |
| 210 | + .key[data-code="lang_toggle"], |
| 211 | + .key[data-code="backspace"], |
| 212 | + .key[data-code="enter"] { |
| 213 | + flex-grow: 1.5; |
| 214 | + } |
207 | 215 |
|
208 | 216 | .key.space-key { |
209 | | - flex: 2; |
| 217 | + flex: 3.5; /* 讓空白鍵更寬 */ |
210 | 218 | background-color: #c5cae9; |
211 | 219 | } |
| 220 | + |
| 221 | + /* 調整 A-Row 和 Z-Row 的鍵寬,使其能夠容納更多或更少的鍵 */ |
| 222 | + .key-row:nth-child(3) .key, /* A-Row, 12 keys */ |
| 223 | + .key-row:nth-child(4) .key { /* Z-Row, 10 keys */ |
| 224 | + flex-grow: 1; |
| 225 | + flex-basis: auto; |
| 226 | + } |
212 | 227 |
|
213 | 228 | /* 按鍵互動效果 */ |
214 | 229 | .key:active { |
|
237 | 252 | </div> |
238 | 253 |
|
239 | 254 | <script> |
240 | | - // --- 資料處理與解析 --- |
| 255 | + // --- 狀態變數 --- |
241 | 256 | let currentCodemap = {}; |
242 | 257 | let currentReverseCodemap = {}; |
| 258 | + let isEnglishMode = false; |
| 259 | + let isShiftActive = false; // 控制大寫和符號切換的狀態 |
243 | 260 |
|
244 | | - // 中文模式下的鍵盤顯示 (Shift 鍵顯示為 '三碼') |
245 | | - const keyname_map_upper = { |
246 | | - 'a': 'A', 'b': 'B', 'c': 'C', 'd': 'D', 'e': 'E', 'f': 'F', 'g': 'G', |
247 | | - 'h': 'H', 'i': 'I', 'j': 'J', 'k': 'K', 'l': 'L', 'm': 'M', 'n': 'N', |
248 | | - 'o': 'O', 'p': 'P', 'q': 'Q', 'r': 'R', 's': 'S', 't': 'T', 'u': 'U', |
249 | | - 'v': 'V', 'w': 'W', 'x': 'X', 'y': 'Y', 'z': 'Z', |
250 | | - // 數字與符號對應 (按鍵顯示) |
251 | | - '1': '1', '2': '2', '3': '3', '4': '4', '5': '5', |
252 | | - '6': '6', '7': '7', '8': '8', '9': '9', '0': '0', |
253 | | - "'": "〃", ';': ';', ',': ',', '.': '.', '/': '/', ' ': '空白鍵', 'enter': 'Enter', 'backspace': '刪除鍵', |
254 | | - 'shift': '三碼', |
| 261 | + // --- 鍵盤顯示內容映射表 (四種狀態) --- |
| 262 | + |
| 263 | + // 字母鍵的基礎映射 |
| 264 | + const letter_maps = { |
| 265 | + 'cn_normal': (key) => key.toUpperCase(), // 中文模式顯示大寫字母 (A-Z) |
| 266 | + // 中文模式下,字母鍵的顯示不隨 Shift 改變,維持大寫 |
| 267 | + 'cn_shifted': (key) => key.toUpperCase(), |
| 268 | + 'en_normal': (key) => key.toLowerCase(), // 英文模式顯示小寫字母 (a-z) |
| 269 | + 'en_shifted': (key) => key.toUpperCase() // 英文 Shift 模式顯示大寫字母 (A-Z) |
255 | 270 | }; |
256 | | - // 英文模式下的鍵盤顯示 (Shift 鍵顯示為 'En') |
257 | | - const keyname_map_lower = { |
258 | | - 'a': 'a', 'b': 'b', 'c': 'c', 'd': 'd', 'e': 'e', 'f': 'f', 'g': 'g', |
259 | | - 'h': 'h', 'i': 'i', 'j': 'j', 'k': 'k', 'l': 'l', 'm': 'm', 'n': 'n', |
260 | | - 'o': 'o', 'p': 'p', 'q': 'q', 'r': 'r', 's': 's', 't': 't', 'u': 'u', |
261 | | - 'v': 'v', 'w': 'w', 'x': 'x', 'y': 'y', 'z': 'z', |
262 | | - // 數字與符號對應 |
263 | | - '1': '1', '2': '2', '3': '3', '4': '4', '5': '5', |
264 | | - '6': '6', '7': '7', '8': '8', '9': '9', '0': '0', |
265 | | - "'": "'", ';': ';', ',': ',', '.': '.', '/': '/', ' ': ' ', 'enter': 'Enter', 'backspace': '刪除鍵', |
266 | | - 'shift': 'En', |
| 271 | + |
| 272 | + const key_display_maps = { |
| 273 | + // [0, 0] Chinese Normal (Default) |
| 274 | + 'cn_normal': { |
| 275 | + // Numbers/Symbols: 中文輸入法的預設符號 (數字輸出,符號編碼) |
| 276 | + '1': '1', '2': '2', '3': '3', '4': '4', '5': '5', '6': '6', '7': '7', '8': '8', '9': '9', '0': '0', |
| 277 | + ';': ';', "'": '〃', ',': ',', '.': '.', '/': '/', |
| 278 | + 'lang_toggle': '三碼', // 顯示 '三碼' (當前模式) |
| 279 | + 'case_shift': '⇧', |
| 280 | + 'backspace': '⌫', // 顯示 '⌫' |
| 281 | + 'enter': '↵', // 顯示 '↵' |
| 282 | + ' ': ' ' // 修正: 顯示 ' ' (單一空白字元) |
| 283 | + }, |
| 284 | + // [0, 1] Chinese Shifted (與 cn_normal 相同,除了 case_shift 鍵本身) |
| 285 | + 'cn_shifted': { |
| 286 | + '1': '1', '2': '2', '3': '3', '4': '4', '5': '5', '6': '6', '7': '7', '8': '8', '9': '9', '0': '0', |
| 287 | + ';': ';', "'": '〃', ',': ',', '.': '.', '/': '/', |
| 288 | + 'lang_toggle': '三碼', |
| 289 | + 'case_shift': '⬆', // 只有 Shift 鍵的視覺改變 |
| 290 | + 'backspace': '⌫', |
| 291 | + 'enter': '↵', |
| 292 | + ' ': ' ' |
| 293 | + }, |
| 294 | + // [1, 0] English Normal (Lowercase) |
| 295 | + 'en_normal': { |
| 296 | + '1': '1', '2': '2', '3': '3', '4': '4', '5': '5', '6': '6', '7': '7', '8': '8', '9': '9', '0': '0', |
| 297 | + // 英文模式預設符號 |
| 298 | + ';': ';', "'": "'", ',': ',', '.': '.', '/': '/', |
| 299 | + 'lang_toggle': 'En', // 顯示 'En' (當前模式) |
| 300 | + 'case_shift': '⇧', |
| 301 | + 'backspace': '⌫', |
| 302 | + 'enter': '↵', |
| 303 | + ' ': ' ' |
| 304 | + }, |
| 305 | + // [1, 1] English Shifted (Uppercase + Secondary Symbols) |
| 306 | + 'en_shifted': { |
| 307 | + // Shifted Symbols (直接輸出) |
| 308 | + '1': '!', '2': '@', '3': '#', '4': '$', '5': '%', '6': '^', '7': '&', '8': '*', '9': '(', '0': ')', |
| 309 | + // 英文上檔符號 |
| 310 | + ';': ':', "'": '"', ',': '<', '.': '>', '/': '?', |
| 311 | + 'lang_toggle': 'En', |
| 312 | + 'case_shift': '⬆', |
| 313 | + 'backspace': '⌫', |
| 314 | + 'enter': '↵', |
| 315 | + ' ': ' ' |
| 316 | + } |
267 | 317 | }; |
268 | 318 |
|
| 319 | + // --- 輔助函數:取得實際輸出字元 --- |
| 320 | + |
| 321 | + function getCurrentDisplayMap() { |
| 322 | + let mode = isEnglishMode ? 'en' : 'cn'; |
| 323 | + let shift = isShiftActive ? 'shifted' : 'normal'; |
| 324 | + return key_display_maps[`${mode}_${shift}`]; |
| 325 | + } |
| 326 | + |
| 327 | + // 獲取當前模式下按下某個鍵時應輸出的實際字元(非編碼鍵) |
| 328 | + function getOutputChar(code) { |
| 329 | + const mapKey = isEnglishMode ? (isShiftActive ? 'en_shifted' : 'en_normal') : |
| 330 | + (isShiftActive ? 'cn_shifted' : 'cn_normal'); |
| 331 | + const map = key_display_maps[mapKey]; |
| 332 | + |
| 333 | + // 1. 檢查是否為英文模式下的直接輸出 (字母, 數字, 符號) |
| 334 | + if (isEnglishMode) { |
| 335 | + if (code.match(/^[a-z]$/)) { |
| 336 | + // 英文模式下,字母直接輸出大小寫 |
| 337 | + return letter_maps[mapKey](code); |
| 338 | + } |
| 339 | + if (code.match(/^[0-9;'./,<> ]$/)) { // 包含 space |
| 340 | + // 英文模式下,數字和符號直接輸出半形/上檔符號 |
| 341 | + return map[code] || code; // map[code] 對於數字和符號會輸出正確的上檔符號或數字,對於 ' ' 則輸出 ' ' |
| 342 | + } |
| 343 | + return null; |
| 344 | + } |
| 345 | + |
| 346 | + // 2. 檢查是否為中文模式下的直接輸出 (無,中文模式下的數字輸出在 handleChineseInput 中處理) |
| 347 | + return null; |
| 348 | + } |
| 349 | + |
| 350 | + // --- 舊有代碼區塊 --- |
| 351 | + |
269 | 352 | // DOM 元素 |
270 | 353 | const textOutput = document.getElementById('text-output'); |
271 | 354 | const mainInput = document.getElementById('main-input'); |
|
277 | 360 | const MAX_CODE_LENGTH = 3; |
278 | 361 | let currentCode = ''; |
279 | 362 | let currentCandidates = []; |
280 | | - let isEnglishMode = false; |
281 | | - |
| 363 | + |
282 | 364 | function parseCinData(data) { |
283 | 365 | const newCodemap = {}; |
284 | 366 | const newReverseCodemap = {}; |
|
305 | 387 | } |
306 | 388 |
|
307 | 389 | async function loadSecondaryCodemap() { |
| 390 | + // 注意:這裡假設您的編碼檔名為 3codes2.txt 並且在同目錄 |
308 | 391 | const fileName = './3codes2.txt'; |
309 | 392 | try { |
310 | 393 | const response = await fetch(fileName); |
|
332 | 415 | if (currentCandidates.length > 0) { |
333 | 416 | selectCandidate(0); // 如果有編碼,先上字 |
334 | 417 | } |
335 | | - textOutput.textContent += inputChar; // 直接輸出數字 |
| 418 | + // 中文模式的數字直接輸出數字 |
| 419 | + textOutput.textContent += inputChar; |
336 | 420 | return; |
337 | 421 | } |
338 | 422 |
|
| 423 | + // 2. 處理功能鍵 (與 Shift 狀態無關) |
339 | 424 | if (code === 'backspace') { |
340 | 425 | if (currentCode.length > 0) { |
341 | 426 | currentCode = currentCode.slice(0, -1); |
|
358 | 443 | if (currentCandidates.length > 0) { |
359 | 444 | selectCandidate(0); |
360 | 445 | } else { |
361 | | - textOutput.textContent += ' '; |
| 446 | + textOutput.textContent += ' '; // 輸出全形空白 |
362 | 447 | } |
363 | 448 | return; |
364 | 449 | } |
365 | 450 |
|
366 | | - // 2. 字母 (a-z) 和符號 (;'./,) 用於編碼 |
| 451 | + // 3. 字母 (a-z) 和編碼符號 (;'./,) 用於編碼 |
367 | 452 | if (inputChar.match(/^[a-z;'./,<>]$/)) { |
368 | 453 | if (currentCode.length === MAX_CODE_LENGTH) { |
369 | 454 | if (currentCandidates.length > 0) { |
|
378 | 463 | } |
379 | 464 |
|
380 | 465 | // --- 處理英文模式輸入 --- |
381 | | - function handleEnglishInput(char) { |
382 | | - // 處理功能鍵 |
383 | | - if (char === 'backspace') { |
| 466 | + function handleEnglishInput(code) { |
| 467 | + // 1. 處理功能鍵 |
| 468 | + if (code === 'backspace') { |
384 | 469 | if (textOutput.textContent.length > 0) { |
385 | 470 | textOutput.textContent = textOutput.textContent.slice(0, -1); |
386 | 471 | } |
387 | 472 | return; |
388 | 473 | } |
389 | | - if (char === 'enter') { |
| 474 | + if (code === 'enter') { |
390 | 475 | textOutput.textContent += '\n'; |
391 | 476 | return; |
392 | 477 | } |
393 | | - if (char === ' ') { |
394 | | - textOutput.textContent += ' '; |
| 478 | + if (code === ' ') { |
| 479 | + textOutput.textContent += ' '; // 輸出半形空白 |
395 | 480 | return; |
396 | 481 | } |
397 | | - |
398 | | - // 處理英文字母、數字和符號 |
399 | | - if (char.length === 1) { |
400 | | - textOutput.textContent += char; |
| 482 | + |
| 483 | + // 2. 處理英文字母、數字和符號 |
| 484 | + const outputChar = getOutputChar(code); |
| 485 | + if (outputChar) { |
| 486 | + textOutput.textContent += outputChar; |
401 | 487 | } |
402 | 488 | } |
403 | 489 |
|
404 | 490 | // --- 核心狀態更新與顯示函數 (中文模式專用) --- |
405 | 491 | function updateStateAndDisplay() { |
406 | | - mainInput.textContent = formatCode(currentCode); |
| 492 | + mainInput.textContent = formatCode(currentCode); |
407 | 493 | currentCandidates = currentCodemap[currentCode] || []; |
408 | 494 |
|
409 | 495 | if (currentCode.length === MAX_CODE_LENGTH) { |
|
455 | 541 | function resetInputState() { |
456 | 542 | currentCode = ''; |
457 | 543 | currentCandidates = []; |
| 544 | + |
| 545 | + // 更新輸入提示文字 |
458 | 546 | mainInput.textContent = isEnglishMode ? '英文模式' : '編碼顯示在此'; |
459 | 547 | showCandidates(); |
460 | 548 | } |
461 | 549 |
|
462 | 550 | // --- 格式化編碼顯示 --- |
463 | 551 | function formatCode(code) { |
464 | 552 | let displayString = ''; |
| 553 | + const map = key_display_maps.cn_normal; |
465 | 554 | for (const char of code) { |
466 | | - displayString += keyname_map_upper[char] || char.toUpperCase(); |
| 555 | + displayString += (char.match(/^[a-z]$/) ? char.toUpperCase() : map[char]) || char.toUpperCase(); |
467 | 556 | } |
468 | 557 | return displayString; |
469 | 558 | } |
470 | 559 |
|
471 | 560 | // --- 更新鍵盤按鍵顯示 --- |
472 | 561 | function updateKeyboardDisplay() { |
473 | | - const map = isEnglishMode ? keyname_map_lower : keyname_map_upper; |
| 562 | + const map = getCurrentDisplayMap(); |
| 563 | + // 中文模式下,字母鍵的視覺始終是 letter_maps.cn_normal (大寫 A-Z) |
| 564 | + const letterMap = isEnglishMode ? (isShiftActive ? letter_maps.en_shifted : letter_maps.en_normal) : letter_maps.cn_normal; |
| 565 | + |
474 | 566 | document.querySelectorAll('.key[data-code]').forEach(keyElement => { |
475 | 567 | const code = keyElement.dataset.code; |
| 568 | + |
476 | 569 | if (map[code]) { |
| 570 | + // 處理非字母鍵和特殊功能鍵 (包括 ' ') |
477 | 571 | keyElement.textContent = map[code]; |
| 572 | + } else if (code.match(/^[a-z]$/)) { |
| 573 | + // 處理字母鍵 (a-z) |
| 574 | + keyElement.textContent = letterMap(code); |
478 | 575 | } |
479 | 576 | }); |
480 | | - |
481 | | - const shiftKey = document.querySelector('.key[data-code="shift"]'); |
482 | | - if (shiftKey) { |
483 | | - shiftKey.textContent = isEnglishMode ? keyname_map_lower['shift'] : keyname_map_upper['shift']; |
484 | | - } |
485 | 577 | } |
486 | 578 |
|
487 | | - // --- 動態生成手機優化鍵盤 (新增數字行) --- |
| 579 | + // --- 動態生成手機優化鍵盤 (符號鍵位置修正) --- |
488 | 580 | function generateMobileKeyboard() { |
489 | 581 | const layout_mobile = [ |
490 | | - ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'], // 新增數字行 |
| 582 | + ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'], |
491 | 583 | ['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'], |
492 | | - ['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ';'], |
493 | | - ['Z', 'X', 'C', 'V', 'B', 'N', 'M', ',', '.', '/', "'"], |
494 | | - ['Shift', 'Space', 'Back', 'Enter'] |
| 584 | + ['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', "'"], |
| 585 | + ['Z', 'X', 'C', 'V', 'B', 'N', 'M', ',', '.', '/'], |
| 586 | + ['Case_Shift', 'Lang_Toggle', 'Space', 'Back', 'Enter'] |
495 | 587 | ]; |
496 | 588 |
|
497 | 589 | keyboardContainer.innerHTML = ''; |
|
506 | 598 |
|
507 | 599 | let code = keyText.toLowerCase(); |
508 | 600 |
|
509 | | - // 設定 data-code |
| 601 | + // 設定 data-code 和樣式 |
510 | 602 | if (keyText === 'Back') { |
511 | 603 | code = 'backspace'; |
512 | 604 | key.classList.add('function-key'); |
|
516 | 608 | } else if (keyText === 'Space') { |
517 | 609 | code = ' '; |
518 | 610 | key.classList.add('space-key'); |
519 | | - } else if (keyText === 'Shift') { |
520 | | - code = 'shift'; |
| 611 | + } else if (keyText === 'Lang_Toggle') { |
| 612 | + code = 'lang_toggle'; |
| 613 | + key.classList.add('function-key'); |
| 614 | + } else if (keyText === 'Case_Shift') { |
| 615 | + code = 'case_shift'; |
521 | 616 | key.classList.add('function-key'); |
522 | 617 | } |
523 | 618 |
|
524 | 619 | key.dataset.code = code; |
525 | | - key.textContent = keyname_map_upper[code] || keyText; |
| 620 | + |
| 621 | + // 初始顯示: 根據初始模式 (中文, normal) 顯示正確的圖示或文字 |
| 622 | + const initialMap = getCurrentDisplayMap(); |
| 623 | + if (initialMap[code]) { |
| 624 | + key.textContent = initialMap[code]; |
| 625 | + } else if (code.match(/^[a-z]$/)) { |
| 626 | + key.textContent = letter_maps.cn_normal(code); |
| 627 | + } else { |
| 628 | + key.textContent = keyText; // 保險措施 |
| 629 | + } |
526 | 630 |
|
| 631 | + // --- 鍵盤點擊事件 --- |
527 | 632 | key.addEventListener('click', () => { |
528 | | - if (code === 'shift') { |
| 633 | + if (code === 'lang_toggle') { |
529 | 634 | isEnglishMode = !isEnglishMode; |
| 635 | + isShiftActive = false; |
| 636 | + updateKeyboardDisplay(); |
| 637 | + resetInputState(); // 模式切換後重設輸入狀態並更新提示文字 |
| 638 | + } else if (code === 'case_shift') { |
| 639 | + isShiftActive = !isShiftActive; |
530 | 640 | updateKeyboardDisplay(); |
531 | | - resetInputState(); |
532 | 641 | } else if (isEnglishMode) { |
533 | 642 | handleEnglishInput(code); |
534 | 643 | } else { |
|
544 | 653 | } |
545 | 654 |
|
546 | 655 | /** |
547 | | - * 新增功能:複製文字 |
| 656 | + * 複製文字 |
548 | 657 | */ |
549 | 658 | function copyTextToClipboard() { |
550 | 659 | const textToCopy = textOutput.textContent; |
|
576 | 685 | } |
577 | 686 |
|
578 | 687 | /** |
579 | | - * 新增功能:清空文字 |
| 688 | + * 清空文字 |
580 | 689 | */ |
581 | 690 | function clearOutput() { |
582 | 691 | if (confirm('確定要清空所有已輸入的文字嗎?')) { |
|
0 commit comments