Get full ImageData after Panning the image inside Cavnas - javascript

I have a situation where I need to get the full ImageData inside the canvas even after panning the image.
Thanks in Advance
Example in the fiddle: https://jsfiddle.net/z46cvm9h/
<canvas id="canvas"></canvas>
<button onclick="showImage()">
Show panned Image
</button>
<img id="image" src='' />
var context = canvas.getContext("2d");
canvas.width = 400;
canvas.height = 300;
var global = {
scale : 1,
offset : {
x : 0,
y : 0,
},
};
var pan = {
start : {
x : null,
y : null,
},
offset : {
x : 0,
y : 0,
},
};
function draw() {
context.setTransform(1, 0, 0, 1, 0, 0);
context.clearRect(0, 0, canvas.width, canvas.height);
context.translate(pan.offset.x, pan.offset.y);
context.beginPath();
context.rect(50, 50, 100, 100);
context.fillStyle = 'skyblue';
context.fill();
context.beginPath();
context.arc(350, 250, 50, 0, 2 * Math.PI, false);
context.fillStyle = 'green';
context.fill();
}
draw();
canvas.addEventListener("mousedown", startPan);
canvas.addEventListener("mouseleave", endPan);
canvas.addEventListener("mouseup", endPan);
function startPan(e) {
canvas.addEventListener("mousemove", trackMouse);
canvas.addEventListener("mousemove", draw);
pan.start.x = e.clientX;
pan.start.y = e.clientY;
}
function endPan(e) {
canvas.removeEventListener("mousemove", trackMouse);
pan.start.x = null;
pan.start.y = null;
global.offset.x = pan.offset.x;
global.offset.y = pan.offset.y;
}
function trackMouse(e) {
var offsetX = e.clientX - pan.start.x;
var offsetY = e.clientY - pan.start.y;
pan.offset.x = global.offset.x + offsetX;
pan.offset.y = global.offset.y + offsetY;
}
function showImage(){
document.getElementById('image').src = canvas.toDataURL();
}
PS: JSFiddle is for demonstration, I can't put the exact scenario into JSFiddle. In the application, I pan the image and then use the mouse to draw something on the canvas using the mousedown and mousemove events. On the mouseup event, I need to have the ImageData with the full image I drew, but when using getImageData, I am only getting the image appearing on the canvas. The panned image is cropped to the canvas size.

As #Blindman67 suggested, you can make a second canvas.
Create an object to store the furthest away points in your image.
const imagePadding = {
top: 0,
right: 0,
bottom: 0,
left: 0
}
top shall be the highest point of the drawing, right shall be the point furthest to the right, and so on. For demonstration purposes, the padding is set equal to how far you have panned in each direction. You will want to set it equal to the shapes/points of the drawing that are furthest in each direction (up, right, down, and left--those are the directions I am talking about).
function panImage(e) {
pan.x += e.movementX
pan.y += e.movementY
drawPreview()
imagePadding.top = Math.max(imagePadding.top, pan.y)
imagePadding.left = Math.max(imagePadding.left, pan.x)
imagePadding.bottom = Math.max(imagePadding.bottom, -pan.y)
imagePadding.right = Math.max(imagePadding.right, -pan.x)
}
To draw the full image, you can make a function like this:
function drawFinalImage() {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
const pannedImage = document.getElementById('image')
canvas.width = previewCanvas.width + imagePadding.left + imagePadding.right
canvas.height = previewCanvas.height + imagePadding.top + imagePadding.bottom
ctx.translate(imagePadding.left, imagePadding.top)
drawShapes(ctx)
pannedImage.src = canvas.toDataURL()
}
See the JSFiddle for a demo as well as how you can implement the code. Let me know if there is anything I should explain better.
You can use JSFiddle Console to adjust the padding. Type imagePadding.top = 200, press enter, and click the canvas to update the final image.

Related

HTML-5 Canvas Magnifying Glass unable to mouse down/drag and scrolling interferes with image

I am creating a magnifying glass for a Canvas application that is supposed to take a canvas image snapshot and draw it onto a smaller canvas at a larger scale where the cursor coordinates are, but I have run into 2 issues.
When I scroll down, the canvas containing the zoomed canvas/image no longer shows what its over but instead the top of the canvas/image.
I am unable to set up a click event where the user can hold down the left mouse and move the zoom area until mouseup. I have tried mouse down and mouse up event listeners to no avail so for reference i have it on mousemove and mouseleave, which also doesnt work well but needs to be replaced with mouseup instead.
var canvas = document.getElementById("canvas1");
var ctx = canvas.getContext("2d");
var ox = canvas.width / 2;
var oy = canvas.height / 2;
ctx.font = "42px serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "blue";
ctx.fillRect(ox / 2, oy / 2, ox, oy);
function magnify() {
var main = document.getElementById("canvas1");
var ctx = main.getContext('2d')
var base64 = main.toDataURL('image/png', 0);
drawing = new Image();
drawing.onload = () => {
var zoom = document.getElementById("tCanvas");
var zoomCtx = zoom.getContext('2d');
zoomCtx.drawImage(drawing, 0, 0);
}
main.addEventListener("mousemove", function(e) {
var zoom = document.getElementById("tCanvas");
var zoomCtx = zoom.getContext('2d');
zoomCtx.clearRect(0, 0, zoom.width, zoom.height);
zoomCtx.drawImage(main, e.x, e.y, 200, 200, 0, 0, 300, 300);
zoom.style.top = e.pageY - 10 + "px"
zoom.style.left = e.pageX - 10 + "px"
e.pageY = -150
e.pageX = -150
zoom.style.display = "block";
});
main.addEventListener("mouseleave", function() {
var zoom = document.getElementById("tCanvas");
zoom.style.display = "none";
});
drawing.src = base64;
};
<canvas id="tCanvas" class="cgm" height="100" width="100" style="background-color:white; position: absolute; display: none; z-
index:1;border:1px solid red;"> </canvas>
<canvas tabindex=1 class="cgm" id="canvas1" style="position:relative; background:white;
left:0;right:0;margin:auto;z-index:1;margin-top:70px; "></canvas>
<p></p>
<button id="zoom" onclick="magnify();">Zoom</button>
Here's a fiddle for reference (I fixed the height to display the scroll issue).
JSFiddle
I'm going to focus on the part:
make a canvas image snapshot and draw it onto a smaller canvas
at a larger scale where the cursor coordinates are
Your code was doing a lot of stuff with the styles and I have no clue why, your comments do not give me any indication why that was needed, so I removed all that to keep this example as simple as possible
We need to draw so we will use that drawing = new Image() that you have and we need some integration with click I added a new variable drawing = new Image() clicking on the canvas will pause the zoom movement, and clicking again will resume it, that to me was the intuitive way
See my sample code below
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var zoom = document.getElementById("zoom");
var zoomCtx = zoom.getContext('2d');
const z = zoom.width * 5
var pause = false
ctx.fillStyle = "blue";
ctx.fillRect(5, 5, 5, 90);
ctx.fillRect(60, 15, 20, 50);
for (let i = 10; i < 19; i++)
ctx.fillText(i, i * 2, i * 9 - 70);
var drawing = new Image();
drawing.src = canvas.toDataURL('image/png', 0);
canvas.addEventListener("mousemove", (e) => {
if (pause) return
zoomCtx.clearRect(0, 0, zoom.width, zoom.height);
zoomCtx.drawImage(drawing, e.x - 15, e.y - 15, 50, 50, 0, 0, z, z);
});
canvas.addEventListener("mousedown", (e) => {
pause = !pause
});
canvas {
border: solid
}
<canvas id="canvas" height="100" width="100"></canvas>
<canvas id="zoom" height="100" width="100"></canvas>

Remove point on double click on canvas

I have a canvas on which user can draw point on click on any part of it. As one know we must give the user the possibility to undo an action that he as done. This is where I'am stuck, how can I program the codes to allow the user to remove the point on double click on the the point he wants to remove ?
<canvas id="canvas" width="160" height="160" style="cursor:crosshair;"></canvas>
1- Codes to draw the canvas and load an image in it
var canvasOjo1 = document.getElementById('canvas'),
context1 = canvasOjo1.getContext('2d');
ojo1();
function ojo1()
{
base_image1 = new Image();
base_image1.src = 'https://www.admedicall.com.do/admedicall_v1//assets/img/patients-pictures/620236447.jpg';
base_image1.onload = function(){
context1.drawImage(base_image1, 0, 0);
}
}
2- Codes to draw the points
$("#canvas").click(function(e){
getPosition(e);
});
var pointSize = 3;
function getPosition(event){
var rect = canvas.getBoundingClientRect();
var x = event.clientX - rect.left;
var y = event.clientY - rect.top;
drawCoordinates(x,y);
}
function drawCoordinates(x,y){
var ctx = document.getElementById("canvas").getContext("2d");
ctx.fillStyle = "#ff2626"; // Red color
ctx.beginPath();
ctx.arc(x, y, pointSize, 0, Math.PI * 2, true);
ctx.fill();
}
My fiddle :http://jsfiddle.net/xpvt214o/834918/
By hover the mouse over the image we see a cross to create the point.
How can i remove a point if i want to after create it on double click ?
Thank you in advance.
Please read first this answer how to differentiate single click event and double click event because this is a tricky thing.
For the sake of clarity I've simplified your code by removing irrelevant things.
Also please read the comments of my code.
let pointSize = 3;
var points = [];
var timeout = 300;
var clicks = 0;
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let cw = (canvas.width = 160);
let ch = (canvas.height = 160);
function getPosition(event) {
var rect = canvas.getBoundingClientRect();
return {
x: event.clientX - rect.left,
y: event.clientY - rect.top
};
}
function drawCoordinates(point, r) {
ctx.fillStyle = "#ff2626"; // Red color
ctx.beginPath();
ctx.arc(point.x, point.y, r, 0, Math.PI * 2, true);
ctx.fill();
}
canvas.addEventListener("click", function(e) {
clicks++;
var m = getPosition(e);
// this point won't be added to the points array
// it's here only to mark the point on click since otherwise it will appear with a delay equal to the timeout
drawCoordinates(m, pointSize);
if (clicks == 1) {
setTimeout(function() {
if (clicks == 1) {
// on click add a new point to the points array
points.push(m);
} else { // on double click
// 1. check if point in path
for (let i = 0; i < points.length; i++) {
ctx.beginPath();
ctx.arc(points[i].x, points[i].y, pointSize, 0, Math.PI * 2, true);
if (ctx.isPointInPath(m.x, m.y)) {
points.splice(i, 1); // remove the point from the array
break;// if a point is found and removed, break the loop. No need to check any further.
}
}
//clear the canvas
ctx.clearRect(0, 0, cw, ch);
}
points.map(p => {
drawCoordinates(p, pointSize);
});
clicks = 0;
}, timeout);
}
});
body {
background: #20262E;
padding: 20px;
}
<canvas id="canvas" style="cursor:crosshair;border:1px solid white"></canvas>

Fade out effect for line in HTML5 canvas with background image

I'm currently working on an HTML canvas example that fades out a line after it is drawn on a canvas, though my question is: how do I attain this while using a background image?
Live Demo (current code in a link at the bottom of this post)
CSS:
canvas {
background-image: url("https://zgab33vy595fw5zq-zippykid.netdna-ssl.com/wp-content/uploads/2018/02/PluralsightandSO.jpg");
background-size: 100% 100%;
}
JavaScript:
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
painting = false,
lastX = 0,
lastY = 0;
canvas.width = canvas.height = 600;
canvas.onmousedown = function (e) {
if (!painting) {
painting = true;
} else {
painting = false;
}
lastX = e.pageX - this.offsetLeft;
lastY = e.pageY - this.offsetTop;
};
canvas.onmousemove = function (e) {
if (painting) {
mouseX = e.pageX - this.offsetLeft;
mouseY = e.pageY - this.offsetTop;
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(mouseX, mouseY);
ctx.stroke();
lastX = mouseX;
lastY = mouseY;
}
}
function fadeOut() {
ctx.fillStyle = "rgba(255,255,255,0.3)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
setTimeout(fadeOut,100);
}
fadeOut();
I realize fillRect is filling the entire canvas with the fillStyle so I assumed I could add the image to the canvas instead, but as you can tell, the animation is not the same by using this code instead:
var image = new Image();
image.src = "https://zgab33vy595fw5zq-zippykid.netdna-ssl.com/wp-content/uploads/2018/02/PluralsightandSO.jpg"
ctx.drawImage(image, 0, 0);
Current Live Demo (after changes described above) - it's too rigid and doesn't have as long of a tail
Any thoughts?
You're drawing a 30% transparent square to get the 'fading' effect:
ctx.fillStyle = "rgba(255,255,255,0.3)";
So you need to draw the PNG transparently as well
canvas.onmousemove = function (e) {
if (painting) {
// set line alpha to 1
ctx.globalAlpha = 1.0;
// your paint code here
}
}
function fadeOut() {
var image = new Image();
// set image alpha to 0.3
ctx.globalAlpha = 0.3;
// your draw image code here
}
By the way, you don't need to create the new image every time!
Example
You need to specify width and height:
ctx.drawImage(image, 0, 0, 600, 600);

How to rotate canvas in the right way

I have an image which I want to crop.
Thus, I created canvas as overlay with a darker background and then on mouse move I draw an rect on that canvas and in that rect I clear the dark background. That looks as follows:
Thus the area without background will be cropped.
My code is as follows:
var canvas = document.getElementById('canvas');
var img = document.getElementById('photo');
var ctx = canvas.getContext('2d');
var rect = {};
var drag = false;
var update = true; // when true updates canvas
var original_source = img.src;
img.src = original_source;
var deg = 0;
var image_rotate_angle = 90;
document.getElementById('right').addEventListener("click", rotateRight, false);
document.getElementById('left').addEventListener("click", rotateLeft, false);
function rotateRight(){
deg = deg + image_rotate_angle;
img.style.transform = "rotate(" + deg + "deg)";
canvas.style.transform = "rotate(" + deg + "deg)";
}
function rotateLeft(){
deg = deg - image_rotate_angle;
img.style.transform = "rotate(" + deg + "deg)";
canvas.style.transform = "rotate(" + deg + "deg)";
}
function init() {
img.addEventListener('load', function(){
canvas.width = img.width;
canvas.height = img.height;
canvas.addEventListener('mousedown', mouseDown, false);
canvas.addEventListener('mouseup', mouseUp, false);
canvas.addEventListener('mousemove', mouseMove, false);
});
// start the rendering loop
requestAnimationFrame(updateCanvas);
}
// main render loop only updates if update is true
function updateCanvas(){
if(update){
drawCanvas();
update = false;
}
requestAnimationFrame(updateCanvas);
}
// draws a rectangle with rotation
function drawRect(){
ctx.clearRect(rect.startX, rect.startY, rect.w, rect.h);
}
// clears canvas sets filters and draws rectangles
function drawCanvas(){
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'rgba(32, 32, 32, 0.7)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawRect()
}
// create new rect add to array
function mouseDown(e) {
rect = {
startX : e.offsetX,
startY : e.offsetY,
w : 1,
h : 1
};
drag = true;
}
function mouseUp() { drag = false; update = true; }
function mouseMove(e) {
if (drag) {
rect.w = (e.pageX - this.offsetLeft) - rect.startX;
rect.h = (e.pageY - this.offsetTop) - rect.startY;
update = true;
}
}
init();
canvas{
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
display:inline-block;
background:rgba(0,0,0,0.3);
}
<div style="margin-bottom: 15px;">
<button id="left">Rotate Reft</button>
<button id="right">Rotate Right</button>
</div>
<div style="position: relative; overflow: hidden;display:inline-block;">
<img id="photo" src="http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg"/>
<canvas id="canvas"></canvas>
</div>
I have also two buttons which I use to rotate the image. And there I have the problem. The solution is maybe obvious but I do not see it.
If I draw a the rectangle and then rotate the image, I rotate the image and the canvas also, and then rectangle stays at the same place after rotating. That is what I want and that works fine.
The problem is if I first rotate the image and then try to draw a rectangle. Then the rectangle isn't drawn as it should be, it goes in the opposite direction.
Here is the fiddle.
Any idea how to solve it?
You could probably just get the maths to find where the new x and y will lay, based on your image's size, but I guess it will be more manageable to actually draw everything on your canvas, and rotate its transformation matrix.
But this implies some changes in your code:
You would have to draw the image with compositing (or with 'even-odd' fill-rule, like I already shown you).
You would also have to make your drawCanvas function a bit more complicated in order to rotate your hole only when needed (when the user clicked the "rotate" buttons).
var canvas = document.getElementById('canvas');
var img = new Image();
var ctx = canvas.getContext('2d');
var rect = {};
var drag = false;
var update = true; // when true updates canvas
var angle = 0;
document.getElementById('right').addEventListener("click", rotateRight, false);
document.getElementById('left').addEventListener("click", rotateLeft, false);
function rotateRight() {
angle += Math.PI / 2;
drawCanvas(true); // let know we're from rotate function
}
function rotateLeft() {
angle -= Math.PI / 2;
drawCanvas(true);
}
function init() {
img.addEventListener('load', function() {
canvas.width = img.width;
canvas.height = img.height;
canvas.addEventListener('mousedown', mouseDown, false);
canvas.addEventListener('mouseup', mouseUp, false);
canvas.addEventListener('mousemove', mouseMove, false);
});
// start the rendering loop
requestAnimationFrame(updateCanvas);
}
// main render loop only updates if update is true
function updateCanvas() {
if (update) {
drawCanvas(false); // don't rotate the rectangle here
update = false;
}
requestAnimationFrame(updateCanvas);
}
// draws a rectangle with rotation
function drawRect() {
ctx.clearRect(rect.startX, rect.startY, rect.w, rect.h);
}
// clears canvas sets filters and draws rectangles
function drawCanvas(fromRotate) {
// reset the transformation matrix to its defaults
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'rgba(32, 32, 32, 0.7)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// for the rectangle, rotate only if we are using previously set rect
if (fromRotate) {
// Move to center
ctx.setTransform(1, 0, 0, 1, canvas.width / 2, canvas.height / 2);
// rotate by the current angle
ctx.rotate(angle);
// move back to what is now top right corner
ctx.translate(-canvas.width / 2, -canvas.height / 2);
}
drawRect();
// draw the image behind the overlay
ctx.globalCompositeOperation = 'destination-over';
// rotate the whole context every time for the image
ctx.setTransform(1, 0, 0, 1, canvas.width / 2, canvas.height / 2);
// rotate by the current angle
ctx.rotate(angle);
// draw the image, from center
ctx.drawImage(img, -img.width / 2, -img.height / 2);
}
// create new rect add to array
function mouseDown(e) {
rect = {
startX: e.offsetX,
startY: e.offsetY,
w: 1,
h: 1
};
drag = true;
}
function mouseUp() {
drag = false;
update = true;
}
function mouseMove(e) {
if (drag) {
rect.w = (e.offsetX - this.offsetLeft) - rect.startX;
rect.h = (e.offsetY - this.offsetTop) - rect.startY;
update = true;
}
}
init();
img.src = 'https://www.html5canvastutorials.com/demos/assets/darth-vader.jpg';
<div style="margin-bottom: 15px;">
<button id="left">Rotate Reft</button>
<button id="right">Rotate Right</button>
</div>
<div style="position: relative; overflow: hidden;display:inline-block;">
<canvas id="canvas"></canvas>
</div>

select a drawn object in canvas

I have a drawn image in canvas with context.drawImage() and I want to select that image to move it with drag and drop in the same canvas (same as MS Paint selection tool). How can I code that in javascript?
function crop(xStart,yStart,xLast,yLast){
canvas.width = xLast - xStart;
canvas.height = yLast - yStart;
context.clearRect(0, 0, canvas.width,canvas.height); drawFromAux(xStart,yStart,xLast,yLast,0,0);
return canvas.toDataURL();
}
// img is my original image
function select(xStart,yStart,xLast,yLast){
selection.src = crop(xStart,yStart,xLast,yLast);
selection.draggable = "true";
context.clearRect(0, 0, canvas.width,canvas.height);
canvas.width = img.width;
canvas.height = img.height;
context.clearRect(0, 0, canvas.width,canvas.height);
context.drawImage(img, 0, 0);
context.clearRect(xStart, yStart, xLast - xStart,yLast - yStart);
context.drawImage(selection,0,0);
}
Using Canvas.js and Pointer.js that should not be that hard.
Things to do:
create image object containing with x, y and raw image
render it to a canvas
listen for mouse moves and check if mouse button is pressed
simple point collision detection between mouse and image on canvas to be able to "select it" and drag it
Loading Pointer.js and Canvas.js:
<script type="text/javascript" src="https://gustavgenberg.github.io/handy-front-end/Canvas.js"></script>
<script type="text/javascript" src="https://gustavgenberg.github.io/handy-front-end/Pointer.js"></script>
1) Creating an image object is not very hard:
const image = {
image: new Image(),
x: canvas.width / 2 - image.width / 2, // centered in canvas
y: canvas.height / 2 - image.height / 2 // centered in canvas
};
image.image.src = ' <url> ';
2) Render that image to the canvas (using Canvas.js)
const canvas = new Canvas('my-canvas', 500, 500).start();
canvas.on('draw', function ( renderer ) {
renderer.drawImage(image.image, image.x, image.y);
});
3) Listening for mouse moves and moving the image (using Pointer.js)
const pointer = new Pointer( canvas.element );
let moveImage = false;
pointer.on('move', function ( event ) {
if( moveImage ) {
image.x += (event.x - pointer.getMoveHistory(-2).x);
image.y += (event.y - pointer.getMoveHistory(-2).y);
}
});
4) Pointer collision detection (using Pointer.js)
pointer.on('down', function () {
moveImage = pointer.touches({ x: image.x, y: image.y, width: image.image.width, height: image.image.height });
});
pointer.on('up', function () {
moveImage = false;
});
JSFiddle: https://jsfiddle.net/GustavGenberg/3h39b9h1/
Hope this helps you :) !

Categories