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