Angular2 Draw Rectangle on Canvas with Mouse - javascript

I am new to angular2 and canvases and I am trying to create a component that first draws an image onto a canvas and then the user can draw rectangles on that image with their mouse (in a click and drag fashion). This is what I have so far which almost works as intended.
#Component({
selector:'app-annotation',
template:`
<canvas #myCanvas width="400" height="400" style="background:lightgray;" (mousedown)="mdEvent($event)" (mouseup)="muEvent($event)" (mousemove)="mmEvent($event)"></canvas>
`
})
export class ObjectDetectionComponent implements OnInit{
startX:number=null;
startY:number=null;
drag=false;
#ViewChild("myCanvas") myCanvas:ElementRef;
mdEvent(e){
//persist starting position
this.startX=e.clientX;
this.startY=e.clientY;
this.drag=true;
}
mmEvent(e){
if(this.drag){
//redraw image on canvas
let base_image = new Image();
base_image.src = 'https://ak3.picdn.net/shutterstock/videos/10826363/thumb/1.jpg';
let context: CanvasRenderingContext2D = this.myCanvas.nativeElement.getContext("2d");
base_image.onload = function(){
context.canvas.height=base_image.height;
context.canvas.width=base_image.width;
context.drawImage(base_image, 0, 0);
};
//draw rectangle on canvas
let x = this.startX - this.myCanvas.nativeElement.getBoundingClientRect().left;
let y= this.startY- this.myCanvas.nativeElement.getBoundingClientRect().top;
let w = e.clientX -this.myCanvas.nativeElement.getBoundingClientRect().left - x;
let h = e.clientY -this.myCanvas.nativeElement.getBoundingClientRect().top - y;
context.setLineDash([6]);
context.strokeRect(x, y, w, h);
}
}
muEvent(e){
//draw final rectangle on canvas
let x = this.startX - this.myCanvas.nativeElement.getBoundingClientRect().left;
let y= this.startY- this.myCanvas.nativeElement.getBoundingClientRect().top;
let w = e.clientX -this.myCanvas.nativeElement.getBoundingClientRect().left - x;
let h = e.clientY -this.myCanvas.nativeElement.getBoundingClientRect().top - y;
this.myCanvas.nativeElement.getContext("2d").setLineDash([6]);
this.myCanvas.nativeElement.getContext("2d").strokeRect(x, y, w, h);
this.drag=false;
}
ngOnInit(){
//draw image on canvas
let base_image = new Image();
base_image.src = 'https://ak3.picdn.net/shutterstock/videos/10826363/thumb/1.jpg';
let context: CanvasRenderingContext2D = this.myCanvas.nativeElement.getContext("2d");
base_image.onload = function(){
context.canvas.height=base_image.height;
context.canvas.width=base_image.width;
context.drawImage(base_image, 0, 0);
}
}
}
The problem here is that when the mouse is clicked and held down, the rectangle only shows if there is mouse movement - but I want the current rectangle to be still be shown as long as the mouse is still clicked. This is sort of unexpected since in mmEvent() the rectangle is drawn after the image is redrawn so I would expect the rectangle to be the last thing drawn after any mouse move event - however maybe the base_image.onload() callback is messing with the timing of that. Any suggestions on fixing this issue?
UPDATE:
drawing the rectangle inside the callback seems to fix it
mmEvent(e) {
if (this.drag) {
//redraw image on canvas
let base_image = new Image();
base_image.src = 'https://ak3.picdn.net/shutterstock/videos/10826363/thumb/1.jpg';
let context: CanvasRenderingContext2D = this.myCanvas.nativeElement.getContext("2d");
let sx = this.startX;
let sy = this.startY;
let canvasTop = this.myCanvas.nativeElement.getBoundingClientRect().top;
let canvasLeft = this.myCanvas.nativeElement.getBoundingClientRect().left;
base_image.onload = function () {
context.canvas.height = base_image.height;
context.canvas.width = base_image.width;
context.drawImage(base_image, 0, 0);
//draw rectangle on canvas
let x = sx - canvasLeft;
let y = sy - canvasTop;
let w = e.clientX - canvasLeft - x;
let h = e.clientY - canvasTop - y;
context.setLineDash([6]);
context.strokeRect(x, y, w, h);
};
}
}

Related

I'm making a drawing based web-app and I'm using HTML Canvas to handle the drawing, however the drawing is offset to the right a lot?

const map = document.getElementById('map')
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
const grid = document.getElementById('grid')
function resize() {
canvas.width = map.offsetWidth
canvas.height = map.offsetHeight
ctx.width = map.offsetWidth
ctx.height = map.offsetHeight
}
resize();
grid.appendChild(canvas)
canvas.style.gridColumn = 2
canvas.style.gridRow = 1
let pos = { x: 0, y: 0 };
window.addEventListener('resize', resize);
document.addEventListener('mousemove', draw);
document.addEventListener('mousedown', setPosition);
document.addEventListener('mouseenter', setPosition);
function setPosition(e) {
pos.x = e.clientX;
pos.y = e.clientY;
}
function draw(e) {
if (e.buttons !== 1) return;
ctx.beginPath();
ctx.lineWidth = 5;
ctx.lineCap = 'round';
ctx.strokeStyle = '#c0392b';
ctx.moveTo(pos.x, pos.y);
setPosition(e);
ctx.lineTo(pos.x, pos.y);
ctx.stroke();
}
Heres the code that generates the canvas relative to the size of a picture and allows the user to draw on the canvas. I've looked over another StackOverflow post with the same problem but no relevant answers. I know that the cause of the problem is that the canvas is stretched from it's standard proportion of 300 x 150 and is drawing at the correct position mathematically but not physically. How do I fix this?

Apply grayscale and sepia filters on mousemove - canvas

I am trying to apply grayscale and sepia filters on canvas at the time of mouseMove.
Am using CanvasRenderingContext2D.filter for applying the filters.
Here's the sample code
var radgrad = this.ctx.createRadialGradient(x, y, 50 / 8, x, y, 50 / 2);
radgrad.addColorStop(0, 'rgb(0, 0, 0)');
radgrad.addColorStop(1, 'rgb(0, 0, 0, 1)');
this.ctx.filter = "grayscale(100%) blur(5px) opacity(50%)";
this.ctx.fillStyle = radgrad;
this.ctx.beginPath();
this.ctx.arc(x, y, 50, 0, Math.PI * 2);
this.ctx.fill();
Problem is when I am trying to apply grayscale am not able to achieve it but the blur(5px) is getting applied.
Any solution how to apply grayscale or sepia filter in the above method.
Here's a sample fiddle
Any lead on the solution will be helpful. Thanks
I am not too clear as to what you want, so I'll assume you want something cumulative, as in moving over the same position twice will apply the filter twice.
To do this, the easiest is to create a CanvasPattern from your image. This way you'll be able to fill sub-path using that image as fillStyle, and in the mean time apply your filters on this new drawing:
const img = new Image();
img.src = "https://upload.wikimedia.org/wikipedia/commons/thumb/5/58/Sunset_2007-1.jpg/1024px-Sunset_2007-1.jpg";
img.onload = begin;
const canvas = document.getElementById( 'canvas' );
const ctx = canvas.getContext( '2d' );
const rad = 25;
function begin() {
canvas.width = img.width;
canvas.height = img.height;
// first draw the original image
ctx.drawImage( img, 0, 0 );
// create a CanvasPattern from it
const patt = ctx.createPattern(img, 'no-repeat');
// set the fillStyle to this pattern
ctx.fillStyle = patt;
// and the filter
ctx.filter = "grayscale(100%) blur(5px) opacity(50%)";
// now at each mousemove
document.onmousemove = e => {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// we just draw a new arc
ctx.beginPath();
ctx.arc( x, y, rad, 0, Math.PI * 2 );
// this will use the filtered pattern
ctx.fill();
};
}
<canvas id="canvas"></canvas>
In case you didn't want it to be cumulative (like a scratch-card), then you could create a single big sub-path and redraw everything at every frame.
const img = new Image();
img.src = "https://upload.wikimedia.org/wikipedia/commons/thumb/5/58/Sunset_2007-1.jpg/1024px-Sunset_2007-1.jpg";
img.onload = begin;
const canvas = document.getElementById( 'canvas' );
const ctx = canvas.getContext( '2d' );
const rad = 25;
const points = [];
const filter = "grayscale(100%) blur(5px) opacity(50%)";
function begin() {
canvas.width = img.width;
canvas.height = img.height;
const patt = ctx.createPattern(img, 'no-repeat');
ctx.fillStyle = patt;
draw();
// now at each mousemove
document.onmousemove = e => {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// store that point
points.push( { x, y } );
// and redraw
draw();
};
}
function draw() {
// remove the filter
ctx.filter = "none";
// so we can draw the background untouched
ctx.drawImage( img, 0, 0 );
// we'll now compose a big sub-path
ctx.beginPath();
points.forEach( ({ x, y }) => {
ctx.moveTo( x, y );
ctx.arc( x, y, rad, 0, Math.PI * 2 )
});
// with the filter
ctx.filter = filter;
ctx.fill();
}
<canvas id="canvas"></canvas>
Note that this code assumes you are on a modern browser which does throttle the mouse events to frame rate. If you are targetting older browsers, you may need to do it yourself.

HTML Canvas making lines using mouseover event

I am trying to write a code using HTML canvas that will create a line beginning where a mousemove event occurs. The line has a defined direction and should continue extending until it is off the screen. The issue I am having is that every time I move the mouse a new line begins(this is good) but the previous line stops extending. I believe that the issue is because each new line is taking on a set of parameters with the same name as the previous line, however I am not certain that this is the issue, nor do I know how to fix it.
Here is a jsfiddle of my current code: https://jsfiddle.net/tdammon/bf8xdyzL/
I start be creating an object named mouse that takes an x and y parameter. The xbeg and ybeg will be the starting coordinates for my lines.
let canvas = document.querySelector('canvas');
canvas.width = window.innerWidth;
canvas.height= window.innerHeight;
let c = canvas.getContext('2d');
let mouse ={
x:undefined,
y:undefined,
}
window.addEventListener("mousemove",function(event){
mouse.x = event.x;
mouse.y = event.y;
xbeg = mouse.x;
ybeg = mouse.y;
})
Next I create an animate function that continuously calls itself. I create a new line object which will take the xbeg and ybeg parameters for beginning points and xbeg+10 and ybeg+10 as ending point. The function then increments xbeg and ybeg. I would like this function to create new lines that do not stop extending whenever the mouse is moved.
function animate() {
requestAnimationFrame(animate);
new Line(xbeg,ybeg,xbeg+10,ybeg+10)
c.beginPath();
c.moveTo(xbeg,ybeg);
c.lineTo(xbeg+10,ybeg+10);
c.stroke();
xbeg += 1;
ybeg += 1;
}
I've added to your code an array for all your lines: let linesRy = []; and I've changed a bit your draw() function by adding this.endx++; this.endy++;
also I'm using your commented out c.clearRect(0, 0, innerWidth, innerHeight);since with every frame you redraw all the lines.
I hope this is what you need.
let linesRy = [];
let canvas = document.querySelector("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let c = canvas.getContext("2d");
let mouse = {
x: undefined,
y: undefined
};
let xbeg, ybeg;
window.addEventListener("mousemove", function(event) {
mouse.x = event.x;
mouse.y = event.y;
xbeg = mouse.x;
ybeg = mouse.y;
});
class Line {
constructor(begx, begy, endx, endy, dx, dy, slope) {
this.begx = begx;
this.begy = begy;
this.endx = endx;
this.endy = endy;
this.dx = endx - begx;
this.dy = endy - begy;
this.slope = dy / dx;
}
draw() {
this.endx++;
this.endy++;
c.beginPath();
c.moveTo(this.begx, this.begy);
c.lineTo(this.endx, this.endy);
c.stroke();
}
}
//let xend = 420;
//let yend = 220;
function animate() {
requestAnimationFrame(animate);
c.clearRect(0, 0, innerWidth, innerHeight);
linesRy.push(new Line(xbeg, ybeg, xbeg + 10, ybeg + 10, 10, 10, 1));
linesRy.forEach(l => {
l.draw();
});
}
animate();
canvas{border:1px solid;}
<canvas></canvas>
the variable c is taken local variable
function animate() {
c = canvas.getContext('2d');
requestAnimationFrame(animate);
new Line(xbeg,ybeg,xbeg+10,ybeg+10)
c.beginPath();
c.moveTo(xbeg,ybeg);
c.lineTo(xbeg+10,ybeg+10);
c.stroke();
xbeg += 1;
ybeg += 1;
}

Dragging points dynamically content in canvas

I have an application where I need to move the points I created to a certain part of the canvas. How can I do it?
$("#canvas").click(function(e){
getPosition(e);
});
var pointSize = 1;
function getPosition(event){
var rect = canvas.getBoundingClientRect();
var x = event.clientX - rect.left;
var y = event.clientY - rect.top;
console.log(x,y)
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();
}
http://jsfiddle.net/bfe8160h/
There's no easy way to drag drawn things on canvas, so you're gonna have to store all points' positions, and redraw the whole canvas.
Something like this:
var points = []
var drag_point = -1
$("#canvas").mousedown(function(e){
var pos = getPosition(e)
drag_point = getPointAt(pos.x,pos.y)
if(drag_point==-1){ // no point at this position, add new point
drawCoordinates(pos.x,pos.y)
points.push(pos)
}
});
$("#canvas").mousemove(function(e){
if(drag_point!=-1){ // if currently dragging a point...
var pos = getPosition(e)
//...update points position...
points[drag_point].x = pos.x
points[drag_point].y = pos.y
redraw() // ... and redraw canvas
}
});
$("#canvas").mouseup(function(e){
drag_point = -1
});
function getPointAt(x,y){
for(var i=0;i<points.length;i++){
if(Math.abs(points[i].x-x)<pointSize && Math.abs(points[i].y-y)<pointSize) // check if x,y is inside points bounding box. replace with pythagoras theorem if you like.
return i
}
return -1 // no point at x,y
}
function redraw(){
var canvas = document.getElementById("canvas")
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height); // clear canvas
for(var i=0;i<points.length;i++){ // draw all points again
drawCoordinates(points[i].x,points[i].y)
}
}
function getPosition(event){
var rect = canvas.getBoundingClientRect();
var x = event.clientX - rect.left;
var y = event.clientY - rect.top;
return {x:x,y: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();
}
import
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
and use jquery to drag your div..Example:
$('#divname').draggable();

myContext.clearRect(0, 0, 500, 700); Not Clearing Canvas Correctly

I have an HTML5 Canvas that can be drawn on with the mouse. I would like to be able to clear the canvas so the user can make a new drawing. I do this with:
myContext.clearRect(0, 0, 500, 700);
The canvas appears clear but as soon as the user begins a new drawing the old drawing reappears. My JavaScript for the mouse drawing part is:
// Variables
var x1;
var y1;
var isPressed = false;
var myCanvas;
var myContext;
function startCanvas() {
// Canvas stuff
myCanvas = document.getElementById("can1");
myContext = myCanvas.getContext("2d");
// Specify a black background, and white lines that are 3 pixels thick.
myContext.fillStyle = '#000000';
myContext.strokeStyle = '#000000';
myContext.fillRect(0, 0, 500, 700);
myContext.lineWidth = 3;
myContext.fill();
}
function functionMouseDown(e) {
// Get coordinates
x1 = e.clientX - myCanvas.offsetLeft;
y1 = e.clientY - myCanvas.offsetTop;
isPressed = true;
}
function functionMouseMove(e) {
// If mouse is down and moved start drawing line
if (isPressed == true) {
drawLine(e);
}
}
function functionMouseUp() {
// Stop drawing line
isPressed = false;
}
function drawLine(e) {
// Draw line
var x = e.clientX - myCanvas.offsetLeft;
var y = e.clientY - myCanvas.offsetTop;
myContext.strokeStyle = '#ffffff';
myContext.lineWidth = 1;
myContext.moveTo(x1, y1);
myContext.lineTo(x, y);
myContext.stroke();
// Set start coordinates to current coordinates
x1 = x;
y1 = y;
}
startCanvas();
The HTML is:
<canvas id="can1" width="500" height="700"></canvas>
myContext.strokeStyle = '#000000';
myContext.beginPath();//<---- add this and read about this.
myContext.fillRect(0, 0, 500, 700);
myContext.lineWidth = 3; //why?
myContext.fill();

Categories