diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..22e8364 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +/* \ No newline at end of file diff --git a/pages/fontdrawer.js b/pages/fontdrawer.js index 6e3ad12..7865668 100644 --- a/pages/fontdrawer.js +++ b/pages/fontdrawer.js @@ -253,11 +253,51 @@ $(document).ready(async function () { const $progressBar = $('#progress-bar'); const $progressText = $('#progress-text'); + // 初始化 PressureDrawing 實例 + const pressureDrawing = new PressureDrawing(); + let pressureDrawingEnabled = false; + let pressureDrawingSettings = { + thinning: 0.5, + smoothing: 0.4, + streamline: 0.4 + }; + + // 更新筆壓繪圖狀態 + async function updatePressureDrawingStatus() { + const enabled = await loadFromDB('pressureDrawingEnabled'); + const moduleInitialized = await pressureDrawing.initialize(); + + // 預設啟用筆壓繪圖(除非明確設定為 N) + pressureDrawingEnabled = (enabled !== 'N') && moduleInitialized; + + // 載入筆壓繪圖設定 + pressureDrawingSettings.thinning = parseFloat(await loadFromDB('pressureThinning') || 0.5); + pressureDrawingSettings.smoothing = parseFloat(await loadFromDB('pressureSmoothing') || 0.4); + pressureDrawingSettings.streamline = parseFloat(await loadFromDB('pressureStreamline') || 0.4); + } + // 初始化 IndexedDB - initDB().then(() => { + initDB().then(async () => { console.log('IndexedDB 起動完成'); initCanvas(canvas); // 初始化九宮格底圖 $listSelect.change(); // 觸發一次 change 事件以載入第一個列表 + + // 初始化筆壓繪圖狀態 + await updatePressureDrawingStatus(); + + // 如果是第一次使用,保存預設值 + if (await loadFromDB('pressureDrawingEnabled') === null) { + await saveToDB('pressureDrawingEnabled', 'Y'); + } + if (await loadFromDB('pressureThinning') === null) { + await saveToDB('pressureThinning', 0.5); + } + if (await loadFromDB('pressureSmoothing') === null) { + await saveToDB('pressureSmoothing', 0.4); + } + if (await loadFromDB('pressureStreamline') === null) { + await saveToDB('pressureStreamline', 0.4); + } }).catch((error) => { console.error('IndexedDB 起動失敗', error); }); @@ -298,6 +338,11 @@ $(document).ready(async function () { undoStack.length = 0; // 清空復原堆疊 ctx.clearRect(0, 0, canvas.width, canvas.height); loadCanvasData(nowGlyph); + + // 重置筆壓檢測狀態 + if (pressureDrawingEnabled) { + pressureDrawing.resetPressureDetection(); + } } // 儲存畫布的功能 @@ -361,13 +406,29 @@ $(document).ready(async function () { saveToDB('lineWidth', lineWidth); // 儲存筆寬到 Local Storage }); + // 儲存背景用於筆壓繪圖的即時預覽 + let backgroundImageData = null; + // 開始繪製 - $canvas.on('mousedown touchstart', function (event) { + $canvas.on('mousedown touchstart pointerdown', function (event) { isDrawing = true; undoStack.push(canvas.toDataURL()); // 儲存當前畫布狀態到 undoStack const { x, y } = getCanvasCoordinates(event); - ctx.beginPath(); - ctx.moveTo(x * ratio, y * ratio); + + if (pressureDrawingEnabled) { + // 使用筆壓繪圖系統 + const pressure = pressureDrawing.simulatePressure(event.originalEvent, 'start'); + pressureDrawing.startStroke(x * ratio, y * ratio, pressure); + // 儲存背景圖像用於即時預覽 + backgroundImageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + + // 防止預設的觸控行為(如滾動) + event.preventDefault(); + } else { + // 使用傳統繪圖系統 + ctx.beginPath(); + ctx.moveTo(x * ratio, y * ratio); + } }); var eraseMode = false; @@ -384,24 +445,77 @@ $(document).ready(async function () { }); // 繪製中 - $canvas.on('mousemove touchmove', function (event) { + $canvas.on('mousemove touchmove pointermove', function (event) { if (!isDrawing) return; const { x, y } = getCanvasCoordinates(event); - ctx.globalCompositeOperation = eraseMode ? "destination-out" : "source-over"; // 如果是橡皮擦模式,則使用 destination-out,否則使用 source-over - // 毛筆模式:動態調整線條粗細 - ctx.lineWidth = lineWidth * 0.7 + Math.random() * lineWidth * 0.6; // 粗細隨機變化 - ctx.lineJoin = 'round'; // 線條連接處為圓角 - ctx.lineCap = 'round'; // 線條端點為圓角 - ctx.lineTo(x*ratio, y*ratio); - ctx.strokeStyle = 'black'; - ctx.stroke(); + if (pressureDrawingEnabled) { + // 使用筆壓繪圖系統:收集點並提供即時預覽 + const pressure = pressureDrawing.simulatePressure(event.originalEvent, 'move'); + pressureDrawing.addPoint(x * ratio, y * ratio, pressure); + + // 生成即時預覽筆跡 + const previewStroke = pressureDrawing.createPreviewStroke({ + size: lineWidth, + thinning: pressureDrawingSettings.thinning, + smoothing: pressureDrawingSettings.smoothing, + streamline: pressureDrawingSettings.streamline + }); + + if (previewStroke && backgroundImageData) { + // 恢復背景圖像 + ctx.putImageData(backgroundImageData, 0, 0); + + // 繪製預覽筆跡 + pressureDrawing.drawStrokeOnCanvas(ctx, previewStroke, eraseMode); + } + + // 防止預設的觸控行為 + event.preventDefault(); + } else { + // 使用傳統繪圖系統 + ctx.globalCompositeOperation = eraseMode ? "destination-out" : "source-over"; // 如果是橡皮擦模式,則使用 destination-out,否則使用 source-over + // 毛筆模式:動態調整線條粗細 + ctx.lineWidth = lineWidth * 0.7 + Math.random() * lineWidth * 0.6; // 粗細隨機變化 + ctx.lineJoin = 'round'; // 線條連接處為圓角 + ctx.lineCap = 'round'; // 線條端點為圓角 + ctx.lineTo(x*ratio, y*ratio); + ctx.strokeStyle = 'black'; + ctx.stroke(); + } }); // 停止繪製 - $canvas.on('mouseup mouseleave touchend', function () { + $canvas.on('mouseup mouseleave touchend pointerup pointerleave', function (event) { + if (!isDrawing) return; isDrawing = false; - ctx.closePath(); + + if (pressureDrawingEnabled) { + // 使用筆壓繪圖系統:生成最終筆跡並繪製 + const finalStroke = pressureDrawing.finishStroke({ + size: lineWidth, + thinning: pressureDrawingSettings.thinning, + smoothing: pressureDrawingSettings.smoothing, + streamline: pressureDrawingSettings.streamline + }); + + if (finalStroke && finalStroke.length > 0) { + // 恢復背景圖像(如果有的話) + if (backgroundImageData) { + ctx.putImageData(backgroundImageData, 0, 0); + } + + // 繪製最終筆跡 + pressureDrawing.drawStrokeOnCanvas(ctx, finalStroke, eraseMode); + } + + // 清除背景圖像數據 + backgroundImageData = null; + } else { + // 使用傳統繪圖系統 + ctx.closePath(); + } + saveToLocalDB(); // 停止繪製時儲存畫布內容到 Local Storage }); @@ -621,6 +735,26 @@ $(document).ready(async function () { $('#scaleRateSlider').val(scale); $('#scaleRateValue').text(scale + '%'); + // 載入筆壓繪圖設定 + const pressureEnabledSetting = await loadFromDB('pressureDrawingEnabled'); + const pressureEnabled = pressureEnabledSetting !== 'N'; // 預設啟用(除非明確設定為 N) + $('#pressureDrawingEnabled').prop('checked', pressureEnabled); + + const thinning = await loadFromDB('pressureThinning') || 0.5; + $('#pressureThinningSlider').val(thinning); + $('#pressureThinningValue').text(thinning); + + const smoothing = await loadFromDB('pressureSmoothing') || 0.4; + $('#pressureSmoothingSlider').val(smoothing); + $('#pressureSmoothingValue').text(smoothing); + + const streamline = await loadFromDB('pressureStreamline') || 0.4; + $('#pressureStreamlineSlider').val(streamline); + $('#pressureStreamlineValue').text(streamline); + + // 控制筆壓設定區域的顯示/隱藏 + // $('#pressureSettings').toggle(pressureEnabled); + $('#spanAllCount').text(Object.keys(glyphMap).length); $('#spanDoneCount').text(await countGlyphFromDB()); }); @@ -640,6 +774,39 @@ $(document).ready(async function () { initCanvas(canvas); }); + // 筆壓繪圖設定事件監聽器 + $('#pressureDrawingEnabled').on('change', async function () { + const enabled = $(this).prop('checked'); + saveToDB('pressureDrawingEnabled', enabled ? 'Y' : 'N'); + //$('#pressureSettings').toggle(enabled); + // 立即更新筆壓繪圖狀態 + await updatePressureDrawingStatus(); + }); + + $('#pressureThinningSlider').on('input', function () { + var value = parseFloat($(this).val()); + $('#pressureThinningValue').text(value); + saveToDB('pressureThinning', value); + // 立即更新設定 + pressureDrawingSettings.thinning = value; + }); + + $('#pressureSmoothingSlider').on('input', function () { + var value = parseFloat($(this).val()); + $('#pressureSmoothingValue').text(value); + saveToDB('pressureSmoothing', value); + // 立即更新設定 + pressureDrawingSettings.smoothing = value; + }); + + $('#pressureStreamlineSlider').on('input', function () { + var value = parseFloat($(this).val()); + $('#pressureStreamlineValue').text(value); + saveToDB('pressureStreamline', value); + // 立即更新設定 + pressureDrawingSettings.streamline = value; + }); + // 顯示字表畫面 $('#canvasListButton').on('click', async function () { $('#listup-container').show(); diff --git a/pages/index.html b/pages/index.html index 1b3009b..d30fde7 100644 --- a/pages/index.html +++ b/pages/index.html @@ -36,19 +36,12 @@ #nextButton { font-size: 2.5em; position: absolute; right: 0; top: 0; border: 0; background: transparent; box-shadow: none } #canvas-container { position: relative; width: 360px; height: 360px; border: 1px solid #888; background-color: #fff;} - canvas { position: absolute; top: 0; left: 0; width: 360px; height: 360px } + canvas { position: absolute; top: 0; left: 0; width: 360px; height: 360px; touch-action: none; } @media screen and ((max-height: 700px) or (max-width: 380px)) { #canvas-container { width: 320px; height: 320px } - canvas { width: 320px; height: 320px } + canvas { width: 320px; height: 320px; touch-action: none; } } - /* 九宮格底圖樣式 */ - #gridCanvas { - background-image: url('grid.png'); /* 替換為您的九宮格底圖 */ - background-size: cover; - background-repeat: no-repeat; - - } #gridCanvas { z-index: 0 } /* 九宮格底圖在下方 */ #drawingCanvas { position: absolute; top: 0; left: 0; z-index: 1 } /* 繪圖畫布在上方 */ @@ -69,7 +62,7 @@ .dialog { display: none; position: fixed; top: 0; left: 0; width: 90%; height: 100%; background-color: rgba(0, 0, 0, 0.8); color: #fff; z-index: 999; text-align: left; padding: 5% } .dialog h2 { margin-top: 0.2em } .close { position: absolute; display: block; right: 20px; top: 15px; font: normal 3em/1 sans-serif} - .dialog-body { height: 84%; overflow: scroll } + .dialog-body { height: 100%; overflow: scroll } #listup-body img { display: block; background-color: #fff; width: 50px; height: 50px; float: left; border: 1px solid #ccc; margin: 5px } #listup-body span { display: block; background-color: #666; color: #ddd; width: 50px; height: 50px; float: left; border: 1px solid #ccc; line-height: 50px; text-align: center; font-size: 2em; margin: 5px; font-family: GenYoExt, LessonOne, sans-serif; overflow: hidden; } @@ -81,6 +74,7 @@ #settings-container input[type="range"] { width: 80%; vertical-align: middle; } #settings-container input[type="checkbox"] { width: 2em; height: 2em } #settings-container .note { display: block; color: #ccc } + #pressureSettings { display: none; /*margin-left: 20px;*/ } #hintContent { padding: 0 1.2em } #hintContent li { margin: 10px 0; line-height: 1.6; } @@ -141,7 +135,7 @@