Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/*
197 changes: 182 additions & 15 deletions pages/fontdrawer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
Expand Down Expand Up @@ -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();
}
}

// 儲存畫布的功能
Expand Down Expand Up @@ -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;
Expand All @@ -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
});

Expand Down Expand Up @@ -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());
});
Expand All @@ -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();
Expand Down
37 changes: 26 additions & 11 deletions pages/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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 } /* 繪圖畫布在上方 */

Expand All @@ -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; }
Expand All @@ -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; }
Expand Down Expand Up @@ -141,7 +135,7 @@ <h2>字符列表</h2>

<div id="hint-container" class="dialog">
<span id="closeHintButton" class="close">×</span>
<h2>提示 Ver 0.26</h2>
<h2>提示 Ver 0.30</h2>
<ul id="hintContent" class="dialog-body">
<li>本工具由「<a href="https://zi-hi.com" target="_blank">字嗨!</a>」提供。</li>
<li>書寫的字表來自「<a href="https://justfont.com/jf7000" target="_blank">jf7000當務字集</a>」。</li>
Expand Down Expand Up @@ -176,6 +170,26 @@ <h2>設定</h2>
<input id="noFixedWidthFlag" type="checkbox" />
<span class="note">設定為不等寬會更像手寫字型,但可能不適合用於直排。</span>
<br />
<label for="pressureDrawingEnabled">啟用筆壓/模擬筆壓繪圖系統</label>
<input id="pressureDrawingEnabled" type="checkbox" checked />
<span class="note">啟用後可提供更自然的筆壓效果,適合觸控筆或支援筆壓的設備。不支援的設備也會自動模擬筆壓繪圖。</span>
<br />
<div id="pressureSettings">
<label for="pressureThinningSlider">壓力對粗細的影響 (thinning)</label>
<input id="pressureThinningSlider" type="range" min="0" max="1" step="0.1" value="0.5" />
<span id="pressureThinningValue">0.5</span>
<span class="note">數值越大,壓力變化對線條粗細的影響越明顯。</span>
<br />
<label for="pressureSmoothingSlider">平滑程度 (smoothing)</label>
<input id="pressureSmoothingSlider" type="range" min="0" max="1" step="0.1" value="0.4" />
<span id="pressureSmoothingValue">0.4</span>
<span class="note">數值越大,線條越平滑。</span>
<br />
<label for="pressureStreamlineSlider">流線化程度 (streamline)</label>
<input id="pressureStreamlineSlider" type="range" min="0" max="1" step="0.1" value="0.4" />
<span id="pressureStreamlineValue">0.4</span>
<span class="note">數值越大,線條越流暢,但筆壓變化可能較不明顯。</span>
</div>
<br />
<br />
<hr />
Expand Down Expand Up @@ -210,6 +224,7 @@ <h2>設定</h2>
clearDone: '已清除。'
};
</script>
<script src="pressure-drawing.js"></script>
<script src="fontdrawer.js"></script>
</body>
</html>
Loading