Lashtw commited on
Commit
2fa8505
·
verified ·
1 Parent(s): 88e3de0

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +88 -80
index.html CHANGED
@@ -798,112 +798,113 @@
798
 
799
  // [新增] 初始化手寫板功能 (修復無反應問題)
800
  function initHandwritingBoard() {
801
- if (handwritingCanvasObj) return; // 避免重複初始化
802
-
803
  // 建立 handwriting.js 實例 (參數: canvas元素, 筆畫粗細)
804
  const canvasEl = document.getElementById('handwriting-canvas');
 
805
  // 確保 canvas 寬度正確 (解決有些手機上寬度不對的問題)
806
  const rect = canvasEl.parentElement.getBoundingClientRect();
 
 
 
 
 
807
  canvasEl.width = rect.width || 300;
808
- canvasEl.height = 250; // 明確設置高度,避免座標對應錯誤
809
 
810
- // [重要修復] 強制註冊 passive: false 的事件監聽器
811
- // 這是為了解決在 iOS Safari / 新版 Chrome 上,手指滑動會觸發頁面捲動而非畫線的問題
812
- // 雖然 CSS 設了 touch-action: none,但在某些舊版 library 實作上仍需此修正
813
  const preventDefaultTouch = (e) => {
814
  if (e.cancelable) e.preventDefault();
815
  };
816
  canvasEl.addEventListener('touchstart', preventDefaultTouch, { passive: false });
817
  canvasEl.addEventListener('touchmove', preventDefaultTouch, { passive: false });
818
 
819
- handwritingCanvasObj = new handwriting.Canvas(canvasEl, 3);
820
-
821
- // 設定辨識後的回呼函式
822
- handwritingCanvasObj.setCallBack(function(data, err) {
823
- if (err) {
824
- console.error(err);
825
- alert("辨識失敗,請檢查網路連線"); // handwriting.js 需要連網
826
- return;
827
- }
828
- // data 是候選字陣列,取第一個
829
- if (data && data.length > 0) {
830
- const result = data[0];
831
- answerInput.value = result; // 填入答案框
832
-
833
- // 視覺回饋
834
- answerInput.classList.add('border-green-500', 'bg-green-50');
835
- setTimeout(() => answerInput.classList.remove('border-green-500', 'bg-green-50'), 500);
836
- }
837
- });
 
 
838
 
839
- // 設定筆畫樣式
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
840
  handwritingCanvasObj.setLineWidth(5);
841
  handwritingCanvasObj.setPenColor("#333");
842
-
843
- // 讓 undo/redo 功能生效
844
  handwritingCanvasObj.setOptions({
845
  language: "en",
846
  numOfReturn: 1
847
  });
848
-
849
- // 綁定按鈕事件
850
- document.getElementById('hw-clear-btn').addEventListener('click', () => {
851
- handwritingCanvasObj.erase();
852
- answerInput.value = '';
853
- answerInput.focus(); // 保持焦點
854
- });
855
-
856
- document.getElementById('hw-undo-btn').addEventListener('click', () => {
857
- handwritingCanvasObj.undo();
858
- });
859
-
860
- document.getElementById('hw-recognize-btn').addEventListener('click', () => {
861
- handwritingCanvasObj.recognize();
862
- });
863
  }
864
 
865
- // [新增] 應用版本設定 (控制手寫板顯示)
866
- function applyVersionSettings() {
 
867
  const hwContainer = document.getElementById('handwriting-container');
868
 
869
- // 如果手寫容器還沒生成(避免報錯),先跳過
870
- if (!hwContainer) return;
871
 
872
- if (effectiveVersion === 'mobile') {
873
- // --- 行動版模式 (手寫) ---
874
- console.log("切換為手寫模式");
875
-
876
- // 1. 顯示手寫板
877
  hwContainer.classList.remove('hidden');
878
  hwContainer.classList.add('flex');
879
 
880
- // 2. 初始化手寫板 (如果還沒建立,且元素可見時才初始化以取得正確寬度)
881
- // 使用 setTimeout 確保 display:flex 生效後才抓得到寬度
882
- setTimeout(() => initHandwritingBoard(), 100);
883
 
884
- // 3. 鎖定輸入框
885
  answerInput.readOnly = true;
886
- answerInput.placeholder = "請在下方手寫板書寫...";
887
- answerInput.classList.add('handwriting-mode'); // 套用我們剛寫的 CSS
888
 
889
- // 4. 移除之前可能加上的原生輸入法設定
890
  if (window.google && window.google.ime) {
891
  window.google.ime.setOptions({ ime: 'none', trigger: null });
892
  }
893
 
894
  } else {
895
- // --- PC 版模式 (打字) ---
896
- console.log("切換為打字模式");
897
-
898
  // 1. 隱藏手寫板
899
  hwContainer.classList.add('hidden');
900
  hwContainer.classList.remove('flex');
901
 
902
  // 2. 恢復輸入框
903
  answerInput.readOnly = false;
904
- answerInput.placeholder = "輸入你的答案 (按 Enter 提交)";
905
  answerInput.classList.remove('handwriting-mode');
906
  }
 
 
907
  }
908
 
909
  // --- 視圖管理 ---
@@ -1096,31 +1097,29 @@
1096
  frontDisplay.classList.add('hidden');
1097
  clozeQuestionContainer.classList.add('hidden');
1098
 
1099
- // [新增] 每次顯示卡片時,呼叫設定確保介面正確
1100
- applyVersionSettings();
1101
-
1102
  let frontText, backText;
1103
-
 
1104
  if (isSpeed) {
1105
- // 極速挑戰暫不支援克漏字
1106
  switch (currentSpeedQuestionType) {
1107
  case 'zh-en':
1108
  frontText = card.chinese;
1109
  backText = card.english;
1110
- answerInput.placeholder = effectiveVersion === 'mobile' ? "請在下方手寫板書寫..." : "請輸入英文答案...";
1111
  break;
1112
  case 'en-zh':
1113
  frontText = card.english;
1114
  backText = card.chinese;
1115
  [speakBtn, speakSlowBtn].forEach(btn => btn.classList.remove('hidden'));
1116
- answerInput.placeholder = "請輸入中文答案...";
1117
  break;
1118
  case 'listen':
1119
  frontText = '請聽發音';
1120
  backText = card.english;
1121
  [speakBtn, speakSlowBtn].forEach(btn => btn.classList.remove('hidden'));
1122
  speakWord(card);
1123
- answerInput.placeholder = effectiveVersion === 'mobile' ? "請在下方手寫板書寫..." : "請輸入英文答案...";
1124
  break;
1125
  }
1126
  } else {
@@ -1132,22 +1131,24 @@
1132
  progressDisplay.textContent = `第 ${currentCardIndex + 1}/${wordsForCurrentMode.length} 張`;
1133
  prevBtn.disabled = currentCardIndex === 0;
1134
  nextBtn.disabled = currentCardIndex === wordsForCurrentMode.length - 1;
1135
- answerInput.placeholder = "請輸入中文答案...";
 
 
1136
  break;
1137
  case 'zh-en': case 'hard':
1138
  frontText = card.chinese; backText = card.english;
1139
- answerInput.placeholder = effectiveVersion === 'mobile' ? "請在下方手寫板書寫..." : "請輸入英文答案...";
1140
  break;
1141
  case 'en-zh':
1142
  frontText = card.english; backText = card.chinese;
1143
  [speakBtn, speakSlowBtn].forEach(btn => btn.classList.remove('hidden'));
1144
- answerInput.placeholder = "請輸入中文答案...";
1145
  break;
1146
  case 'listen':
1147
  frontText = '請聽發音'; backText = card.english;
1148
  [speakBtn, speakSlowBtn].forEach(btn => btn.classList.remove('hidden'));
1149
  speakWord(card);
1150
- answerInput.placeholder = effectiveVersion === 'mobile' ? "請在下方手寫板書寫..." : "請輸入英文答案...";
1151
  break;
1152
  case 'sentence-cloze':
1153
  clozeQuestionContainer.classList.remove('hidden');
@@ -1155,11 +1156,15 @@
1155
  sentenceZhDisplay.textContent = `(${card.sentence.zh})`;
1156
  sentenceZhDisplay.classList.add('hidden');
1157
  toggleTranslationBtn.textContent = '顯示翻譯';
1158
- backText = card.sentence.answer || card.english; // 【修改】背面顯示正確答案
1159
- answerInput.placeholder = effectiveVersion === 'mobile' ? "請填入空格中的單字..." : "請填入空格中的單字...";
1160
  break;
1161
  }
1162
  }
 
 
 
 
1163
  if (frontText) {
1164
  frontDisplay.classList.remove('hidden');
1165
  frontDisplay.textContent = frontText;
@@ -1911,8 +1916,11 @@
1911
  if (currentMode === 'review') {
1912
  flashcardContainer.classList.toggle('flipped');
1913
  const isFlipped = flashcardContainer.classList.contains('flipped');
1914
- answerInput.placeholder = isFlipped ? "請輸入英文答案..." : "請輸入中文答案...";
1915
- answerInput.value = '';
 
 
 
1916
  feedbackDisplay.textContent = '';
1917
  setTimeout(() => answerInput.focus(), 100);
1918
  }
@@ -1974,7 +1982,7 @@
1974
  passwordModal.classList.add('hidden');
1975
  renderWordList();
1976
  // 在進入管理畫面時也更新一下設定狀態
1977
- applyVersionSettings();
1978
  showView('manage');
1979
  } else {
1980
  passwordError.textContent = '密碼錯誤!';
 
798
 
799
  // [新增] 初始化手寫板功能 (修復無反應問題)
800
  function initHandwritingBoard() {
 
 
801
  // 建立 handwriting.js 實例 (參數: canvas元素, 筆畫粗細)
802
  const canvasEl = document.getElementById('handwriting-canvas');
803
+
804
  // 確保 canvas 寬度正確 (解決有些手機上寬度不對的問題)
805
  const rect = canvasEl.parentElement.getBoundingClientRect();
806
+
807
+ // [關鍵修復]:如果寬度是 0 (表示元素隱藏中),則不進行初始化,避免狀態錯誤
808
+ if (rect.width === 0) return;
809
+
810
+ // 設定 canvas 屬性寬高 (這是繪圖解析度)
811
  canvasEl.width = rect.width || 300;
812
+ canvasEl.height = 250;
813
 
814
+ // [重要修復] 強制註冊 passive: false 的事件監聽器,防止畫面捲動
 
 
815
  const preventDefaultTouch = (e) => {
816
  if (e.cancelable) e.preventDefault();
817
  };
818
  canvasEl.addEventListener('touchstart', preventDefaultTouch, { passive: false });
819
  canvasEl.addEventListener('touchmove', preventDefaultTouch, { passive: false });
820
 
821
+ // 如果物件已經存在,不需要重新 new,但需要確保屬性設定正確
822
+ if (!handwritingCanvasObj) {
823
+ handwritingCanvasObj = new handwriting.Canvas(canvasEl, 3);
824
+
825
+ // 設定辨識後的回呼函式
826
+ handwritingCanvasObj.setCallBack(function(data, err) {
827
+ if (err) {
828
+ console.error(err);
829
+ alert("辨識失敗,請檢查網路連線"); // handwriting.js 需要連網
830
+ return;
831
+ }
832
+ // data 是候選字陣列,取第一個
833
+ if (data && data.length > 0) {
834
+ const result = data[0];
835
+ answerInput.value = result; // 填入答案框
836
+
837
+ // 視覺回饋
838
+ answerInput.classList.add('border-green-500', 'bg-green-50');
839
+ setTimeout(() => answerInput.classList.remove('border-green-500', 'bg-green-50'), 500);
840
+ }
841
+ });
842
 
843
+ // 綁定按鈕事件 (只需綁定一次)
844
+ document.getElementById('hw-clear-btn').addEventListener('click', () => {
845
+ handwritingCanvasObj.erase();
846
+ answerInput.value = '';
847
+ answerInput.focus(); // 保持焦點
848
+ });
849
+
850
+ document.getElementById('hw-undo-btn').addEventListener('click', () => {
851
+ handwritingCanvasObj.undo();
852
+ });
853
+
854
+ document.getElementById('hw-recognize-btn').addEventListener('click', () => {
855
+ handwritingCanvasObj.recognize();
856
+ });
857
+ }
858
+
859
+ // [關鍵] 每次 resize 或是重啟時,都要重新設定筆畫樣式,以免因為 canvas 重設寬高而遺失
860
  handwritingCanvasObj.setLineWidth(5);
861
  handwritingCanvasObj.setPenColor("#333");
 
 
862
  handwritingCanvasObj.setOptions({
863
  language: "en",
864
  numOfReturn: 1
865
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
866
  }
867
 
868
+ // [新增] 更新輸入介面 (控制手寫板顯示與輸入法)
869
+ // expectedLang: 'en' (預期輸入英文) 或 'zh' (預期輸入中文)
870
+ function updateInputInterface(expectedLang) {
871
  const hwContainer = document.getElementById('handwriting-container');
872
 
873
+ // 只有在行動裝置 且 預期輸入英文時,才開啟手寫板
874
+ const shouldUseHandwriting = (effectiveVersion === 'mobile' && expectedLang === 'en');
875
 
876
+ if (shouldUseHandwriting) {
877
+ // --- 行動版 + 輸入英文 (開啟手寫) ---
878
+ // 1. 顯示手寫板容器
 
 
879
  hwContainer.classList.remove('hidden');
880
  hwContainer.classList.add('flex');
881
 
882
+ // 2. 初始化手寫板 (使用 setTimeout 確保 flex 佈局生效後有正確寬度)
883
+ setTimeout(() => initHandwritingBoard(), 50);
 
884
 
885
+ // 3. 鎖定輸入框 (變成顯示結果用)
886
  answerInput.readOnly = true;
887
+ answerInput.placeholder = "請在下方手寫英文...";
888
+ answerInput.classList.add('handwriting-mode');
889
 
890
+ // 4. 嘗試移除原生輸入法干擾
891
  if (window.google && window.google.ime) {
892
  window.google.ime.setOptions({ ime: 'none', trigger: null });
893
  }
894
 
895
  } else {
896
+ // --- PC 或 行動版輸入中文 (關閉手寫,用鍵盤) ---
 
 
897
  // 1. 隱藏手寫板
898
  hwContainer.classList.add('hidden');
899
  hwContainer.classList.remove('flex');
900
 
901
  // 2. 恢復輸入框
902
  answerInput.readOnly = false;
903
+ answerInput.placeholder = (expectedLang === 'zh') ? "輸入中文答案 (按 Enter 提交)" : "輸入英文答案 (按 Enter 提交)";
904
  answerInput.classList.remove('handwriting-mode');
905
  }
906
+
907
+ console.log(`輸入模式: ${shouldUseHandwriting ? '手寫 (Handwriting)' : '鍵盤 (Keyboard)'}, 語言: ${expectedLang}`);
908
  }
909
 
910
  // --- 視圖管理 ---
 
1097
  frontDisplay.classList.add('hidden');
1098
  clozeQuestionContainer.classList.add('hidden');
1099
 
 
 
 
1100
  let frontText, backText;
1101
+ let targetLanguage = 'en'; // 預設輸入英文
1102
+
1103
  if (isSpeed) {
1104
+ // 極速挑戰
1105
  switch (currentSpeedQuestionType) {
1106
  case 'zh-en':
1107
  frontText = card.chinese;
1108
  backText = card.english;
1109
+ targetLanguage = 'en';
1110
  break;
1111
  case 'en-zh':
1112
  frontText = card.english;
1113
  backText = card.chinese;
1114
  [speakBtn, speakSlowBtn].forEach(btn => btn.classList.remove('hidden'));
1115
+ targetLanguage = 'zh';
1116
  break;
1117
  case 'listen':
1118
  frontText = '請聽發音';
1119
  backText = card.english;
1120
  [speakBtn, speakSlowBtn].forEach(btn => btn.classList.remove('hidden'));
1121
  speakWord(card);
1122
+ targetLanguage = 'en';
1123
  break;
1124
  }
1125
  } else {
 
1131
  progressDisplay.textContent = `第 ${currentCardIndex + 1}/${wordsForCurrentMode.length} 張`;
1132
  prevBtn.disabled = currentCardIndex === 0;
1133
  nextBtn.disabled = currentCardIndex === wordsForCurrentMode.length - 1;
1134
+ // 複習模式下:沒翻面(看英文)=輸入中文,翻面(看中文)=輸入英文
1135
+ const isFlipped = flashcardContainer.classList.contains('flipped');
1136
+ targetLanguage = isFlipped ? 'en' : 'zh';
1137
  break;
1138
  case 'zh-en': case 'hard':
1139
  frontText = card.chinese; backText = card.english;
1140
+ targetLanguage = 'en';
1141
  break;
1142
  case 'en-zh':
1143
  frontText = card.english; backText = card.chinese;
1144
  [speakBtn, speakSlowBtn].forEach(btn => btn.classList.remove('hidden'));
1145
+ targetLanguage = 'zh';
1146
  break;
1147
  case 'listen':
1148
  frontText = '請聽發音'; backText = card.english;
1149
  [speakBtn, speakSlowBtn].forEach(btn => btn.classList.remove('hidden'));
1150
  speakWord(card);
1151
+ targetLanguage = 'en';
1152
  break;
1153
  case 'sentence-cloze':
1154
  clozeQuestionContainer.classList.remove('hidden');
 
1156
  sentenceZhDisplay.textContent = `(${card.sentence.zh})`;
1157
  sentenceZhDisplay.classList.add('hidden');
1158
  toggleTranslationBtn.textContent = '顯示翻譯';
1159
+ backText = card.sentence.answer || card.english;
1160
+ targetLanguage = 'en';
1161
  break;
1162
  }
1163
  }
1164
+
1165
+ // [新增] 根據目標語言決定是否顯示手寫板
1166
+ updateInputInterface(targetLanguage);
1167
+
1168
  if (frontText) {
1169
  frontDisplay.classList.remove('hidden');
1170
  frontDisplay.textContent = frontText;
 
1916
  if (currentMode === 'review') {
1917
  flashcardContainer.classList.toggle('flipped');
1918
  const isFlipped = flashcardContainer.classList.contains('flipped');
1919
+
1920
+ // [智慧輸入切換] 複習模式下:翻面後看到中文->要輸入英文
1921
+ const targetLanguage = isFlipped ? 'en' : 'zh';
1922
+ updateInputInterface(targetLanguage);
1923
+
1924
  feedbackDisplay.textContent = '';
1925
  setTimeout(() => answerInput.focus(), 100);
1926
  }
 
1982
  passwordModal.classList.add('hidden');
1983
  renderWordList();
1984
  // 在進入管理畫面時也更新一下設定狀態
1985
+ updateInputInterface('pc'); // 管理介面不需要手寫
1986
  showView('manage');
1987
  } else {
1988
  passwordError.textContent = '密碼錯誤!';