update html

This commit is contained in:
Shuanglei Tao
2025-07-29 12:48:35 +08:00
parent d232298ddf
commit 4a31862e22
5 changed files with 100 additions and 70 deletions

View File

@@ -202,6 +202,10 @@ code {
align-items: center;
}
.flex-group.right {
margin-left: auto;
}
#status {
margin: 10px 0;
}
@@ -436,6 +440,10 @@ body.debug-mode select:disabled {
min-width: 80px;
}
.flex-group.right {
margin-left: 0;
}
.canvas-tools.flex-container {
flex-direction: row;
flex-wrap: wrap;

View File

@@ -3,13 +3,13 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>电子墨水屏蓝牙控制器</title>
<link rel="stylesheet" href="css/main.css?v=20250607">
<title>墨水屏日历</title>
<link rel="stylesheet" href="css/main.css?v=20250731">
</head>
<body>
<div class="main">
<h3>电子墨水屏蓝牙控制器</h3>
<h3>墨水屏日历</h3>
<fieldset>
<legend>蓝牙连接</legend>
<div class="flex-container">
@@ -18,9 +18,9 @@
<button id="reconnectbutton" type="button" class="secondary" onclick="reConnect()">重连</button>
<button type="button" class="secondary" onclick="clearLog()">清空日志</button>
</div>
<div class="flex-group debug">
<div class="flex-group right debug">
<label for="epddriver">驱动</label>
<select id="epddriver" onchange="updateDitcherOptions()">
<select id="epddriver">
<option value="01" data-color="blackWhiteColor" data-size="4.2_400_300">UC8176/UC8276(黑白)</option>
<option value="03" data-color="threeColor" data-size="4.2_400_300">UC8176/UC8276(三色)</option>
<option value="04" data-color="blackWhiteColor" data-size="4.2_400_300">SSD1619/SSD1683(黑白)</option>
@@ -33,28 +33,32 @@
<input id="epdpins" type="text" value="">
<button id="setDriverbutton" type="button" class="primary" onclick="setDriver()">确认</button>
</div>
<div class="flex-group">
</div>
<div id="log"></div>
</fieldset>
<fieldset>
<legend>设备控制</legend>
<div class="flex-container">
<div class="flex-group">
<button id="calendarmodebutton" type="button" class="primary" onclick="syncTime(1)">日历模式</button>
<button id="clockmodebutton" type="button" class="primary" onclick="syncTime(2)">时钟模式</button>
<button id="clearscreenbutton" type="button" class="secondary" onclick="clearScreen()">清除屏幕</button>
</div>
<div class="flex-group debug">
<div class="flex-group right debug">
<input type="text" id="cmdTXT" value="">
<button id="sendcmdbutton" type="button" class="primary" onclick="sendcmd()">发送命令</button>
</div>
</div>
<div id="log"></div>
</fieldset>
</div>
</fieldset>
<fieldset>
<legend>蓝牙传</legend>
<legend>片处理</legend>
<div class="flex-container">
<input type="file" id="image_file" onchange="updateImage(true)" accept=".png,.jpg,.bmp,.webp,.jpeg">
<input type="file" id="imageFile" accept=".png,.jpg,.bmp,.webp,.jpeg">
</div>
<div class="flex-container options">
<div class="flex-group debug">
<label for="canvasSize">画布尺寸</label>
<select id="canvasSize" onchange="updateCanvasSize()">
<label for="canvasSize">画布尺寸:</label>
<select id="canvasSize">
<option value="1.54_152_152">1.54 (152x152)</option>
<option value="1.54_200_200">1.54 (200x200)</option>
<option value="2.13_212_104">2.13 (212x104)</option>
@@ -76,8 +80,8 @@
</select>
</div>
<div class="flex-group debug">
<label for="ditherMode">颜色模式</label>
<select id="ditherMode" onchange="updateImage(false)">
<label for="ditherMode">颜色模式:</label>
<select id="ditherMode">
<option value="blackWhiteColor">双色(黑白)</option>
<option value="threeColor">三色(黑白红)</option>
<option value="fourColor">四色(黑白红黄)</option>
@@ -85,8 +89,8 @@
</select>
</div>
<div class="flex-group">
<label for="ditherType">抖动算法</label>
<select id="ditherType" onchange="updateImage(false)">
<label for="ditherAlg">抖动算法:</label>
<select id="ditherAlg">
<option value="floydSteinberg">Floyd-Steinberg</option>
<option value="atkinson">Atkinson</option>
<option value="bayer">Bayer</option>
@@ -95,19 +99,21 @@
</select>
</div>
<div class="flex-group">
<label for="ditherStrength">抖动强度</label>
<input type="range" min="0" max="5" step="0.1" value="1.0" id="ditherStrength" oninput="updateImage(false)">
<label for="ditherStrength">抖动强度:</label>
<input type="range" min="0" max="5" step="0.1" value="1.0" id="ditherStrength">
<label id="ditherStrengthValue">1.0</label>
</div>
<div class="flex-group">
<label for="contrast">对比度</label>
<input type="range" min="0.5" max="2" step="0.1" value="1.2" id="contrast" oninput="updateImage(false)">
<label for="ditherContrast">对比度:</label>
<input type="range" min="0.5" max="2" step="0.1" value="1.2" id="ditherContrast">
<label id="ditherContrastValue">1.2</label>
</div>
</div>
<div class="flex-container options">
<div class="flex-group debug">
<label for="mtusize">MTU</label>
<label for="mtusize">MTU:</label>
<input type="number" id="mtusize" value="20" min="0" max="255">
<label for="interleavedcount">确认间隔</label>
<label for="interleavedcount">确认间隔:</label>
<input type="number" id="interleavedcount" value="50" min="0" max="500">
</div>
</div>
@@ -129,7 +135,7 @@
<button id="text-mode" title="添加文字" class="tool-button">T</button>
</div>
<div class="flex-group brush-tools">
<label for="brush-color">画笔:</label>
<label for="brush-color">颜色:</label>
<select id="brush-color">
<option value="#000000">黑色</option>
<option value="#FF0000">红色</option>
@@ -138,13 +144,13 @@
<option value="#0000FF">蓝色</option>
<option value="#FFFFFF">白色</option>
</select>
<label for="brush-size">粗细</label>
<label for="brush-size">粗细:</label>
<input type="number" id="brush-size" value="2" min="1" max="100">
</div>
</div>
<div class="flex-container canvas-tools">
<div class="flex-group text-tools">
<label for="font-family">字体</label>
<label for="font-family">字体:</label>
<select id="font-family">
<option value="Arial">Arial</option>
<option value="sans-serif">Sans-serif</option>
@@ -176,7 +182,7 @@
<option value="WenQuanYi Micro Hei">文泉驿微米黑</option>
</optgroup>
</select>
<label for="font-size">大小</label>
<label for="font-size">大小:</label>
<input type="number" id="font-size" value="16" min="1" max="100">
</div>
<div class="flex-group text-tools">
@@ -197,9 +203,9 @@
</span>
</div>
</div>
<script type="text/javascript" src="js/dithering.js?v=20250607"></script>
<script type="text/javascript" src="js/paint.js?v=20250607"></script>
<script type="text/javascript" src="js/main.js?v=20250607"></script>
<script type="text/javascript" src="js/dithering.js?v=20250731"></script>
<script type="text/javascript" src="js/paint.js?v=20250731"></script>
<script type="text/javascript" src="js/main.js?v=20250731"></script>
</body>
</html>

View File

@@ -74,8 +74,7 @@ function labDistance(lab1, lab2) {
return Math.sqrt(0.2 * dl * dl + 3 * da * da + 3 * db * db);
}
function findClosestColor(r, g, b) {
const mode = document.getElementById('ditherMode').value;
function findClosestColor(r, g, b, mode) {
let palette;
if (mode === 'fourColor') {
@@ -118,7 +117,7 @@ function findClosestColor(r, g, b) {
return closestColor;
}
function floydSteinbergDither(imageData, strength) {
function floydSteinbergDither(imageData, strength, mode) {
const width = imageData.width;
const height = imageData.height;
const data = imageData.data;
@@ -131,7 +130,7 @@ function floydSteinbergDither(imageData, strength) {
const g = tempData[idx + 1];
const b = tempData[idx + 2];
const closest = findClosestColor(r, g, b);
const closest = findClosestColor(r, g, b, mode);
const errR = (r - closest.r) * strength;
const errG = (g - closest.g) * strength;
@@ -171,7 +170,7 @@ function floydSteinbergDither(imageData, strength) {
const g = tempData[idx + 1];
const b = tempData[idx + 2];
const closest = findClosestColor(r, g, b);
const closest = findClosestColor(r, g, b, mode);
data[idx] = closest.r;
data[idx + 1] = closest.g;
data[idx + 2] = closest.b;
@@ -181,7 +180,7 @@ function floydSteinbergDither(imageData, strength) {
return imageData;
}
function atkinsonDither(imageData, strength) {
function atkinsonDither(imageData, strength, mode) {
const width = imageData.width;
const height = imageData.height;
const data = imageData.data;
@@ -194,7 +193,7 @@ function atkinsonDither(imageData, strength) {
const g = tempData[idx + 1];
const b = tempData[idx + 2];
const closest = findClosestColor(r, g, b);
const closest = findClosestColor(r, g, b, mode);
data[idx] = closest.r;
data[idx + 1] = closest.g;
@@ -248,7 +247,7 @@ function atkinsonDither(imageData, strength) {
return imageData;
}
function stuckiDither(imageData, strength) {
function stuckiDither(imageData, strength, mode) {
// 执行Stucki错误扩散算法以处理图像
const width = imageData.width;
const height = imageData.height;
@@ -262,7 +261,7 @@ function stuckiDither(imageData, strength) {
const g = tempData[idx + 1];
const b = tempData[idx + 2];
const closest = findClosestColor(r, g, b);
const closest = findClosestColor(r, g, b, mode);
const errR = (r - closest.r) * strength;
const errG = (g - closest.g) * strength;
@@ -352,7 +351,7 @@ function stuckiDither(imageData, strength) {
const g = tempData[idx + 1];
const b = tempData[idx + 2];
const closest = findClosestColor(r, g, b);
const closest = findClosestColor(r, g, b, mode);
data[idx] = closest.r;
data[idx + 1] = closest.g;
data[idx + 2] = closest.b;
@@ -362,7 +361,7 @@ function stuckiDither(imageData, strength) {
return imageData;
}
function jarvisDither(imageData, strength) {
function jarvisDither(imageData, strength, mode) {
const width = imageData.width;
const height = imageData.height;
const data = imageData.data;
@@ -375,7 +374,7 @@ function jarvisDither(imageData, strength) {
const g = tempData[idx + 1];
const b = tempData[idx + 2];
const closest = findClosestColor(r, g, b);
const closest = findClosestColor(r, g, b, mode);
data[idx] = closest.r;
data[idx + 1] = closest.g;
@@ -465,7 +464,7 @@ function jarvisDither(imageData, strength) {
return imageData;
}
function bayerDither(imageData, strength) {
function bayerDither(imageData, strength, mode) {
const width = imageData.width;
const height = imageData.height;
const data = imageData.data;
@@ -508,7 +507,7 @@ function bayerDither(imageData, strength) {
const clampedB = Math.min(255, Math.max(0, adjustedB));
// Find closest color in palette
const closest = findClosestColor(clampedR, clampedG, clampedB);
const closest = findClosestColor(clampedR, clampedG, clampedB, mode);
data[idx] = closest.r;
data[idx + 1] = closest.g;
@@ -519,21 +518,18 @@ function bayerDither(imageData, strength) {
return imageData;
}
function ditherImage(imageData) {
const ditherType = document.getElementById('ditherType').value;
const ditherStrength = parseFloat(document.getElementById('ditherStrength').value);
switch (ditherType) {
function ditherImage(imageData, alg, strength, mode) {
switch (alg) {
case 'floydSteinberg':
return floydSteinbergDither(imageData, ditherStrength);
return floydSteinbergDither(imageData, strength, mode);
case 'atkinson':
return atkinsonDither(imageData, ditherStrength);
return atkinsonDither(imageData, strength, mode);
case 'stucki':
return stuckiDither(imageData, ditherStrength);
return stuckiDither(imageData, strength, mode);
case 'jarvis':
return jarvisDither(imageData, ditherStrength);
return jarvisDither(imageData, strength, mode);
case 'bayer':
return bayerDither(imageData, ditherStrength);
return bayerDither(imageData, strength, mode);
default:
return imageData;
}
@@ -620,11 +616,10 @@ function decodeProcessedData(processedData, width, height, mode) {
return imageData;
}
function processImageData(imageData) {
function processImageData(imageData, mode) {
const width = imageData.width;
const height = imageData.height;
const data = imageData.data;
const mode = document.getElementById('ditherMode').value;
let processedData;
@@ -637,7 +632,7 @@ function processImageData(imageData) {
const g = data[index + 1];
const b = data[index + 2];
const closest = findClosestColor(r, g, b);
const closest = findClosestColor(r, g, b, mode);
const newIndex = (x * height) + (height - 1 - y);
processedData[newIndex] = closest.value;
}
@@ -650,7 +645,7 @@ function processImageData(imageData) {
const r = data[index];
const g = data[index + 1];
const b = data[index + 2];
const closest = findClosestColor(r, g, b); // 使用 fourColorPalette
const closest = findClosestColor(r, g, b, mode); // 使用 fourColorPalette
const colorValue = closest.value; // 0x00 (黑), 0x01 (白), 0x02 (红), 0x03 (黄)
const newIndex = (y * width + x) / 4 | 0;
const shift = 6 - ((x % 4) * 2);

View File

@@ -176,7 +176,7 @@ async function sendimg() {
status.parentElement.style.display = "block";
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const processedData = processImageData(imageData);
const processedData = processImageData(imageData, ditherMode);
updateButtonStatus(true);
@@ -207,9 +207,9 @@ async function sendimg() {
}
function downloadDataArray() {
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const processedData = processImageData(imageData);
const mode = document.getElementById('ditherMode').value;
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const processedData = processImageData(imageData, mode);
if (mode === 'sixColor' && processedData.length !== canvas.width * canvas.height) {
console.log(`错误:预期${canvas.width * canvas.height}字节,但得到${processedData.length}字节`);
@@ -440,13 +440,13 @@ function updateDitcherOptions() {
}
function updateImage(clear = false) {
const image_file = document.getElementById('image_file');
if (image_file.files.length == 0) return;
const imageFile = document.getElementById('imageFile');
if (imageFile.files.length == 0) return;
const file = imageFile.files[0];
if (clear) clearCanvas();
const file = image_file.files[0];
let image = new Image();;
let image = new Image();
image.src = URL.createObjectURL(file);
image.onload = function (event) {
URL.revokeObjectURL(this.src);
@@ -472,7 +472,7 @@ function clearCanvas() {
}
function convertDithering() {
const contrast = parseFloat(document.getElementById('contrast').value);
const contrast = parseFloat(document.getElementById('ditherContrast').value);
const currentImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const imageData = new ImageData(
new Uint8ClampedArray(currentImageData.data),
@@ -482,12 +482,30 @@ function convertDithering() {
adjustContrast(imageData, contrast);
const alg = document.getElementById('ditherAlg').value;
const strength = parseFloat(document.getElementById('ditherStrength').value);
const mode = document.getElementById('ditherMode').value;
const processedData = processImageData(ditherImage(imageData));
const processedData = processImageData(ditherImage(imageData, alg, strength, mode), mode);
const finalImageData = decodeProcessedData(processedData, canvas.width, canvas.height, mode);
ctx.putImageData(finalImageData, 0, 0);
}
function initEventHandlers() {
document.getElementById("epddriver").addEventListener("change", updateDitcherOptions);
document.getElementById("imageFile").addEventListener("change", function () { updateImage(true); });
document.getElementById("ditherMode").addEventListener("change", function () { updateImage(false); });
document.getElementById("ditherAlg").addEventListener("change", function () { updateImage(false); });
document.getElementById("ditherStrength").addEventListener("input", function () {
updateImage(false);
document.getElementById("ditherStrengthValue").innerText = parseFloat(this.value).toFixed(1);
});
document.getElementById("ditherContrast").addEventListener("input", function () {
updateImage(false);
document.getElementById("ditherContrastValue").innerText = parseFloat(this.value).toFixed(1);
});
document.getElementById("canvasSize").addEventListener("change", updateCanvasSize);
}
function checkDebugMode() {
const link = document.getElementById('debug-toggle');
const urlParams = new URLSearchParams(window.location.search);
@@ -514,6 +532,7 @@ document.body.onload = () => {
ctx.fillRect(0, 0, canvas.width, canvas.height);
initPaintTools();
initEventHandlers();
updateButtonStatus();
checkDebugMode();
}

View File

@@ -96,8 +96,10 @@ function updateToolUI() {
// Show/hide brush tools
document.querySelectorAll('.brush-tools').forEach(el => {
el.style.display = ['brush', 'text'].includes(currentTool) ? 'flex' : 'none';
el.style.display = ['brush', 'eraser', 'text'].includes(currentTool) ? 'flex' : 'none';
});
document.getElementById('brush-color').disabled = currentTool === 'eraser';
document.getElementById('brush-size').disabled = currentTool === 'text';
// Show/hide text tools
document.querySelectorAll('.text-tools').forEach(el => {