Reputation: 504
I want to drag & drop text above image. For that I am using canvas. I am using this code
var c = document.getElementById("canvas");
var ctx1 = c.getContext("2d");
var img = document.getElementById("scream");
ctx1.drawImage(img, 10, 10);
var canvas;
var ctx;
var x = 75;
var y = 50;
var dx = 5;
var dy = 3;
var WIDTH = 400;
var HEIGHT = 300;
var dragok = false,
text = "Hey there im moving!",
textLength = (text.length * 14) / 2;
function rect(x, y, w, h) {
ctx.font = "14px Arial";
ctx.strokeText("Hey there im a moving!!", x, y);
}
function clear() {
ctx.clearRect(0, 0, WIDTH, HEIGHT);
}
function init() {
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
return setInterval(draw, 10);
}
function draw() {
clear();
ctx.fillStyle = "#FAF7F8";
ctx.fillStyle = "#444444";
rect(x - 15, y + 15, textLength, 30);
}
function myMove(e) {
if (dragok) {
x = e.pageX - canvas.offsetLeft;
y = e.pageY - canvas.offsetTop;
}
}
function myDown(e) {
if (e.pageX < x + textLength + canvas.offsetLeft && e.pageX > x - textLength + canvas.offsetLeft && e.pageY < y + 15 + canvas.offsetTop &&
e.pageY > y - 15 + canvas.offsetTop) {
x = e.pageX - canvas.offsetLeft;
y = e.pageY - canvas.offsetTop;
dragok = true;
canvas.onmousemove = myMove;
}
}
function myUp() {
dragok = false;
canvas.onmousemove = null;
}
init();
canvas.onmousedown = myDown;
canvas.onmouseup = myUp;
<img id="scream" src="http://127.0.0.1/demo/images.jpg" alt="The Scream" style="display:none;" width="220" height="277">
<p>Canvas:</p>
<canvas id="canvas" width="300" height="300" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>
I either able to show image or drag & drop text but I want both, please help me where I am wrong. You can check here:- http://jsfiddle.net/FWdSv/11/
Upvotes: 1
Views: 3139
Reputation: 1
I started over, built it up piece by piece, and got my base code to work. If anyone is interested, this project implements a drag-and-drop image upload system, providing users the ability to crop and save their images with a resizable crop box.
Code Overview HTML Structure
Drop Area: The main interactive area where users can drag and drop or manually select an image. It contains a hidden file input () that is triggered when the user clicks a button. Canvas Container: After an image is uploaded, it displays the image on a canvas element for cropping. Control Buttons: There are buttons to save the cropped image and delete the current image preview. CSS Styling (styles.css)
Drop Area Styling: The drop area is styled with a dashed border and highlighted when a file is dragged over it. It also uses flexbox to center its content. Canvas Container & Buttons: The crop area and buttons are hidden initially and displayed only when the user uploads a valid image. The buttons are styled with a green background for saving the image and a red trash icon for deleting it. JavaScript Functionality (app.js)
File Handling: JavaScript handles the drag-and-drop functionality, preventing default browser behavior for drag events and validating the file type (ensuring it's an image). Canvas & Cropping: Once an image is uploaded, it is displayed on an HTML canvas. A crop box is drawn on top, allowing the user to adjust its size or drag it around. The JavaScript code listens for mouse events to allow dragging and resizing of the crop box. Save Cropped Image: The save button crops the image according to the user's crop box and provides a download link for the cropped image in PNG format. Delete Functionality: The delete button clears the canvas and hides the controls, allowing the user to upload another image. Key Features Drag-and-Drop Upload: Users can upload images either by dragging them into the drop area or by selecting them via the file input. Image Cropping: Users can adjust the crop box size and position before saving the cropped image. Canvas Manipulation: The image is drawn on a canvas, allowing custom cropping without reloading or server-side processing. This structure provides a simple and user-friendly way to upload, crop, and download images on a webpage.
let dropArea = document.getElementById('drop-area');
let formContainer = document.getElementsByClassName('form-container')[0];
let fileInput = document.getElementById('fileElem');
const fileSelect = document.getElementById('fileSelect');
const fileElem = document.getElementById('fileElem');
const uploadForm = document.querySelector('.upload-form');
let saveImage = document.getElementById('save-image');
let deleteBtn = document.getElementsByClassName('delete')[0];
let img;
// Get the canvas element and its context
const canvasContainer = document.querySelector('.canvas-container');
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// Set initial crop box dimensions
let isDragging = false;
let cropX, cropY; // X and Y positions of the crop box
let cropWidth = parseInt(canvasContainer.dataset.cropWidth); // Read crop width from data attribute
let cropHeight = parseInt(canvasContainer.dataset.cropHeight); // Read crop height from data attribute
let startX, startY; // Starting mouse coordinates for dragging
let isResizing = false; // Flag to track if resizing is active
let resizeDirection = ''; // Track the direction of resize (e.g., 'right', 'bottom', etc.)
const handleSize = 10; // Size of the resize handles
// Prevent default browser behavior for drag and drop
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
// Highlight drop area when file is dragged over
['dragenter', 'dragover'].forEach(eventName => {
dropArea.addEventListener(eventName, () => {
console.log("entered drop-area");
dropArea.classList.add('highlight');
}, false);
});
// Make sure this is an image file (file type) and if valid show inside div/hide form
function handleFiles(files) {
let file = files[0];
if (file && file.type.startsWith('image/')) {
saveImage.classList.remove('hidden');
deleteBtn.classList.remove('hidden');
canvasContainer.classList.remove('hidden');
formContainer.classList.add('hidden');
previewFile(file);
} else {
alert('Please upload a valid image file.');
// Allow the user to keep the file explorer open
fileInput.value = ''; // Reset the input so the user can try again
}
};
function previewFile(file) {
img = new Image();
img.src = URL.createObjectURL(file);
img.onload = function () {
const originalAspectRatio = img.width / img.height;
let canvasWidth = canvas.width;
let canvasHeight = canvas.height;
// Adjust canvas dimensions based on the aspect ratio of the original image
if (canvasWidth / canvasHeight < originalAspectRatio) {
canvasHeight = canvasWidth / originalAspectRatio; // Scale based on the width
} else {
canvasWidth = canvasHeight * originalAspectRatio; // Scale based on the height
}
// Adjust canvas dimensions and draw
canvas.width = canvasWidth;
canvas.height = canvasHeight;
// Center the crop box horizontally and vertically initially
cropX = (canvas.width - cropWidth) / 2; // Center horizontally
cropY = (canvas.height - cropHeight) / 2; // Center vertically
// Draw image and crop box
drawCanvas();
};
};
function drawCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw the image
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
// Draw overlay and crop box
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
ctx.fillRect(0, 0, canvas.width, cropY);
ctx.fillRect(0, cropY + cropHeight, canvas.width, canvas.height - cropY - cropHeight);
ctx.fillRect(0, cropY, cropX, cropHeight);
ctx.fillRect(cropX + cropWidth, cropY, canvas.width - cropX - cropWidth, cropHeight);
// Draw the crop box outline
ctx.strokeStyle = '#ffffff';
ctx.lineWidth = 2;
ctx.strokeRect(cropX, cropY, cropWidth, cropHeight);
// Draw resize handles
ctx.fillStyle = '#ff0000'; // Red color for handles
drawHandle(cropX, cropY); // Top-left
drawHandle(cropX + cropWidth, cropY); // Top-right
drawHandle(cropX, cropY + cropHeight); // Bottom-left
drawHandle(cropX + cropWidth, cropY + cropHeight); // Bottom-right
}
function drawHandle(x, y) {
ctx.fillRect(x - handleSize / 2, y - handleSize / 2, handleSize, handleSize);
}
// Function to resize the crop box
function resizeCropBox(mouseX, mouseY) {
if (resizeDirection === 'top-left') {
cropWidth += cropX - mouseX;
cropHeight += cropY - mouseY;
cropX = mouseX;
cropY = mouseY;
} else if (resizeDirection === 'top-right') {
cropWidth = mouseX - cropX;
cropHeight += cropY - mouseY;
cropY = mouseY;
} else if (resizeDirection === 'bottom-left') {
cropWidth += cropX - mouseX;
cropX = mouseX;
cropHeight = mouseY - cropY;
} else if (resizeDirection === 'bottom-right') {
cropWidth = mouseX - cropX;
cropHeight = mouseY - cropY;
}
// Ensure the crop box doesn't shrink below a minimum size
cropWidth = Math.max(handleSize, cropWidth);
cropHeight = Math.max(handleSize, cropHeight);
}
// Remove highlight when file is dragged away or dropped
['dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, () => {
dropArea.classList.remove('highlight');
}, false);
});
// Handle dropped files (uses Drag and Drop API)
dropArea.addEventListener('drop', (e) => {
let dt = e.dataTransfer;
let files = dt.files;
handleFiles(files);
}, false);
// Fallback to file input
document.getElementById('fileSelect').onclick = function () {
fileInput.click();
};
// Detect delete button click
deleteBtn.addEventListener('click', function () {
console.log("Delete Me"); // Will ultimately delete from server
ctx.clearRect(0, 0, canvas.width, canvas.height);
saveImage.classList.add('hidden');
deleteBtn.classList.add('hidden');
canvasContainer.classList.add('hidden');
formContainer.classList.remove('hidden');
})
// Detect if the mouse is near an edge or corner for resizing
canvas.addEventListener('mousemove', function (e) {
const mouseX = e.offsetX;
const mouseY = e.offsetY;
if (isResizing) {
resizeCropBox(mouseX, mouseY);
drawCanvas();
return;
}
// Check if inside the crop box (change to grab cursor)
if (mouseX > cropX && mouseX < cropX + cropWidth && mouseY > cropY && mouseY < cropY + cropHeight) {
canvas.style.cursor = 'grab'; // Change cursor to grab inside the crop box
} else {
canvas.style.cursor = 'default'; // Reset cursor outside the crop box
}
// Check if near any of the crop box edges or corners
if (mouseX > cropX - handleSize && mouseX < cropX + handleSize &&
mouseY > cropY - handleSize && mouseY < cropY + handleSize) {
// Top-left corner
canvas.style.cursor = 'nwse-resize';
resizeDirection = 'top-left';
} else if (mouseX > cropX + cropWidth - handleSize && mouseX < cropX + cropWidth + handleSize &&
mouseY > cropY - handleSize && mouseY < cropY + handleSize) {
// Top-right corner
canvas.style.cursor = 'nesw-resize';
resizeDirection = 'top-right';
} else if (mouseX > cropX - handleSize && mouseX < cropX + handleSize &&
mouseY > cropY + cropHeight - handleSize && mouseY < cropY + cropHeight + handleSize) {
// Bottom-left corner
canvas.style.cursor = 'nesw-resize';
resizeDirection = 'bottom-left';
} else if (mouseX > cropX + cropWidth - handleSize && mouseX < cropX + cropWidth + handleSize &&
mouseY > cropY + cropHeight - handleSize && mouseY < cropY + cropHeight + handleSize) {
// Bottom-right corner
canvas.style.cursor = 'nwse-resize';
resizeDirection = 'bottom-right';
} else {
resizeDirection = '';
}
// Handle dragging logic if isDragging is true
if (isDragging) {
cropX = e.offsetX - startX;
cropY = e.offsetY - startY;
// Prevent the crop box from going out of bounds
cropX = Math.max(0, Math.min(cropX, canvas.width - cropWidth));
cropY = Math.max(0, Math.min(cropY, canvas.height - cropHeight));
drawCanvas();
}
});
// Mouse down event: Start dragging or resizing
canvas.addEventListener('mousedown', function (e) {
const mouseX = e.offsetX;
const mouseY = e.offsetY;
if (resizeDirection) {
isResizing = true;
} else if (mouseX > cropX && mouseX < cropX + cropWidth && mouseY > cropY && mouseY < cropY + cropHeight) {
isDragging = true;
startX = mouseX - cropX;
startY = mouseY - cropY;
}
});
// Mouse up event: Stop dragging or resizing
canvas.addEventListener('mouseup', function () {
isDragging = false;
isResizing = false;
canvas.style.cursor = 'default';
resizeDirection = '';
});
// Save the cropped image
saveImage.addEventListener('click', function () {
// Create a new canvas to draw the cropped portion of the image (without the outline or overlay)
const croppedCanvas = document.createElement('canvas');
const croppedCtx = croppedCanvas.getContext('2d');
// Set the size of the new canvas to the crop box size
croppedCanvas.width = cropWidth;
croppedCanvas.height = cropHeight;
// Calculate the scaling ratio between the original image and canvas
const scaleX = img.width / canvas.width;
const scaleY = img.height / canvas.height;
// Calculate the exact position and size of the crop area from the original image
const sourceX = cropX * scaleX;
const sourceY = cropY * scaleY;
const sourceWidth = cropWidth * scaleX;
const sourceHeight = cropHeight * scaleY;
// Draw the cropped portion of the original image onto the new canvas
croppedCtx.drawImage(img, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, cropWidth, cropHeight);
// Convert the cropped image to a data URL and trigger download
const croppedImage = croppedCanvas.toDataURL('image/png');
const link = document.createElement('a');
link.href = croppedImage;
link.download = 'cropped-image.png';
link.click(); // Will ultimately save on server
console.log("Cropped Image Saved");
});
:root {
--drop-area-width: 600px;
--btn-color: #4caf50;
}
div.main-container {
margin: 50px 0 0 0;
text-align: center;
width: 80%;
}
#drop-area {
border: 2px dashed #ccc;
border-radius: 10px;
width: var(--drop-area-width);
height: auto;
font-family: sans-serif;
padding: 50px 25px;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
position: relative;
display: inline-block;
overflow: hidden;
}
#drop-area.highlight {
border: 2px dashed #00ff00; /* Change border to green */
background-color: #f0f8ff; /* Light background color */
}
.canvas-container {
width: 80%;
height: auto;
margin: auto;
}
#save-image {
padding: 0.25rem 0.5rem;
border-radius: 5px;
background-color: var(--btn-color);
border: none;
color: white;
}
.canvas-container.hidden,
.form-container.hidden,
#save-image.hidden,
.delete.hidden {
display: none;
}
.delete {
i.fa.fa-trash {
width: 16px;
height: auto;
color: #d00000;
}
}
#fileSelect {
display: inline-block;
padding: 8px 16px;
background-color: var(--btn-color);
color: white;
border: none;
cursor: pointer;
margin-top: 10px;
}
.file-container-controls {
display: flex;
gap: 50px;
justify-content: end;
align-items: center;
position: relative;
width: var(--drop-area-width);
margin: auto;
padding: 10px 25px;
}
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="main-container">
<div id="drop-area">
<div class="form-container">
<form class="upload-form">
<p>Drag and drop an image here</p>
<input type="file" id="fileElem" accept="image/*" style="display:none" onchange="handleFiles(this.files)">
<button type="button" id="fileSelect">Select an image</button>
</form>
</div>
<div class="canvas-container hidden" data-crop-width="200" data-crop-height="200">
<!-- <canvas id="canvas" width="500" height="500"></canvas> -->
<canvas id="canvas" width="450" height="500"></canvas>
</div>
</div>
<div class="file-container-controls">
<button id="save-image" class="hidden">Load</button>
<div class="delete hidden"><i class="fa fa-trash" aria-hidden="true"></i></div>
</div>
</div>
<script src="https://kit.fontawesome.com/e1ead10cf7.js" crossorigin="anonymous"></script>
<script src="app.js"></script>
</body>
</html>
Upvotes: 0
Reputation: 1
I believe you missed my point. I am creating a circular mask in canvas, hence the snippet:
I'm not following rect(x-15, y+15, textLength, 30). I don't have such a function and wanting to use and offset determine from my mouse up event, not some fixed value.
Upvotes: 0
Reputation: 105035
When you're clearing the canvas, you're also clearing your image.
So the easy fix is to redraw the image in your draw function:
function draw() {
clear();
ctx.drawImage(img,0,0);
ctx.fillStyle = "#FAF7F8";
ctx.fillStyle = "#444444";
rect(x - 15, y + 15, textLength, 30);
}
Alternatively:
You could display your image underneath your canvas so it's not affected when you clear the canvas.
Upvotes: 2