mirror of
https://github.com/jam422470459/EPD-nRF52-hema213.git
synced 2025-12-06 16:42:49 +08:00
add paint tools for html
This commit is contained in:
@@ -348,11 +348,86 @@ label {
|
||||
border-bottom: 1px dotted #AAA;
|
||||
}
|
||||
|
||||
canvas.text-placement-mode {
|
||||
border: 2px dashed var(--primary-color) !important;
|
||||
cursor: text !important; /* Force text cursor */
|
||||
}
|
||||
|
||||
.canvas-tooltip {
|
||||
display: none;
|
||||
text-align: center;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.canvas-tools {
|
||||
margin-top: 10px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.text-tools {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tool-button {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
font-size: 1.2rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 5px;
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.tool-button:hover {
|
||||
background-color: #e9ecef;
|
||||
border-color: #ced4da;
|
||||
}
|
||||
|
||||
.tool-button.active {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
body.debug-mode .tool-button {
|
||||
background-color: var(--dark-input-bg);
|
||||
border-color: var(--dark-border);
|
||||
color: var(--dark-text);
|
||||
}
|
||||
|
||||
body.debug-mode .tool-button:hover {
|
||||
background-color: #3a3a3a;
|
||||
border-color: #444;
|
||||
}
|
||||
|
||||
body.debug-mode .tool-button.active {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
border-color: var(--primary-hover);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.flex-container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.canvas-tools.flex-container {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.canvas-tools .flex-group {
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.left-controls,
|
||||
.right-controls {
|
||||
margin-left: 0;
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -364,7 +364,9 @@ function clear_canvas() {
|
||||
if(confirm('确认清除画布内容?')) {
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function convert_dithering() {
|
||||
|
||||
428
html/js/paint.js
Normal file
428
html/js/paint.js
Normal file
@@ -0,0 +1,428 @@
|
||||
let painting = false;
|
||||
let lastX = 0;
|
||||
let lastY = 0;
|
||||
let brushColor = "#000000";
|
||||
let brushSize = 2;
|
||||
let currentTool = null; // Start with no tool selected
|
||||
let textElements = []; // Store text elements for re-rendering after dithering
|
||||
let lineSegments = []; // Store line segments for re-rendering after dithering
|
||||
let isTextPlacementMode = false;
|
||||
let draggingCanvasContext = null; // Backup of the canvas for dragging
|
||||
let selectedTextElement = null; // Track the currently selected text for dragging
|
||||
let isDraggingText = false; // Track if we're currently dragging text
|
||||
let dragOffsetX = 0; // Offset from mouse to text position when dragging
|
||||
let dragOffsetY = 0;
|
||||
let textBold = false; // Track if text should be bold
|
||||
let textItalic = false; // Track if text should be italic
|
||||
|
||||
function initPaintTools() {
|
||||
document.getElementById('brush-mode').addEventListener('click', () => {
|
||||
if (currentTool === 'brush') {
|
||||
setActiveTool(null);
|
||||
} else {
|
||||
setActiveTool('brush');
|
||||
brushColor = document.getElementById('brush-color').value;
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('eraser-mode').addEventListener('click', () => {
|
||||
if (currentTool === 'eraser') {
|
||||
setActiveTool(null);
|
||||
} else {
|
||||
setActiveTool('eraser');
|
||||
brushColor = "#FFFFFF";
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('text-mode').addEventListener('click', () => {
|
||||
if (currentTool === 'text') {
|
||||
setActiveTool(null);
|
||||
} else {
|
||||
setActiveTool('text');
|
||||
brushColor = document.getElementById('brush-color').value;
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('brush-color').addEventListener('change', (e) => {
|
||||
brushColor = e.target.value;
|
||||
});
|
||||
|
||||
document.getElementById('brush-size').addEventListener('change', (e) => {
|
||||
brushSize = parseInt(e.target.value);
|
||||
});
|
||||
|
||||
document.getElementById('add-text-btn').addEventListener('click', startTextPlacement);
|
||||
|
||||
// Add event listeners for bold and italic buttons
|
||||
document.getElementById('text-bold').addEventListener('click', () => {
|
||||
textBold = !textBold;
|
||||
document.getElementById('text-bold').classList.toggle('primary', textBold);
|
||||
});
|
||||
|
||||
document.getElementById('text-italic').addEventListener('click', () => {
|
||||
textItalic = !textItalic;
|
||||
document.getElementById('text-italic').classList.toggle('primary', textItalic);
|
||||
});
|
||||
|
||||
setupCanvasForPainting();
|
||||
|
||||
// Override the existing clear_canvas function to clear our text positions too
|
||||
const originalClearCanvas = window.clear_canvas;
|
||||
window.clear_canvas = function() {
|
||||
if(originalClearCanvas()) {
|
||||
textElements = []; // Clear stored text positions
|
||||
lineSegments = []; // Clear stored line segments
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Override the existing convert_dithering function to preserve text and lines
|
||||
const originalConvertDithering = window.convert_dithering;
|
||||
window.convert_dithering = function() {
|
||||
originalConvertDithering();
|
||||
// Redraw text and lines after dithering
|
||||
redrawTextElements();
|
||||
redrawLineSegments();
|
||||
};
|
||||
|
||||
// Ensure no tool is selected by default
|
||||
updateToolUI();
|
||||
}
|
||||
|
||||
function setActiveTool(tool) {
|
||||
currentTool = tool;
|
||||
updateToolUI();
|
||||
|
||||
// clear and hide tooltip
|
||||
document.querySelector('.canvas-tooltip').innerText = '';
|
||||
document.querySelector('.canvas-tooltip').style.display = 'none';
|
||||
|
||||
// Cancel any pending text placement
|
||||
cancelTextPlacement();
|
||||
}
|
||||
|
||||
function updateToolUI() {
|
||||
// Update UI to reflect active tool or no tool
|
||||
document.getElementById('brush-mode').classList.toggle('active', currentTool === 'brush');
|
||||
document.getElementById('eraser-mode').classList.toggle('active', currentTool === 'eraser');
|
||||
document.getElementById('text-mode').classList.toggle('active', currentTool === 'text');
|
||||
|
||||
// Show/hide text tools
|
||||
document.querySelectorAll('.text-tools').forEach(el => {
|
||||
el.style.display = currentTool === 'text' ? 'flex' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
function setupCanvasForPainting() {
|
||||
canvas.addEventListener('mousedown', startPaint);
|
||||
canvas.addEventListener('mousemove', paint);
|
||||
canvas.addEventListener('mouseup', endPaint);
|
||||
canvas.addEventListener('mouseleave', endPaint);
|
||||
canvas.addEventListener('click', handleCanvasClick);
|
||||
|
||||
// Touch support
|
||||
canvas.addEventListener('touchstart', handleTouchStart);
|
||||
canvas.addEventListener('touchmove', handleTouchMove);
|
||||
canvas.addEventListener('touchend', endPaint);
|
||||
}
|
||||
|
||||
function startPaint(e) {
|
||||
if (!currentTool) return;
|
||||
|
||||
if (currentTool === 'text') {
|
||||
// Check if we're clicking on a text element to drag
|
||||
const textElement = findTextElementAt(e);
|
||||
if (textElement && textElement === selectedTextElement) {
|
||||
isDraggingText = true;
|
||||
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const scaleX = canvas.width / rect.width;
|
||||
const scaleY = canvas.height / rect.height;
|
||||
const x = (e.clientX - rect.left) * scaleX;
|
||||
const y = (e.clientY - rect.top) * scaleY;
|
||||
|
||||
// Calculate offset for smooth dragging
|
||||
dragOffsetX = textElement.x - x;
|
||||
dragOffsetY = textElement.y - y;
|
||||
|
||||
return; // Don't start drawing
|
||||
}
|
||||
} else {
|
||||
painting = true;
|
||||
draw(e);
|
||||
}
|
||||
}
|
||||
|
||||
function endPaint() {
|
||||
painting = false;
|
||||
isDraggingText = false;
|
||||
lastX = 0;
|
||||
lastY = 0;
|
||||
}
|
||||
|
||||
function paint(e) {
|
||||
if (!currentTool) return;
|
||||
|
||||
if (currentTool === 'text') {
|
||||
if (isDraggingText && selectedTextElement) {
|
||||
dragText(e);
|
||||
}
|
||||
} else {
|
||||
if (painting) {
|
||||
draw(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function draw(e) {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const scaleX = canvas.width / rect.width;
|
||||
const scaleY = canvas.height / rect.height;
|
||||
const x = (e.clientX - rect.left) * scaleX;
|
||||
const y = (e.clientY - rect.top) * scaleY;
|
||||
|
||||
ctx.lineJoin = 'round';
|
||||
ctx.lineCap = 'round';
|
||||
ctx.strokeStyle = brushColor;
|
||||
ctx.lineWidth = brushSize;
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
if (lastX === 0 && lastY === 0) {
|
||||
// For the first point, just do a dot
|
||||
ctx.moveTo(x, y);
|
||||
ctx.lineTo(x+0.1, y+0.1);
|
||||
|
||||
// Store the dot for redrawing
|
||||
lineSegments.push({
|
||||
type: 'dot',
|
||||
x: x,
|
||||
y: y,
|
||||
color: brushColor,
|
||||
size: brushSize
|
||||
});
|
||||
} else {
|
||||
// Connect to the previous point
|
||||
ctx.moveTo(lastX, lastY);
|
||||
ctx.lineTo(x, y);
|
||||
|
||||
// Store the line segment for redrawing
|
||||
lineSegments.push({
|
||||
type: 'line',
|
||||
x1: lastX,
|
||||
y1: lastY,
|
||||
x2: x,
|
||||
y2: y,
|
||||
color: brushColor,
|
||||
size: brushSize
|
||||
});
|
||||
}
|
||||
|
||||
ctx.stroke();
|
||||
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
}
|
||||
|
||||
function handleCanvasClick(e) {
|
||||
if (currentTool === 'text' && isTextPlacementMode) {
|
||||
placeText(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Improve touch handling for text placement
|
||||
function handleTouchStart(e) {
|
||||
e.preventDefault();
|
||||
const touch = e.touches[0];
|
||||
|
||||
// If in text placement mode, handle as a click
|
||||
if (currentTool === 'text' && isTextPlacementMode) {
|
||||
const mouseEvent = new MouseEvent('click', {
|
||||
clientX: touch.clientX,
|
||||
clientY: touch.clientY
|
||||
});
|
||||
canvas.dispatchEvent(mouseEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise handle as normal drawing
|
||||
const mouseEvent = new MouseEvent('mousedown', {
|
||||
clientX: touch.clientX,
|
||||
clientY: touch.clientY
|
||||
});
|
||||
canvas.dispatchEvent(mouseEvent);
|
||||
}
|
||||
|
||||
function handleTouchMove(e) {
|
||||
e.preventDefault();
|
||||
const touch = e.touches[0];
|
||||
const mouseEvent = new MouseEvent('mousemove', {
|
||||
clientX: touch.clientX,
|
||||
clientY: touch.clientY
|
||||
});
|
||||
canvas.dispatchEvent(mouseEvent);
|
||||
}
|
||||
|
||||
function dragText(e) {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const scaleX = canvas.width / rect.width;
|
||||
const scaleY = canvas.height / rect.height;
|
||||
const x = (e.clientX - rect.left) * scaleX;
|
||||
const y = (e.clientY - rect.top) * scaleY;
|
||||
|
||||
// Update text position with offset
|
||||
selectedTextElement.x = x + dragOffsetX;
|
||||
selectedTextElement.y = y + dragOffsetY;
|
||||
|
||||
// Redraw selected text element
|
||||
if (draggingCanvasContext) {
|
||||
ctx.putImageData(draggingCanvasContext, 0, 0);
|
||||
} else {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
ctx.font = selectedTextElement.font;
|
||||
ctx.fillStyle = selectedTextElement.color;
|
||||
ctx.fillText(selectedTextElement.text, selectedTextElement.x, selectedTextElement.y);
|
||||
}
|
||||
|
||||
function findTextElementAt(e) {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const scaleX = canvas.width / rect.width;
|
||||
const scaleY = canvas.height / rect.height;
|
||||
const x = (e.clientX - rect.left) * scaleX;
|
||||
const y = (e.clientY - rect.top) * scaleY;
|
||||
|
||||
// Search through text elements in reverse order (top-most first)
|
||||
for (let i = textElements.length - 1; i >= 0; i--) {
|
||||
const text = textElements[i];
|
||||
|
||||
// Calculate text dimensions
|
||||
ctx.font = text.font;
|
||||
const textWidth = ctx.measureText(text.text).width;
|
||||
|
||||
// Extract font size correctly from the font string
|
||||
// This handles "bold 14px Arial", "italic 14px Arial", "bold italic 14px Arial", etc.
|
||||
const fontSizeMatch = text.font.match(/(\d+)px/);
|
||||
const fontSize = fontSizeMatch ? parseInt(fontSizeMatch[1]) : 14; // Default to 14 if not found
|
||||
const textHeight = fontSize * 1.2; // Approximate height
|
||||
|
||||
// Check if click is within text bounds (allowing for some margin)
|
||||
const margin = 5;
|
||||
if (x >= text.x - margin &&
|
||||
x <= text.x + textWidth + margin &&
|
||||
y >= text.y - textHeight + margin &&
|
||||
y <= text.y + margin) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function startTextPlacement() {
|
||||
const text = document.getElementById('text-input').value.trim();
|
||||
if (!text) {
|
||||
alert('请输入文字内容');
|
||||
return;
|
||||
}
|
||||
|
||||
isTextPlacementMode = true;
|
||||
|
||||
// Add visual feedback
|
||||
document.querySelector('.canvas-tooltip').style.display = 'block';
|
||||
document.querySelector('.canvas-tooltip').innerText = '点击画布放置文字';
|
||||
canvas.classList.add('text-placement-mode');
|
||||
}
|
||||
|
||||
function cancelTextPlacement() {
|
||||
isTextPlacementMode = false;
|
||||
canvas.classList.remove('text-placement-mode');
|
||||
|
||||
// reset dragging state
|
||||
isDraggingText = false;
|
||||
dragOffsetX = 0;
|
||||
dragOffsetY = 0;
|
||||
selectedTextElement = null;
|
||||
draggingCanvasContext = null;
|
||||
}
|
||||
|
||||
function placeText(e) {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const scaleX = canvas.width / rect.width;
|
||||
const scaleY = canvas.height / rect.height;
|
||||
const x = (e.clientX - rect.left) * scaleX;
|
||||
const y = (e.clientY - rect.top) * scaleY;
|
||||
|
||||
const text = document.getElementById('text-input').value;
|
||||
const fontFamily = document.getElementById('font-family').value;
|
||||
const fontSize = document.getElementById('font-size').value;
|
||||
|
||||
// Build font style string
|
||||
let fontStyle = '';
|
||||
if (textItalic) fontStyle += 'italic ';
|
||||
if (textBold) fontStyle += 'bold ';
|
||||
|
||||
// Create a new text element
|
||||
const newText = {
|
||||
text: text,
|
||||
x: x,
|
||||
y: y,
|
||||
font: `${fontStyle}${fontSize}px ${fontFamily}`,
|
||||
color: brushColor
|
||||
};
|
||||
|
||||
// Add to our list of text elements
|
||||
textElements.push(newText);
|
||||
|
||||
// Select this text element for immediate dragging
|
||||
selectedTextElement = newText;
|
||||
draggingCanvasContext = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Draw text on canvas
|
||||
ctx.font = newText.font;
|
||||
ctx.fillStyle = newText.color;
|
||||
ctx.fillText(newText.text, newText.x, newText.y);
|
||||
|
||||
// Reset
|
||||
document.getElementById('text-input').value = '';
|
||||
isTextPlacementMode = false;
|
||||
canvas.classList.remove('text-placement-mode');
|
||||
document.querySelector('.canvas-tooltip').style.display = 'block';
|
||||
document.querySelector('.canvas-tooltip').innerText = '鼠标拖动新添加文字可调整位置';
|
||||
}
|
||||
|
||||
function redrawTextElements() {
|
||||
// Redraw all text elements after dithering
|
||||
textElements.forEach(item => {
|
||||
ctx.font = item.font;
|
||||
ctx.fillStyle = item.color;
|
||||
ctx.fillText(item.text, item.x, item.y);
|
||||
});
|
||||
}
|
||||
|
||||
function redrawLineSegments() {
|
||||
// Redraw all line segments after dithering
|
||||
lineSegments.forEach(segment => {
|
||||
ctx.lineJoin = 'round';
|
||||
ctx.lineCap = 'round';
|
||||
ctx.strokeStyle = segment.color;
|
||||
ctx.lineWidth = segment.size;
|
||||
ctx.beginPath();
|
||||
|
||||
if (segment.type === 'dot') {
|
||||
ctx.moveTo(segment.x, segment.y);
|
||||
ctx.lineTo(segment.x+0.1, segment.y+0.1);
|
||||
} else {
|
||||
ctx.moveTo(segment.x1, segment.y1);
|
||||
ctx.lineTo(segment.x2, segment.y2);
|
||||
}
|
||||
|
||||
ctx.stroke();
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize paint functionality when the page loads
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
setTimeout(initPaintTools, 500); // Delay to ensure canvas is initialized
|
||||
});
|
||||
Reference in New Issue
Block a user