Canvas starts to lag after some time - javascript

Reproduction: focus your mouse on canvas , then spin your mouse in circles for ~15 sec. At first you'll notice how things are smooth. After some time it starts to lose its smoothness and becomes really laggy.
Part of the js function came from the following answer
Make moving Rect more smooth
var canvas = document.getElementById('canvas');
var ctx = document.getElementById('canvas').getContext('2d');
var x;
var y;
var tx = tx || 0;
var ty = ty || 0;
var xDir;
var yDir;
function followMouse(e) {
x = e.offsetX;
y = e.offsetY;
moveObject();
}
function moveObject() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
var scale = 0.2 * Math.max(canvas.width, canvas.height);
xDir = 0;
yDir = 0;
xDir = (x - tx) / scale;
yDir = (y - ty) / scale;
tx = tx != x ? tx + xDir : tx;
ty = ty != y ? ty + yDir : ty;
ctx.fillRect(tx - 25, ty + 25, 50, 10);
if (tx != x || ty != y) {
window.requestAnimationFrame(moveObject);
}
}
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
};
canvas.addEventListener('mousemove', _.throttle(function(e) {
followMouse(e);
}, 30));
window.addEventListener('resize', resizeCanvas, false);
resizeCanvas();
html,
body {
margin: 0;
height: 100%;
}
canvas {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<canvas id="canvas"></canvas>

This happens as for each mousemove a new loop is started. These loops will accumulate and eventually slow things down.
To solve you can implement cancelAnimationFrame() by doing:
...
var timer;
function followMouse(e) {
x = e.offsetX;
y = e.offsetY;
cancelAnimationFrame(timer);
moveObject();
}
Then store timer reference in the main loop:
...
timer = requestAnimationFrame(moveObject);
This will abort the current request for frame update and allow you to start a new loop without accumulating calls.
For this reason you would also have to initialize x and y since they are not initialized otherwise until the mouse has been moved (which is of course no guarantee).
var x = 0;
var y = 0;
Note: A side-effect of this correction is that now the movement is only calculated once per frame. When accumulated the movement got calculated many times per frame. To compensate adjust the scale to a lower value (shown below).
Modified example
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var x = 0;
var y = 0;
var tx = tx || 0;
var ty = ty || 0;
var xDir;
var yDir;
var timer;
function followMouse(e) {
x = e.clientX;
y = e.clientY;
cancelAnimationFrame(timer);
moveObject();
}
function moveObject() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
var scale = 0.02 * Math.max(canvas.width, canvas.height);
xDir = 0;
yDir = 0;
xDir = (x - tx) / scale;
yDir = (y - ty) / scale;
tx = tx != x ? tx + xDir : tx;
ty = ty != y ? ty + yDir : ty;
ctx.fillRect(tx - 25, ty + 25, 50, 10);
if (tx != x || ty != y) {
timer = requestAnimationFrame(moveObject);
}
}
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
};
canvas.addEventListener('mousemove', _.throttle(function(e) {
followMouse(e);
}, 30));
window.addEventListener('resize', resizeCanvas, false);
resizeCanvas();
html,
body {
margin: 0;
height: 100%;
}
canvas {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<canvas id="canvas"></canvas>

Related

How to get coordinates of html canvas tag

I want to get coordinates of the painted area of an image placed in an HTML canvas tag and send it to the database. And populate it on the same area of that image in another page. And how can I reset or clear the painted area by clicking the reset button?
JSfiddle example
var canvas = document.getElementById("canvas");
var img = document.getElementById("imagearea"),
ctx = canvas.getContext("2d"),
painting = false,
lastX = 0,
lastY = 0,
lineThickness = 1;
canvas.width = canvas.height = 600;
ctx.fillRect(0, 0, 600, 600);
ctx.drawImage(img, 10, 10);
canvas.onmousedown = function(e) {
painting = true;
ctx.fillStyle = "#ff0000";
lastX = e.pageX - this.offsetLeft;
lastY = e.pageY - this.offsetTop;
};
canvas.onmouseup = function(e){
painting = false;
}
canvas.onmousemove = function(e) {
if (painting) {
mouseX = e.pageX - this.offsetLeft;
mouseY = e.pageY - this.offsetTop;
// find all points between
var x1 = mouseX,
x2 = lastX,
y1 = mouseY,
y2 = lastY;
var steep = (Math.abs(y2 - y1) > Math.abs(x2 - x1));
if (steep){
var x = x1;
x1 = y1;
y1 = x;
var y = y2;
y2 = x2;
x2 = y;
}
if (x1 > x2) {
var x = x1;
x1 = x2;
x2 = x;
var y = y1;
y1 = y2;
y2 = y;
}
var dx = x2 - x1,
dy = Math.abs(y2 - y1),
error = 0,
de = dy / dx,
yStep = -1,
y = y1;
if (y1 < y2) {
yStep = 1;
}
lineThickness = 5 - Math.sqrt((x2 - x1) *(x2-x1) + (y2 - y1) * (y2-y1))/10;
if(lineThickness < 1){
lineThickness = 1;
}
for (var x = x1; x < x2; x++) {
if (steep) {
ctx.fillRect(y, x, lineThickness , lineThickness );
} else {
ctx.fillRect(x, y, lineThickness , lineThickness );
}
error += de;
if (error >= 0.5) {
y += yStep;
error -= 1.0;
}
}
lastX = mouseX;
lastY = mouseY;
}
}
<canvas id="canvas">
</canvas>
<img id="imagearea" src="https://media.istockphoto.com/photos/green-apple-with-leaf-and-cut-isolated-on-white-picture-id1141004606?k=6&m=1141004606&s=170667a&w=0&h=zwbN4lLc7MFb6f_aZ4npNL3i4Tgde-yINlYTztlI1QQ=" style="display: none;" />
<button> Reset </button>
To achieve what you're looking for, you need to store the min(x, y) coords for the (top, left) position and the max(x, y) coords for the (bottom, right) position, and you can't get the image on the same canvas if you are looking to remove the drawing on the area. use an HTML element with absolute position relative to the canvas for the area frame, attach an event to crop, and display the target area another one to remove it.
Here is a working example with a lot of comments, it should be clear. Click on the area to display the preview on the "x" to remove it with the drawing, it can handle multiple areas.
const source = "https://media.istockphoto.com/photos/green-apple-with-leaf-and-cut-isolated-on-white-picture-id1141004606?k=6&m=1141004606&s=170667a&w=0&h=zwbN4lLc7MFb6f_aZ4npNL3i4Tgde-yINlYTztlI1QQ=";
const container = document.querySelector("#container");
const canvas = container.querySelector("canvas");
const ctx = canvas.getContext("2d");
const resetButton = document.querySelector("button");
let lastDrawnArea = [[Infinity, Infinity], [0, 0]];
let image;
let painting = false;
let lastX = 0;
let lastY = 0;
let lineThickness = 1;
init();
async function init() {
await loadDrawImage();
// Start Event Listening
canvas.onmousedown = function(e) {
painting = true;
ctx.fillStyle = "#ff0000";
lastX = e.pageX - this.offsetLeft;
lastY = e.pageY - this.offsetTop;
};
canvas.onmouseup = function(e){
painting = false;
// Set the drawing area
setDrawingArea();
}
canvas.onmousemove = function(e) {
if (painting) {
mouseX = e.pageX - this.offsetLeft;
mouseY = e.pageY - this.offsetTop;
// find all points between
var x1 = mouseX,
x2 = lastX,
y1 = mouseY,
y2 = lastY;
var steep = (Math.abs(y2 - y1) > Math.abs(x2 - x1));
if (steep){
var x = x1;
x1 = y1;
y1 = x;
var y = y2;
y2 = x2;
x2 = y;
}
if (x1 > x2) {
var x = x1;
x1 = x2;
x2 = x;
var y = y1;
y1 = y2;
y2 = y;
}
var dx = x2 - x1,
dy = Math.abs(y2 - y1),
error = 0,
de = dy / dx,
yStep = -1,
y = y1;
if (y1 < y2) {
yStep = 1;
}
lineThickness = 5 - Math.sqrt((x2 - x1) *(x2-x1) + (y2 - y1) * (y2-y1))/10;
if(lineThickness < 1){
lineThickness = 1;
}
for (var x = x1; x < x2; x++) {
if (steep) {
ctx.fillRect(y, x, lineThickness , lineThickness );
} else {
ctx.fillRect(x, y, lineThickness , lineThickness );
}
error += de;
if (error >= 0.5) {
y += yStep;
error -= 1.0;
}
}
lastX = mouseX;
lastY = mouseY;
// Set The min, max coordinate of the current drawing
// to define the current drawing area
lastDrawnArea = [
[// Top left min([x, y]) coords
Math.min(lastDrawnArea[0][0], mouseX),
Math.min(lastDrawnArea[0][1], mouseY)
],
[// Bottom right max([x, y]) coords
Math.max(lastDrawnArea[1][0], mouseX),
Math.max(lastDrawnArea[1][1], mouseY)
]
]
}
}
}
async function loadDrawImage() {
image = new Image();
// Load the image
await new Promise(resolve => {
image.onload = resolve;
image.src = source;
});
const [width, height] = [image.naturalWidth, image.naturalHeight];
// Set the container and canvas size
container.style.width = `${width}px`;
container.style.height = `${height}px`;
canvas.width = width;
canvas.height = height;
// Set the container in the background
container.style.background = `url(${image.src})`;
}
function setDrawingArea(){
const [TOP_LEFT, BOTTOM_RIGHT, X, Y] = [0, 1, 0, 1];
const container = document.querySelector("#container");
const template = document.querySelector("#areaTemplate");
const area = template.content.firstElementChild.cloneNode(true);
// You should replace this with the lineThickness
const offset = 10;
// Get the area size
const width = lastDrawnArea[BOTTOM_RIGHT][X] - lastDrawnArea[TOP_LEFT][X];
const height = lastDrawnArea[BOTTOM_RIGHT][Y] - lastDrawnArea[TOP_LEFT][Y];
area.style.left = `${lastDrawnArea[TOP_LEFT][X] - offset}px`;
area.style.top = `${lastDrawnArea[TOP_LEFT][Y] - offset}px`;
area.style.width = `${width + (offset * 2)}px`;
area.style.height = `${height + (offset * 2)}px`;
// Draw the template
container.append(area);
// Add the events
area.onclick = previewArea; // Preveiw event
area.querySelector("b").onclick = removeArea; // Remove event
// Reset "lastDrawnArea" value
lastDrawnArea = [[Infinity, Infinity], [0, 0]];
}
function previewArea(e) {
const preview = document.querySelector("#preview");
const previewCanvas = preview.querySelector("canvas");
const previewCtx = previewCanvas.getContext("2d");
// Get the drawing area coords
const area = e.target;
const [x, y, width, height] = [
parseFloat(area.style.left),
parseFloat(area.style.top),
parseFloat(area.style.width),
parseFloat(area.style.height)
];
// Draw the preview area
previewCanvas.width = width;
previewCanvas.height = height;
previewCtx.drawImage(image,
x, y, width, height,
0, 0, width, height);
}
function removeArea(e) {
const area = e.target.parentElement;
const [x, y, width, height] = [
parseFloat(area.style.left),
parseFloat(area.style.top),
parseFloat(area.style.width),
parseFloat(area.style.height)
];
ctx.clearRect(x, y, width, height);
area.remove();
}
resetButton.onclick = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
document.querySelectorAll('.area').forEach(el => el.remove());
}
body {
display: flex;
}
#container {
position: relative;
}
.area {
position: absolute;
border: 2px solid #333;
color: #333;
cursor: pointer;
}
.area b {
position: absolute;
right: 0;
top: 0;
transform: translate(100%, -100%);
color: red;
cursor: pointer;
}
<div id="container">
<canvas></canvas>
</div>
<div id="preview">
<canvas></canvas>
</div>
<template id="areaTemplate">
<div class="area">
<b>x</b>
</div>
</template>
<button> Reset All </button>
There is one problem with this example if two areas overlap the drawing will be removed at the intersection of both areas, to overcome this issue you need to keep each drawing on its own canvas (... a lot of work).
Last but not least, you can achieve the same result with ease and have better control over the canvas and its elements if you use a library like fabric js, take a look at this example of freedrawing, you will get the drawing coordinates for free, and you can still have everything on the same canvas (the image or whatever you need to add to canvas and all the drawing with no overlap ...), the initial learning curve may take some time but by the end of the day, you'll get a better understanding of HTML canvas overall.
Side note: the image you're using has a cross-origin limitation, you should use images from the same domain or from domains that allow cross-origin.
EDIT : Update with a working demo based on your fiddle
You may have to adapt this function to also include the thikness of the drawed lines (they may appear outside of the registered area).
But like this, you have te position and the size of your drawed area.
You can now do a ROI on it if you want.
You can track the area drawed with a function like this one:
var drawedArea = [0,0,0,0];
function drawedAreaTrack(x, y) {
// top left x
if (drawedArea[0] === 0) {
drawedArea[0] = x;
} else {
drawedArea[0] = Math.min(drawedArea[0], x);
}
// top left y
if (drawedArea[1] === 0) {
drawedArea[1] = y;
} else {
drawedArea[1] = Math.min(drawedArea[1], y);
}
// bottom right x
drawedArea[2] = Math.max(drawedArea[2], x);
// bottom right y
drawedArea[3] = Math.max(drawedArea[3], y);
console.log(drawedArea);
}
You could use those two point to get the total area drawed.
Here is a working example-> Fiddle :
http://jsfiddle.net/b90h6gaq/8/
The 'onmousemove' is doing all the drawing, I would save all you need to an array then on some event send to the database or draw to another canvas...
Here is a very simple example. I'm keeping the code minimal to convey my point that you already have all you need, all that is needed is to store it in a variable, You can bring back the lineThickness, the rest of the calculations and logic later, that should not be a problem.
var button = document.getElementById("btn");
var canvas = document.getElementById("canvas1");
var canvas2 = document.getElementById("canvas2");
var ctx = canvas.getContext("2d");
var ctx2 = canvas2.getContext("2d");
var painting = false;
var coordinates = [];
canvas.onmousedown = function(e) {painting = true;}
canvas.onmouseup = function(e) {painting = false;}
canvas.onmousemove = function(e) {
if (painting) {
x = e.pageX - this.offsetLeft;
y = e.pageY - this.offsetTop;
coordinates.push({x, y})
ctx.fillRect(x, y, 5, 5);
}
}
button.onmousedown = function(e) {
ctx.clearRect(0, 0, 300, 150);
coordinates.forEach(coord => {
ctx2.fillRect(coord.x, coord.y, 5, 5);
});
};
<canvas id="canvas1" width=300 height=150></canvas>
<button id="btn"> Reset </button>
<canvas id="canvas2" width=300 height=150></canvas>
Here we have a new event on the button click, I'm clearing the initial canvas and drawing all the coordinates to a second canvas, we could also be sending that data to server to be stored that should not be a problem.
I don’t know if this is quite what you were thinking, but if you simply want the same image to appear on another webpage, you could use ctx.getImageData() to copy the drawing from the canvas as an object, convert it to json, then send it to the database. Then on the other end, turn it back into an object and use ctx.putImageData() to place it back on the new canvas.

How can I reverse the direction of this square after it reaches a certain value?

I'm trying to create an idle animation where the red rectangle moves back and forth slightly in a loop. For some reason once it reaches the specified threshhold instead of proceeding to move in the opposite direction, it just stops.
What did I do wrong?
<canvas id="myCanvas" width="1500" height="500" style="border:1px solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>
<script>
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
// Spaceship structure
var shipWidth = 250;
var shipHeight = 100;
// Canvas parameters
var cWidth = canvas.width;
var cHeight = canvas.height;
// Positioning variables
var centerWidthPosition = (cWidth / 2) - (shipWidth / 2);
var centerHeightPosition = (cHeight / 2) - (shipHeight / 2);
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
function drawShip(){
ctx.clearRect(0, 0, cWidth, cHeight);
ctx.fillStyle = "#FF0000";
ctx.fillRect(centerWidthPosition,centerHeightPosition,shipWidth,shipHeight);
centerWidthPosition--;
if (centerWidthPosition < 400){
++centerWidthPosition;
}
requestAnimationFrame(drawShip);
}
drawShip();
</script>
#TheAmberlamps explained why it's doing that. Here I offer you a solution to achieve what I believe you are trying to do.
Use a velocity variable that changes magnitude. X position always increases by velocity value. Velocity changes directions at screen edges.
// use a velocity variable
var xspeed = 1;
// always increase by velocity
centerWidthPosition += xspeed;
// screen edges are 0 and 400 in this example
if (centerWidthPosition > 400 || centerWidthPosition < 0){
xspeed *= -1; // change velocity direction
}
I added another condition in your if that causes the object to bounce back and forth. Remove the selection after || if you don't want it doing that.
Your function is caught in a loop; once centerWidthPosition reaches 399 your conditional makes it increment back up to 400, and then it decrements back to 399.
here is another one as a brain teaser - how would go by making this animation bounce in the loop - basically turn text into particles and then reverse back to text and reverse back to particles and back to text and so on and on and on infinitely:
var random = Math.random;
window.onresize = function () {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
};
window.onresize();
var ctx = canvas.getContext('2d');
ctx.font = 'bold 50px "somefont"';
ctx.textBaseline = 'center';
ctx.fillStyle = 'rgba(255,255,255,1)';
var _particles = [];
var particlesLength = 0;
var currentText = "SOMETEXT";
var createParticle = function createParticle(x, y) {_particles.push(new Particle(x, y));};
var checkAlpha = function checkAlpha(pixels, i) {return pixels[i * 4 + 3] > 0;};
var createParticles = function createParticles() {
var textSize = ctx.measureText(currentText);
ctx.fillText(currentText,Math.round((canvas.width / 2) - (textSize.width / 2)),Math.round(canvas.height / 2));
var imageData = ctx.getImageData(1, 1, canvas.width, canvas.height);
var pixels = imageData.data;
var dataLength = imageData.width * imageData.height;
for (var i = 0; i < dataLength; i++) {
var currentRow = Math.floor(i / imageData.width);
var currentColumn = i - Math.floor(i / imageData.height);
if (currentRow % 2 || currentColumn % 2) continue;
if (checkAlpha(pixels, i)) {
var cy = ~~(i / imageData.width);
var cx = ~~(i - (cy * imageData.width));
createParticle(cx, cy);
}}
particlesLength = _particles.length;
};
var Point = function Point(x, y) {
this.set(x, y);
};
Point.prototype = {
set: function (x, y) {
x = x || 0;
y = y || x || 0;
this._sX = x;
this._sY = y;
this.reset();
},
add: function (point) {
this.x += point.x;
this.y += point.y;
},
multiply: function (point) {
this.x *= point.x;
this.y *= point.y;
},
reset: function () {
this.x = this._sX;
this.y = this._sY;
return this;
},
};
var FRICT = new Point(0.98);//set to 0 if no flying needed
var Particle = function Particle(x, y) {
this.startPos = new Point(x, y);
this.v = new Point();
this.a = new Point();
this.reset();
};
Particle.prototype = {
reset: function () {
this.x = this.startPos.x;
this.y = this.startPos.y;
this.life = Math.round(random() * 300);
this.isActive = true;
this.v.reset();
this.a.reset();
},
tick: function () {
if (!this.isActive) return;
this.physics();
this.checkLife();
this.draw();
return this.isActive;
},
checkLife: function () {
this.life -= 1;
this.isActive = !(this.life < 1);
},
draw: function () {
ctx.fillRect(this.x, this.y, 1, 1);
},
physics: function () {
if (performance.now()<nextTime) return;
this.a.x = (random() - 0.5) * 0.8;
this.a.y = (random() - 0.5) * 0.8;
this.v.add(this.a);
this.v.multiply(FRICT);
this.x += this.v.x;
this.y += this.v.y;
this.x = Math.round(this.x * 10) / 10;
this.y = Math.round(this.y * 10) / 10;
}
};
var nextTime = performance.now()+3000;
createParticles();
function clearCanvas() {
ctx.fillStyle = 'rgba(0,0,0,1)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
(function clearLoop() {
clearCanvas();
requestAnimationFrame(clearLoop);
})();
(function animLoop(time) {
ctx.fillStyle = 'rgba(255,255,255,1)';
var isAlive = true;
for (var i = 0; i < particlesLength; i++) {
if (_particles[i].tick()) isAlive = true;
}
requestAnimationFrame(animLoop);
})();
function resetParticles() {
for (var i = 0; i < particlesLength; i++) {
_particles[i].reset();
}}

Make moving Rect more smooth

I want to make "animation" of my Rect more smooth. Currently it's really clunky. I know the reason for it. One of the coordinates becomes wanted value before the other.
For example if I'm currently at (0,0) and I need to go to (150,75) and I increment each one equally every second , y-cord will come much sooner than x-cord.
var canvas = document.getElementById('canvas');
var ctx = document.getElementById('canvas').getContext('2d');
var aniTimer;
var x;
var y;
var tx = tx || 0;
var ty = ty || 0;
var xDir;
var yDir;
function followMouse(e) {
x = e.offsetX;
y = e.offsetY;
cancelAnimationFrame(aniTimer);
moveObject();
}
function moveObject() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (x < tx) {
xDir = -1;
} else {
xDir = 1;
}
if (y < ty) {
yDir = -1;
} else {
yDir = 1;
}
tx = tx != x ? tx + xDir : tx;
ty = ty != y ? ty + yDir : ty;
ctx.fillRect(tx - 25 , ty + 25, 50, 10);
if ( tx != x || ty != y ) {
aniTimer = window.requestAnimationFrame(moveObject);
}
}
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
};
canvas.addEventListener('mousemove', _.throttle(function(e) {
followMouse(e);
}, 100));
window.addEventListener('resize', resizeCanvas, false);
resizeCanvas();
html,
body {
margin: 0;
height: 100%;
}
canvas {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<canvas id="canvas"></canvas>
That's because you set dir [+-1, +-1] instead [dx, dy] (the actual displacement), which are not always proportional.
Have a look at the modified snippet:
var canvas = document.getElementById('canvas');
var ctx = document.getElementById('canvas').getContext('2d');
var x;
var y;
var tx = tx || 0;
var ty = ty || 0;
var xDir;
var yDir;
function followMouse(e) {
x = e.pageX;
y = e.pageY;
moveObject();
}
function moveObject() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
var scale = 0.2 * Math.max(canvas.width, canvas.height);
xDir = (x - tx) / scale;
yDir = (y - ty) / scale;
tx = tx != x ? tx + xDir : tx;
ty = ty != y ? ty + yDir : ty;
ctx.fillRect(tx - 25, ty + 25, 50, 10);
if (tx != x || ty != y) {
window.requestAnimationFrame(moveObject);
}
}
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
};
canvas.addEventListener('mousemove', _.throttle(function(e) {
followMouse(e);
}, 100));
window.addEventListener('resize', resizeCanvas, false);
resizeCanvas();
html,
body {
margin: 0;
height: 100%;
}
canvas {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<canvas id="canvas" style="border: 1px solid black"></canvas>

HTML5 canvas, make image rotate around click to select and drag circle

I have completed code that has a user click a button (to the right of the canvas), then the image is added to the canvas and is constrained to only move around the circumference of a circle. In order to move the image the user just needs to click the image and then move the mouse. To release the image the user simply needs to click where the image goes on the canvas.
Here is a fiddle showing what the current code does.
http://jsfiddle.net/smacnabb/68awv7sq/9/
Question: I am looking to be able to have the images that move around the circumference of the circle rotate while moving around the circumference of the circle.
This is what I mean:
Here is a fiddle for the code I added to try and make this happen
http://jsfiddle.net/smacnabb/68awv7sq/11/
in the handlemousemove method, it calls state.draw() every time the mouse move i'm passing mouseX, mouseY to state.draw.
state.draw() is in addstate method and this was the code I added to make the image rotate
var dx = mouseX - centerX;
var dy = mouseY - centerY;
var radianAngle = Math.atan2(dy, dx);
context.save();
context.translate(centerX, centerY);
context.rotate(radianAngle);
if (this.dragging) {
context.strokeStyle = 'black';
context.strokeRect(this.x, this.y, this.width + 2, this.height + 2)
}
context.drawImage(this.image, this.x, this.y);
context.restore();
What am I doing wrong?
You are close...Take a look at this example:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var radianAngle = 0;
var cx = 225;
var cy = 125;
var radius = 50;
// image loader
var imageURLs = [];
var imagesOK = 0;
var imgs = [];
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/multple/cars.png");
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/multple/plane.png");
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/multple/cars1.png");
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/multple/plane1.png");
loadAllImages(start);
function loadAllImages(callback) {
for (var i = 0; i < imageURLs.length; i++) {
var img = new Image();
imgs.push(img);
img.onload = function() {
imagesOK++;
if (imagesOK >= imageURLs.length) {
callback();
}
};
img.onerror = function() {
alert("image load failed");
}
img.crossOrigin = "anonymous";
img.src = imageURLs[i];
}
}
var imagesY = 20;
var targets = [];
var selectedTarget = -1;
function start() {
var n = imgs.length / 2;
for (var i = 0; i < n; i++) {
var target = imgs[i + n];
ctx.drawImage(target, 15, imagesY);
targets.push({
x: 15,
y: imagesY,
width: target.width,
height: target.height,
image: imgs[i]
});
imagesY += target.height + 10;
}
}
function handleMouseDown(e) {
e.preventDefault();
x = parseInt(e.clientX - offsetX);
y = parseInt(e.clientY - offsetY);
for (var i = 0; i < targets.length; i++) {
var t = targets[i];
if (x >= t.x && x <= t.x + t.width && y >= t.y && y <= t.y + t.height) {
selectedTarget = i;
draw(x, y);
}
}
}
function handleMouseMove(e) {
if (selectedTarget < 0) {
return;
}
e.preventDefault();
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
draw(mouseX, mouseY);
}
function draw(mouseX, mouseY) {
var dx = mouseX - cx;
var dy = mouseY - cy;
var radianAngle = Math.atan2(dy, dx);
// Drawing code goes here
var img = targets[selectedTarget].image;
ctx.clearRect(100, 0, canvas.width, canvas.height);
// draw the circle
ctx.beginPath();
ctx.arc(cx, cy, radius, 0, Math.PI * 2);
ctx.closePath();
ctx.stroke();
// draw the image rotated around the circumference
ctx.save();
ctx.translate(cx, cy);
ctx.rotate(radianAngle);
ctx.drawImage(img, radius - img.width / 2, -img.height / 2);
ctx.restore();
}
$("#canvas").mousedown(function(e) {
handleMouseDown(e);
});
$("#canvas").mousemove(function(e) {
handleMouseMove(e);
});
body{ background-color: ivory; }
canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Select an icon on left by clicking<br>
Then move mouse to have icon rotate around circle</h4>
<canvas id="canvas" width=350 height=350></canvas>

Html5 canvas image on click

I'm having a problem whit my code.
I draw some circles in a circular path and I expect when to click on them to return something other than 0 in firebug console but that's not happening;
I don't know what is wrong with my code and i hope someone will tell me.
Here's my code:
var canvas, ctx;
var circle_data = [];
function circles(x, y, radius) {
this.x = x;
this.y = y;
this.radius = radius;
circle_data.push(this);
}
circles.prototype = {
draw: function (context) {
context.beginPath();
context.arc(this.x, this.y, this.radius / 5, 0, 2 * Math.PI, false);
context.fillStyle = "red";
context.fill();
}
}
function draw() {
ctx.translate(250, 250);
for (var n = 0; n < 10; n++) {
var radi = (Math.PI / 180);
var x = Math.sin(radi * n * 36) * 70;
var y = Math.cos(radi * n * 36) * 70;
var radius = 50;
var thiscircle = new circles(x, y, radius);
thiscircle.draw(ctx);
}
}
function mouseDown(e) {
var img_data = ctx.getImageData(e.pageX, e.pageY, 1, 1);
console.log(img_data.data[3]);
}
function init() {
canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d');
canvas.addEventListener('mousedown', mouseDown, false);
}
init();
It dosen't matter is i use data[3];
I tried whit console.log(img_data.data[0]+" "+img_data.data[1]+" "+img_data.data[2]);
Still getting 0 0 0
Your detecting the mouse position relative to the page and not the canvas, you need to get the position of the canvas on the page and subtract that from the X and Y of the mouse to find you position relative to the canvas. I use functions similar to the ones below when working with canvas.
getOffsetPosition = function(obj){
/*obj is the Canvas element*/
var offsetX = offsetY = 0;
if (obj.offsetParent) {
do {
offsetX += obj.offsetLeft;
offsetY += obj.offsetTop;
}while(obj = obj.offsetParent);
}
return [offsetX,offsetY];
}
getMouse = function(e,canvasElement){
OFFSET = getOffsetPosition(canvasElement);
mouse_x = (e.pageX || (e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft)) - OFFSET[0];
mouse_y = (e.pageY || (e.clientY + document.body.scrollTop + document.documentElement.scrollTop)) - OFFSET[1];
return [mouse_x,mouse_y];
}
The following code works.
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Untitled Document</title>
</head>
<body>
<canvas id="canvas" width="500" height="500" style="background-color:#999999;"></canvas>
</body>
<script>
var canvas,ctx;
var circle_data = [];
function circles(x,y,radius)
{
this.x = x;
this.y = y;
this.radius = radius;
circle_data.push(this);
}
circles.prototype = {
draw: function(context){
context.beginPath();
context.arc(this.x, this.y, this.radius / 5, 0, 2* Math.PI, false);
context.fillStyle = "red";
context.fill();
}
}
getOffsetPosition = function(obj){
/*obj is the Canvas element*/
var offsetX = offsetY = 0;
if (obj.offsetParent) {
do {
offsetX += obj.offsetLeft;
offsetY += obj.offsetTop;
}while(obj = obj.offsetParent);
}
return [offsetX,offsetY];
}
getMouse = function(e,canvasElement){
OFFSET = getOffsetPosition(canvasElement);
mouse_x = (e.pageX || (e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft)) - OFFSET[0];
mouse_y = (e.pageY || (e.clientY + document.body.scrollTop + document.documentElement.scrollTop)) - OFFSET[1];
return [mouse_x,mouse_y];
}
function draw(){
ctx.translate(250, 250);
for (var n = 0; n < 10; n++) {
var radi = (Math.PI/180);
var x = Math.sin(radi*n*36)*70;
var y = Math.cos(radi*n*36)*70;
var radius = 50;
var thiscircle = new circles(x,y,radius);
thiscircle.draw(ctx);
}
}
function mouseDown(e)
{
var pos = getMouse(e,ctx.canvas);
var img_data = ctx.getImageData(pos[0],pos[1],1,1);
console.log(img_data.data[3]);
}
function init() {
canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d');
draw();
canvas.addEventListener('mousedown', mouseDown, false);
}
init();
</script>
</html>

Categories

Resources