|
205 | 205 | white-space: nowrap; |
206 | 206 | overflow-x: scroll; |
207 | 207 | -webkit-overflow-scrolling: touch; |
208 | | - min-height: 60px; /* 修正:增加最小高度 */ |
| 208 | + min-height: 48px; |
209 | 209 | background-color: #fff; |
210 | 210 | box-shadow: 0 1px 0 var(--border-color); |
211 | 211 | } |
|
221 | 221 | transition: all 0.2s; |
222 | 222 | user-select: none; |
223 | 223 | white-space: nowrap; |
224 | | - font-size: 1.4rem; /* 修正:加大字體 */ |
| 224 | + font-size: 1.2rem; |
225 | 225 | flex-shrink: 0; |
226 | 226 | text-align: center; |
227 | 227 | } |
|
869 | 869 |
|
870 | 870 | /** |
871 | 871 | * 統一的事件綁定函數 (同時支援 Touch 和 Mouse) |
| 872 | + * 【修正區域:優化反白邏輯】 |
872 | 873 | */ |
873 | 874 | function bindKeyEvents() { |
874 | 875 | document.querySelectorAll('.keyboard-key').forEach(keyElement => { |
| 876 | + |
| 877 | + // 1. 為了避免重複綁定,先移除可能已經存在的舊事件處理器 (通過儲存在元素上的屬性) |
| 878 | + if (keyElement.handlePressDown) { |
| 879 | + keyElement.removeEventListener('touchstart', keyElement.handlePressDown); |
| 880 | + keyElement.removeEventListener('touchend', keyElement.handlePressUp); |
| 881 | + keyElement.removeEventListener('touchmove', keyElement.handlePressCancel); |
| 882 | + keyElement.removeEventListener('touchcancel', keyElement.handlePressCancel); // 新增:確保移除 touchcancel |
| 883 | + keyElement.removeEventListener('mousedown', keyElement.handlePressDown); |
| 884 | + keyElement.removeEventListener('mouseup', keyElement.handlePressUp); |
| 885 | + keyElement.removeEventListener('mouseleave', keyElement.handlePressCancel); |
| 886 | + } |
| 887 | + |
| 888 | + // 清空舊的 onxxx 屬性 |
875 | 889 | keyElement.onclick = null; |
876 | 890 | keyElement.oncontextmenu = (e) => e.preventDefault(); |
877 | 891 | keyElement.ontouchstart = null; |
|
880 | 894 | keyElement.onmousedown = null; |
881 | 895 | keyElement.onmouseup = null; |
882 | 896 | keyElement.onmouseleave = null; |
883 | | - |
| 897 | + |
884 | 898 | const keyChar = keyElement.dataset.label; |
885 | | - const altChar = currentLayout.alt[keyChar]; |
| 899 | + // 修正 altChar 檢查,防止 currentLayout.alt 未定義時出錯 |
| 900 | + const altChar = currentLayout.alt ? currentLayout.alt[keyChar] : null; |
886 | 901 |
|
| 902 | + // 1. 處理按下 (Press Down) |
887 | 903 | const handlePressDown = (event) => { |
888 | | - event.preventDefault(); |
| 904 | + // 只處理左鍵點擊 (滑鼠) 或觸摸事件 |
| 905 | + if (event.type === 'mousedown' && event.button !== 0) return; |
| 906 | + if (event.type.startsWith('touch')) event.preventDefault(); |
| 907 | + |
889 | 908 | longPressCommitted = false; |
890 | | - keyElement.classList.add('highlighted'); |
| 909 | + keyElement.classList.add('highlighted'); // **添加高亮** |
891 | 910 |
|
892 | 911 | if (currentLayout === Keyboards.english && altChar) { |
893 | 912 | if (longPressTimer) clearTimeout(longPressTimer); |
|
896 | 915 | }, LONG_PRESS_TIMEOUT); |
897 | 916 | } |
898 | 917 | }; |
899 | | - |
| 918 | + |
| 919 | + // 2. 處理抬起/點擊 (Press Up) |
900 | 920 | const handlePressUp = (event) => { |
901 | | - event.preventDefault(); |
902 | | - keyElement.classList.remove('highlighted'); |
| 921 | + if (event.type.startsWith('touch')) event.preventDefault(); |
903 | 922 |
|
904 | 923 | if (longPressTimer) { |
905 | 924 | clearTimeout(longPressTimer); |
906 | 925 | longPressTimer = null; |
907 | 926 | } |
908 | 927 |
|
909 | | - if (!longPressCommitted) { |
| 928 | + // 只有當元素仍處於高亮狀態時才視為短按 |
| 929 | + if (!longPressCommitted && keyElement.classList.contains('highlighted')) { |
910 | 930 | handleShortPress(keyElement); |
911 | 931 | } |
| 932 | + |
| 933 | + // 無論如何,抬起時都移除高亮 |
| 934 | + keyElement.classList.remove('highlighted'); |
912 | 935 | longPressCommitted = false; |
913 | 936 | }; |
914 | 937 |
|
| 938 | + // 3. 處理取消 (Move/Leave/Cancel) |
915 | 939 | const handlePressCancel = () => { |
916 | 940 | if (longPressTimer) { |
917 | 941 | clearTimeout(longPressTimer); |
918 | 942 | longPressTimer = null; |
919 | 943 | } |
| 944 | + // 移動、離開或觸摸中斷時立即移除高亮 |
920 | 945 | keyElement.classList.remove('highlighted'); |
| 946 | + longPressCommitted = false; |
921 | 947 | }; |
922 | 948 |
|
| 949 | + // 將函數儲存在 DOM 元素上以便移除 |
| 950 | + keyElement.handlePressDown = handlePressDown; |
| 951 | + keyElement.handlePressUp = handlePressUp; |
| 952 | + keyElement.handlePressCancel = handlePressCancel; |
| 953 | + |
| 954 | + // Touch Events |
923 | 955 | keyElement.addEventListener('touchstart', handlePressDown); |
924 | 956 | keyElement.addEventListener('touchend', handlePressUp); |
925 | | - keyElement.addEventListener('touchmove', handlePressCancel); |
926 | | - |
927 | | - keyElement.addEventListener('mousedown', (e) => { |
928 | | - if (e.button === 0) { |
929 | | - handlePressDown(e); |
930 | | - } |
931 | | - }); |
| 957 | + keyElement.addEventListener('touchcancel', handlePressCancel); // 新增:處理觸摸中斷 |
| 958 | + keyElement.addEventListener('touchmove', handlePressCancel); // 處理手指移動取消 |
| 959 | + |
| 960 | + // Mouse Events (用於桌面測試) |
| 961 | + keyElement.addEventListener('mousedown', handlePressDown); |
932 | 962 | keyElement.addEventListener('mouseup', handlePressUp); |
933 | 963 | keyElement.addEventListener('mouseleave', handlePressCancel); |
934 | 964 | }); |
|
0 commit comments