mirror of
https://github.com/tsl0922/EPD-nRF5.git
synced 2025-12-06 15:42:48 +08:00
fix brush cursor visibility
This commit is contained in:
@@ -22,7 +22,7 @@
|
||||
</div>
|
||||
<div class="flex-group right debug">
|
||||
<label for="epddriver">驱动</label>
|
||||
<select id="epddriver">
|
||||
<select id="epddriver" onchange="updateDitcherOptions()">
|
||||
<option value="01" data-color="blackWhiteColor" data-size="4.2_400_300">4.2寸 (黑白, UC8176)</option>
|
||||
<option value="03" data-color="threeColor" data-size="4.2_400_300">4.2寸 (三色, UC8176)</option>
|
||||
<option value="04" data-color="blackWhiteColor" data-size="4.2_400_300">4.2寸 (黑白, SSD1619)</option>
|
||||
@@ -62,12 +62,12 @@
|
||||
<fieldset>
|
||||
<legend>蓝牙传图</legend>
|
||||
<div class="flex-container">
|
||||
<input type="file" id="imageFile" accept=".png,.jpg,.bmp,.webp,.jpeg">
|
||||
<input type="file" id="imageFile" accept=".png,.jpg,.bmp,.webp,.jpeg" onchange="updateImage()">
|
||||
</div>
|
||||
<div class="flex-container options">
|
||||
<div class="flex-group debug">
|
||||
<label for="canvasSize">画布尺寸:</label>
|
||||
<select id="canvasSize">
|
||||
<select id="canvasSize" onchange="updateCanvasSize()">
|
||||
<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>
|
||||
@@ -94,7 +94,7 @@
|
||||
</div>
|
||||
<div class="flex-group debug">
|
||||
<label for="ditherMode">颜色模式:</label>
|
||||
<select id="ditherMode">
|
||||
<select id="ditherMode" onchange="applyDither()">
|
||||
<option value="blackWhiteColor">双色(黑白)</option>
|
||||
<option value="threeColor">三色(黑白红)</option>
|
||||
<option value="fourColor">四色(黑白红黄)</option>
|
||||
@@ -103,7 +103,7 @@
|
||||
</div>
|
||||
<div class="flex-group">
|
||||
<label for="ditherAlg">抖动算法:</label>
|
||||
<select id="ditherAlg">
|
||||
<select id="ditherAlg" onchange="applyDither()">
|
||||
<option value="floydSteinberg">Floyd-Steinberg</option>
|
||||
<option value="atkinson">Atkinson</option>
|
||||
<option value="bayer">Bayer</option>
|
||||
@@ -217,7 +217,7 @@
|
||||
<button id="crop-move-up" title="上移">⇧</button>
|
||||
<button id="crop-move-down" title="下移">⇩</button>
|
||||
<button id="crop-move-right" title="右移">⇨</button>
|
||||
<button class="primary" onclick="cropManager.finishCrop()">完成</button>
|
||||
<button class="primary" onclick="applyDither()">完成</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
class CropManager {
|
||||
constructor(canvas, ctx, paintManager) {
|
||||
constructor(canvas, ctx) {
|
||||
this.canvas = canvas;
|
||||
this.ctx = ctx;
|
||||
this.paintManager = paintManager;
|
||||
this.backgroundZoom = 1;
|
||||
this.backgroundPanX = 0;
|
||||
this.backgroundPanY = 0;
|
||||
@@ -84,7 +83,7 @@ class CropManager {
|
||||
this.canvas.parentNode.classList.add('crop-mode');
|
||||
}
|
||||
|
||||
finishCrop() {
|
||||
finishCrop(callback) {
|
||||
const imageFile = document.getElementById('imageFile');
|
||||
if (imageFile.files.length == 0) return;
|
||||
|
||||
@@ -103,12 +102,8 @@ class CropManager {
|
||||
fillCanvas('white');
|
||||
this.ctx.drawImage(image, sx, sy, sWidth, sHeight, 0, 0, this.canvas.width, this.canvas.height);
|
||||
|
||||
this.paintManager.redrawTextElements();
|
||||
this.paintManager.redrawLineSegments();
|
||||
convertDithering();
|
||||
|
||||
this.exitCropMode();
|
||||
this.paintManager.saveToHistory(); // Save after finishing crop
|
||||
if (callback) callback();
|
||||
};
|
||||
image.src = URL.createObjectURL(imageFile.files[0]);
|
||||
}
|
||||
|
||||
@@ -492,10 +492,7 @@ function updateImage() {
|
||||
if (image.width / image.height == canvas.width / canvas.height) {
|
||||
if (cropManager.isCropMode()) cropManager.exitCropMode();
|
||||
ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height);
|
||||
paintManager.redrawTextElements();
|
||||
paintManager.redrawLineSegments();
|
||||
convertDithering();
|
||||
paintManager.saveToHistory(); // Save after loading image
|
||||
} else {
|
||||
alert(`图片宽高比例与画布不匹配,将进入裁剪模式。\n请放大图片后移动图片使其充满画布, 再点击"完成"按钮。`);
|
||||
paintManager.setActiveTool(null, '');
|
||||
@@ -548,6 +545,9 @@ function clearCanvas() {
|
||||
}
|
||||
|
||||
function convertDithering() {
|
||||
paintManager.redrawTextElements();
|
||||
paintManager.redrawLineSegments();
|
||||
|
||||
const contrast = parseFloat(document.getElementById('ditherContrast').value);
|
||||
const currentImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
const imageData = new ImageData(
|
||||
@@ -564,22 +564,23 @@ function convertDithering() {
|
||||
const processedData = processImageData(ditherImage(imageData, alg, strength, mode), mode);
|
||||
const finalImageData = decodeProcessedData(processedData, canvas.width, canvas.height, mode);
|
||||
ctx.putImageData(finalImageData, 0, 0);
|
||||
|
||||
paintManager.saveToHistory(); // Save dithered image to history
|
||||
}
|
||||
|
||||
function applyDither() {
|
||||
cropManager.finishCrop(() => convertDithering());
|
||||
}
|
||||
|
||||
function initEventHandlers() {
|
||||
document.getElementById("epddriver").addEventListener("change", updateDitcherOptions);
|
||||
document.getElementById("imageFile").addEventListener("change", updateImage);
|
||||
document.getElementById("ditherMode").addEventListener("change", () => cropManager.finishCrop());
|
||||
document.getElementById("ditherAlg").addEventListener("change", () => cropManager.finishCrop());
|
||||
document.getElementById("ditherStrength").addEventListener("input", function () {
|
||||
cropManager.finishCrop();
|
||||
document.getElementById("ditherStrengthValue").innerText = parseFloat(this.value).toFixed(1);
|
||||
document.getElementById("ditherStrength").addEventListener("input", (e) => {
|
||||
document.getElementById("ditherStrengthValue").innerText = parseFloat(e.target.value).toFixed(1);
|
||||
applyDither();
|
||||
});
|
||||
document.getElementById("ditherContrast").addEventListener("input", function () {
|
||||
cropManager.finishCrop();
|
||||
document.getElementById("ditherContrastValue").innerText = parseFloat(this.value).toFixed(1);
|
||||
document.getElementById("ditherContrast").addEventListener("input", (e) => {
|
||||
document.getElementById("ditherContrastValue").innerText = parseFloat(e.target.value).toFixed(1);
|
||||
applyDither();
|
||||
});
|
||||
document.getElementById("canvasSize").addEventListener("change", updateCanvasSize);
|
||||
}
|
||||
|
||||
function checkDebugMode() {
|
||||
|
||||
@@ -174,9 +174,8 @@ class PaintManager {
|
||||
document.addEventListener('keydown', this.handleKeyboard);
|
||||
|
||||
// Mouse move for brush cursor
|
||||
this.canvas.addEventListener('mousemove', this.updateBrushCursor);
|
||||
this.canvas.addEventListener('mouseenter', this.updateBrushCursor);
|
||||
this.canvas.addEventListener('mouseleave', this.hideBrushCursor);
|
||||
this.canvas.addEventListener('mousemove', this.updateBrushCursor);
|
||||
|
||||
// Create brush cursor element
|
||||
this.createBrushCursor();
|
||||
@@ -218,9 +217,6 @@ class PaintManager {
|
||||
|
||||
// Cancel any pending text placement
|
||||
this.cancelTextPlacement();
|
||||
|
||||
// Update brush cursor visibility
|
||||
this.updateBrushCursorVisibility();
|
||||
}
|
||||
|
||||
createBrushCursor() {
|
||||
@@ -259,54 +255,54 @@ class PaintManager {
|
||||
this.brushCursor.style.height = size + 'px';
|
||||
}
|
||||
|
||||
updateBrushCursorVisibility() {
|
||||
if (!this.brushCursor) return;
|
||||
|
||||
if (this.currentTool === 'brush' || this.currentTool === 'eraser') {
|
||||
this.brushCursor.style.display = 'block';
|
||||
this.canvas.style.cursor = 'none';
|
||||
} else {
|
||||
this.brushCursor.style.display = 'none';
|
||||
this.canvas.style.cursor = 'default';
|
||||
}
|
||||
}
|
||||
|
||||
updateBrushCursor(e) {
|
||||
if (!this.brushCursor) return;
|
||||
|
||||
if (this.currentTool === 'brush' || this.currentTool === 'eraser') {
|
||||
this.brushCursor.style.display = 'block';
|
||||
this.canvas.style.cursor = 'none';
|
||||
// Check if mouse is within canvas bounds
|
||||
const rect = this.canvas.getBoundingClientRect();
|
||||
const isInCanvas = e.clientX >= rect.left &&
|
||||
e.clientX <= rect.right &&
|
||||
e.clientY >= rect.top &&
|
||||
e.clientY <= rect.bottom;
|
||||
|
||||
// Store the pending position
|
||||
this.pendingCursorX = e.clientX;
|
||||
this.pendingCursorY = e.clientY;
|
||||
if (isInCanvas) {
|
||||
this.brushCursor.style.display = 'block';
|
||||
this.canvas.style.cursor = 'none';
|
||||
|
||||
// Schedule update using requestAnimationFrame for smooth movement
|
||||
if (!this.cursorUpdateScheduled) {
|
||||
this.cursorUpdateScheduled = true;
|
||||
requestAnimationFrame(() => {
|
||||
this.brushCursor.style.transform = `translate(${this.pendingCursorX}px, ${this.pendingCursorY}px) translate(-50%, -50%)`;
|
||||
this.cursorUpdateScheduled = false;
|
||||
});
|
||||
}
|
||||
// Store the pending position
|
||||
this.pendingCursorX = e.clientX;
|
||||
this.pendingCursorY = e.clientY;
|
||||
|
||||
// Update color to match brush or show white for eraser (only needs to happen once or when tool changes)
|
||||
if (this.currentTool === 'eraser') {
|
||||
if (this.brushCursor.getAttribute('data-tool') !== 'eraser') {
|
||||
this.brushCursor.style.border = '2px solid rgba(255, 0, 0, 0.7)';
|
||||
this.brushCursor.style.backgroundColor = 'rgba(255, 255, 255, 0.2)';
|
||||
this.brushCursor.style.boxShadow = 'none';
|
||||
this.brushCursor.setAttribute('data-tool', 'eraser');
|
||||
// Schedule update using requestAnimationFrame for smooth movement
|
||||
if (!this.cursorUpdateScheduled) {
|
||||
this.cursorUpdateScheduled = true;
|
||||
requestAnimationFrame(() => {
|
||||
this.brushCursor.style.transform = `translate(${this.pendingCursorX}px, ${this.pendingCursorY}px) translate(-50%, -50%)`;
|
||||
this.cursorUpdateScheduled = false;
|
||||
});
|
||||
}
|
||||
|
||||
// Update color to match brush or show white for eraser (only needs to happen once or when tool changes)
|
||||
if (this.currentTool === 'eraser') {
|
||||
if (this.brushCursor.getAttribute('data-tool') !== 'eraser') {
|
||||
this.brushCursor.style.border = '2px solid rgba(255, 0, 0, 0.7)';
|
||||
this.brushCursor.style.backgroundColor = 'rgba(255, 255, 255, 0.2)';
|
||||
this.brushCursor.style.boxShadow = 'none';
|
||||
this.brushCursor.setAttribute('data-tool', 'eraser');
|
||||
}
|
||||
} else {
|
||||
if (this.brushCursor.getAttribute('data-tool') !== 'brush') {
|
||||
// Use a contrasting border - white with black outline for visibility
|
||||
this.brushCursor.style.border = '1px solid white';
|
||||
this.brushCursor.style.boxShadow = '0 0 0 1px black, inset 0 0 0 1px black';
|
||||
this.brushCursor.style.backgroundColor = 'transparent';
|
||||
this.brushCursor.setAttribute('data-tool', 'brush');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (this.brushCursor.getAttribute('data-tool') !== 'brush') {
|
||||
// Use a contrasting border - white with black outline for visibility
|
||||
this.brushCursor.style.border = '1px solid white';
|
||||
this.brushCursor.style.boxShadow = '0 0 0 1px black, inset 0 0 0 1px black';
|
||||
this.brushCursor.style.backgroundColor = 'transparent';
|
||||
this.brushCursor.setAttribute('data-tool', 'brush');
|
||||
}
|
||||
// Hide cursor when outside canvas
|
||||
this.brushCursor.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -353,6 +349,8 @@ class PaintManager {
|
||||
this.isDraggingText = false;
|
||||
this.lastX = 0;
|
||||
this.lastY = 0;
|
||||
|
||||
this.hideBrushCursor();
|
||||
}
|
||||
|
||||
paint(e) {
|
||||
|
||||
Reference in New Issue
Block a user