How to move canvas elements? - javascript

How to move first element with black fill to mouse cursor? Or just how to move it to my position in event handler?
Javascript:
var drawing = document.getElementById('canvas');
var ctx = drawing.getContext('2d');
ctx.fillStyle = 'black';
ctx.fillRect(188, 50, 200, 100);
ctx.fillStyle = 'yellow';
ctx.fillRect(0, 0, 200, 100);
document.onmousemove = function(e) {
/* How to move rectangle here? */
}
http://jsfiddle.net/9545qbo4/1/
Thanks in advance.

I think it is not possible on canvas, but your function can do it like this:
ctx.clearRect(/* Old rect position */);
ctx.fillRect(/* New rect position*/);
EDIT: The same question is here.

Related

canvas animation unintentionally drawn using multiple imgdata

I have a div that contains an img tag and a canvas. When I drag an image into the div, the img.src changes, the new picture appears, and in the canvas, a pointillization animation using the data from the img tag is drawn.
When I drag and drop imgA into the div, imgA appears in the img tag, and the canvas does the animation using imgA, which is what I want.
The problem is here:
From this point, if I drag and drop imgB into the div, imgB appears in the img tag, BUT, the canvas animation uses imgA + imgB data.
I think what I did is this:
I drag and drop an image into the div.
The img.src changes
Once theimg is loaded, i resize and clear the canvas
I draw the new img on the canvas
Function pointillize takes the new canvas data and uses it in the animation which it draws onto the canvas
I thought it had something to do with clearRect() and I tried implementing some of the suggestions but I don't think it's that. I added a click event listener to the document and it does indeed clear the canvas.
Could it have something to do with setInterval? I'm just thinking now that maybe, when the new image is loaded it triggers setInterval to run concurrently to the previous setInterval()? Maybe the solution is to kill all previous setInterval functions upon loading a new img?
EDIT: It works perfectly now. I declared draw a global variable, and inside function pointillize() I clearInterval(draw) before I set draw = setInterval(). This wipes the setInterval function clean before I start a new one.
html:
<div id='container'>
<img id='output' src='file.jpg'/>
</div>
javascript
var container, output;
var c, ctx, draw; // draw is declared here as a global variable
window.onload = function(e) {
c = document.createElement('canvas');
ctx = c.getContext('2d');
document.getElementById('container').appendChild(c);
output = document.getElementById('output');
output.width = '400';
container = document.getElementById('container');
container.style.border = '2px dashed rgb(200, 200, 200)';
container.style.padding = '6px';
container.addEventListener('dragenter', function(e) {
e.stopPropagation();
e.preventDefault();
this.style.border = '2px dashed rgb(50, 50, 50)';
}, false);
container.addEventListener('dragover', function(e) {
e.stopPropagation();
e.preventDefault();
this.style.border = '2px dashed rgb(50, 50, 50)';
}, false);
container.addEventListener('dragleave', function(e) {
e.stopPropagation();
e.preventDefault();
this.style.border = '2px dashed rgb(200, 200, 200)';
}, false);
container.addEventListener('drop', function(e) {
e.stopPropagation();
e.preventDefault();
this.style.border = '2px dashed rgb(200, 200, 200)';
var reader = new FileReader();
reader.addEventListener('load', function(e) {
output.src = e.target.result;
// once the new src img is loaded
output.onload = function() {
c.width = output.width;
c.height = output.height;
ctx.clearRect(0, 0, c.width, c.height);
ctx.drawImage(output, 0, 0, output.width, output.height);
pointillize(ctx);
}
}, false);
reader.readAsDataURL(e.dataTransfer.files[0]); // this line!
}, false);
}
function pointillize(context) {
clearInterval(draw); // before a new animation, clear setInterval
// get new image data
var imgData = context.getImageData(0, 0, c.width, c.height);
// clear canvas
ctx.beginPath();
ctx.rect(0, 0, output.width, output.height);
ctx.fillStyle = 'rgba(255, 255, 255, 1)';
ctx.fill();
// store the setInterval in a variable so it can be cleared
var draw = setInterval(function() {
for (var i = 0; i < 100; i++) {
// pick random integers x and y
var y = Math.floor(Math.random()*output.height);
var x = Math.floor(Math.random()*output.width);
// get its position in the array
var loc = (y*output.width + x)*4;
// alpha in rgba goes from 0 to 1
var r = imgData.data[loc];
var g = imgData.data[loc+1];
var b = imgData.data[loc+2];
var a = 0.5;
ctx.beginPath();
ctx.arc(x, y, 2, 0, 2*Math.PI);
ctx.fillStyle = 'rgb(' + r + ',' + g + ', ' + b + ',' + a + ')';
ctx.fill()
}
}, 50); // closing setInterval()
}
document.addEventListener('click', clearCanvas, false);
function clearCanvas() {
//interesting, so clearRect is working, for one frame...
ctx.clearRect(0, 0, c.width, c.height);
}

HTML Canvas: ctx.stroke restroke behaviour with transparent colors

I am working on a sketch tool with html canvas.
I am using a common algorithm for this, that uses the mousedown, mousemove, mouseup events.
mousedown
I beginPath(), and moveTo(// the mouse coordinates).
mousemove
I draw lineTo(// the mouse coordinates), and then stoke(// the line to render it)
mouseup
I do nothing, // closePath()
And I noticed that, calling the stroke method without first calling closePath or beginPath, will redraw or restroke all previous paths or lines, which makes them appear thicker than the define color.
without a transparent color its is barely noticeable, but the colors do appear thicker than they should be.
but with color with transparency|alpha e.g. rgba(). The most recent path or line respects the color's transparency, but all previous line due to the redraw, the transparent colored line overlap and that causes previous lines to get thicker in sequence or succession.
is there a way to avoid|prevent this behavior. thank in advance.
sample is below, try drawing really fast!
var cvs = document.querySelector("canvas");
cvs.width = cvs.parentElement.clientWidth;
var colorInput = document.querySelector("input");
var ctx = cvs.getContext("2d");
ctx.strokeStyle = "rgba(0, 0, 0, 0.4)"
ctx.lineWidth = 20;
onDraw(cvs, {
penDown: function(e) {
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
ctx.strokeStyle = colorInput.value;
ctx.beginPath();
ctx.moveTo(x, y);
},
penMove: function(e) {
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
ctx.lineTo(x, y);
ctx.stroke();
},
penUp: function() {
// ctx.closePath;
}
});
function onDraw(node, drawHandler, beginHandler, endHandler, outOfBoundHandler, sticky) {
var mouseDown = false, mouseOut = false;
if( typeof drawHandler === "object" ) {
var drawEvents = drawHandler;
drawHandler = get(drawEvents.penMove);
beginHandler = get(drawEvents.penDown);
endHandler = get(drawEvents.penUp);
outOfBoundHandler = get(drawEvents.penOff);
sticky = drawEvents.sticky;
}
function get(name) {
return typeof name === "string" ? drawEvents[ name ] : name;
}
node.addEventListener('mousedown', function(e) {
mouseDown = true;
beginHandler&&beginHandler.call(this, e);
});
node.addEventListener('mousemove', function(e) {
mouseDown&&drawHandler&&drawHandler.call(this, e);
});
node.addEventListener('mouseup', function(e) {
mouseDown = false;
endHandler&&endHandler.call(this, e);
});
node.addEventListener('mouseout', function(e) {
mouseDown&&outOfBoundHandler&&outOfBoundHandler.call(this, e);
if( !sticky ) {
mouseDown = false;
}
});
}
.wrapper { border: 1px solid #aaa }
<div class="wrapper">
<canvas border="1" width="600" hieght="400">Canvas is not supported</canvas>
<input type="text" value="rgba(0, 0, 0, 0.3)" placeholder="rgba(#, #, #, #)">
</div>
If no Path argument is passed to stroke and fill methods they will use the path currently being declared with the context's drawing methods.
const ctx = c.getContext('2d');
// starts Path declaration
ctx.moveTo(20, 20);
ctx.lineTo(30, 80);
ctx.stroke(); // first rendering
setTimeout(() => {
ctx.clearRect(0, 0, 300, 150); // even if we clear the canvas
ctx.lineTo(70, 20); // this will continue path declaration
setTimeout(() => {
ctx.stroke(); // and this will draw everything
}, 1000);
}, 1000);
<canvas id="c"></canvas>
The only ways to start a new path declaration (except for the first one) are to either reset the whole context (not good), or to use beginPath method.
const ctx = c.getContext('2d');
// starts Path declaration
ctx.moveTo(20, 20);
ctx.lineTo(30, 80);
ctx.stroke(); // first rendering
setTimeout(() => {
ctx.clearRect(0, 0, 300, 150);
ctx.beginPath(); // start a new Path declaration
ctx.moveTo(30, 80); // we need to move to the previous coords
ctx.lineTo(70, 20); // this will be alone
ctx.stroke(); // and this will draw only the new path
}, 1000);
<canvas id="c"></canvas>
About closePath, it's just a lineTo(last_point_in_current_path_declaration), and doesn't ends a path declaration in no way.
So for your problem, there are two strategies you can adopt :
keep only the last coordinates, and at every mousemove,
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(nextX, nextY);
keep all your coordinates in an array and redraw everything every time
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
coords.forEach(pt => ctx.lineTo(pt.x, pt.y));
ctx.stroke();
Personally, I prefer the second one, which allows some undo - redo, and to e.g change your pen's style.

How to evaluate onclick function on a canvas?

So, be this code, drawing a line in canvas:
var x = document.getElementById("canvas").onclick = function() {
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
context.moveTo(0, 0);
context.lineTo(300, 150);
context.stroke();
}
I want, for example to alert if the line was drawn. How can I do that? I mean something like if(x===true) alert("aa"); I know it doesn't works in js.
document.getElementById("canvas").onclick = function() {
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
context.moveTo(0, 0);
context.lineTo(300, 150);
context.stroke();
alert("aa"); /* here */
}
There is no change() event for <canvas>, so you cannot attach an event handler to the canvas element as you would to a div or to an input element.
However, this question has some other ideas:
https://stackoverflow.com/a/4649358

Take Keyboard Input Into Ember Component

I've created an ember component with a rectangular block inside a green canvas.
What I'm having trouble with is adding a keyboard-input command for A S D W to move the rectangle around the canvas. It's easy enough to do in regular javascript or jquery but inside the component model I'm a bit lost. Any help regarding the function would be very useful.
Linked here is an ember javascript bin: http://emberjs.jsbin.com/miyatoti/1/edit
Here is my present code of the component.
App.BoxGameComponent = Em.Component.extend({
tagName:'canvas',
width: 325,
height: 280,
refresh:30,
attributeBindings:['width', 'height'],
stars:null,
on:false,
build: function(){
var canvas = this.$()[0],
ctx = canvas.getContext('2d'),
shippos = [150, 120],
height = this.get('height'),
width = this.get('width');
this.set('shippos', shippos);
this.set('ctx', ctx);
this.set('on', true);
}.on('didInsertElement'),
kill: function(){
this.set('on', false);
}.on('willDestroyElement'),
clear: function () {
var ctx = this.get('ctx'),
height = this.get('height'),
width = this.get('width');
ctx.fillStyle = 'green';
ctx.clearRect(0, 0, width, height);
ctx.beginPath();
ctx.rect(0, 0, width, height);
ctx.closePath();
ctx.fill();
},
box: function () {
var that = this;
var ctx = this.get('ctx'),
height = this.get('height'),
width = this.get('width'),
shippos = this.get('shippos');
var posx = shippos[0],
posy = shippos[1];
ctx.rect(posx,posy,50,50);
ctx.stroke();
},
game: function(){
if(this.get('on')){
this.loop();
}
}.observes('on'),
loop: function () {
var refreshRate = this.get('refresh');
this.clear();
this.box();
if(this.get('on')){
Em.run.later(this, this.loop, refreshRate);
}
}
});
If anyone can help I've been slamming my brain at this for hours.
Hooking up keyup a canvas element is a bit trickier since the canvas doesn't get focus. So you just hook up to the window (and then destroy it later).
$(window).on('keyup', {self:this}, this.handleKeyUp );
http://emberjs.jsbin.com/miyatoti/2/edit

Tutorial on isPointinPath not copying to my computer

I found this helpful tutorial
http://www.rgraph.net/blog/2013/february/an-example-of-the-html5-canvas-ispointinpath-function.html
i copied it into my own text editor and nothing happens when I open it. I changed it by adding a declaration
<!DOCTYPE html>
<html lang="en">
<head>
<title>exampleMouseOver</title>
</head>
<script>
window.onload = function (e)
{
var canvas = document.getElementById('cvs');
var context = canvas.getContext('2d');
// Draw the rectangle
context.beginPath();
context.rect(50,50,100,100);
context.fill();
context.fillStyle = 'red';
// Draw the circle
context.beginPath();
context.arc(450,175, 50, 0,2 * Math.PI, false);
context.fill();
context.fillStyle = 'green';
// Draw the shape
context.beginPath();
context.moveTo(250,100);
context.lineTo(350,175);
context.lineTo(325,215);
context.lineTo(185,195);
context.fill();
canvas.onmousemove = function (e)
{
var canvas = e.target;
var context = canvas.getContext('2d');
// This gets the mouse coordinates (relative to the canvas)
var mouseXY = RGraph.getMouseXY(e);
var mouseX = mouseXY[0];
var mouseY = mouseXY[1];
// Replay the rectangle path (no need to fill() it) and test it
context.beginPath();
context.rect(50,50,100,100);
if (context.isPointInPath(mouseX, mouseY)) {
canvas.style.cursor = 'pointer';
return;
}
///////////////////////////////////////////////////////////////
// Replay the circle path (no need to fill() it) and test it
context.beginPath();
context.arc(450,175, 50, 0,2 * Math.PI, false);
if (context.isPointInPath(mouseX, mouseY)) {
canvas.style.cursor = 'pointer';
return;
}
///////////////////////////////////////////////////////////////
// Replay the irregular shape path (no need to fill() it) and test it
context.beginPath();
context.moveTo(250,100);
context.lineTo(350,175);
context.lineTo(325,215);
context.lineTo(185,195);
if (context.isPointInPath(mouseX, mouseY)) {
canvas.style.cursor = 'pointer';
return;
}
///////////////////////////////////////////////////////////////
// Return the cursor to the default style
canvas.style.cursor = 'default';
}
}
</script>
</html>
You'll need a body element and a canvas element. Also your script element needs to be inside either your head element or your body element.
The following is what the sample was using, but did not include in their sample code:
<body>
<canvas id="cvs" width="600" height="250" style="border: 1px solid gray; cursor: pointer;">[No canvas support]</canvas>
</body>
Edit: Additionally the code is calling "RGraph.getMouseXY(e)", which is in a library file that you are not referencing. You can either add a reference to that library or get the mouse position yourself.
If you want to use other parts of the RGraph library, for drawing charts, you should add the library. To add the library you should follow the instructions on the RGraph site related to downloading and starting with RGraph (http://www.rgraph.net/docs/starting-with-rgraph.html).
If this was just a sample that happened to do what you wanted to do, you should get the mouse position yourself. You can do this by changing these lines:
var mouseXY = RGraph.getMouseXY(e);
var mouseX = mouseXY[0];
var mouseY = mouseXY[1];
to these:
var mouseX = e.clientX - canvas.getBoundingClientRect().left;
var mouseY = e.clientY - canvas.getBoundingClientRect().top;
This may not be the most robust solution, but it should suffice for your purposes. Essentially you are getting the mouse position in the window, then subtracting the top-left of the canvas in the window, so that you are left with the mouse position in the canvas.

Categories

Resources