Arrow Spinning in Canvas according to Cursor Position - javascript

I'm working on a script which is supposed to do the following. You lock your mouse to a canvas. It will show you an "artificial" cursor instead that you can also move by using your mouse. Under this cursor, you will have a circle which also moves with the mouse.
All of this worked perfectly fine with my script which was until I added another nice feature: I want to have an Arrow in the middle of the canvas which stays there, exact same size, but rotates according to your cursor movement. To give you an idea what I'm talking about, I made these example graphs (don't worry about dimensions and colour).
https://i.stack.imgur.com/poO6n.jpg
https://i.stack.imgur.com/twXhY.jpg
https://i.stack.imgur.com/RFFBe.jpg
I did some calculations to do this, implemented them, hoped for the best, but it doesn't work. I thought when it works, it will be a cool feature for everyone to have on this site. But so far I didn't see where my mistake is. If you have a clue, I'm absolutely grateful for every answer.
Many thanks!
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>ArrowSpin</title>
<style>
html, body {
margin: 0;
padding: 0;
}
html {
font-family: sans-serif;
}
canvas {
display: block;
margin: 0 auto;
border: 1px solid black;
}
.information {
width: 640px;
margin: 0 auto 50px;
}
#tracker {
position: absolute;
top: 0;
right: 10px;
background-color: white;
}
h1 {
font-size: 200%;
}
</style>
</head>
<body>
<div class="information">
<img id="mousecursor" hidden="true" width="13" height="20.5" src="mousecursor.png" alt="Cursor">
<p id="demo" style="color: black" oncl></p>
<p id="Message" style="color: black" oncl></p>
<canvas id="myCanvas" width="640" height="360">
Your browser does not support HTML5 canvas
</canvas>
<div id="tracker"></div>
</div>
<script>
try {
// helper functions
const RADIUS = 20;
// this image is you mousecursor
var img = document.getElementById("mousecursor");
// degree to radians
function degToRad(degrees) {
var result = Math.PI / 180 * degrees;
return result;
}
// generate a random number, later on, mouse cursor should start at random position, now unused
function generateRandomNumber() {
var minangle = 0;
var maxangle = 2*Math.PI;
randangle = Math.random() * (maxangle- minangle) + minangle;
return randangle;
};
//this function draws the actual arrow
function drawArrow(fromx, fromy, tox, toy, colourarrow){
//variables to be used when creating the arrow
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var headlen = 3;
var angle = Math.atan2(toy-fromy,tox-fromx);
//starting path of the arrow from the start square to the end square and drawing the stroke
ctx.beginPath();
ctx.moveTo(fromx, fromy);
ctx.lineTo(tox, toy);
ctx.strokeStyle = colourarrow;
ctx.lineWidth = 20;
ctx.stroke();
//starting a new path from the head of the arrow to one of the sides of the point
ctx.beginPath();
ctx.moveTo(tox, toy);
ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));
//path from the side point of the arrow, to the other side point
ctx.lineTo(tox-headlen*Math.cos(angle+Math.PI/7),toy-headlen*Math.sin(angle+Math.PI/7));
//path from the side point back to the tip of the arrow, and then again to the opposite side point
ctx.lineTo(tox, toy);
ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));
//draws the paths created above
ctx.strokeStyle = colourarrow;
ctx.lineWidth = 20;
ctx.stroke();
ctx.fillStyle = colourarrow
ctx.fill();
}
// this function calculates the current angle of the cursor from the exact middle of the canvas (x0,y0) by using two simple assumptions which are a) radius=sqrt(sqr(xfrom)+sqr(y)) b) x=x0+radius*cos(alpha) <=> alpha=arccos((x-x0)/radius)
function CursorAngle() {
var currentrad=Math.sqrt([Math.pow(x-canvas.width/2)+Math.pow(y+canvas.height)]);
var currentangle=Math.acos([(x-canvas.width/2)/currentrad]);
return currentangle
}
//in this function I use the just calculated cursor angle to now calculate where my arrow shall begin and end, again I use x=x0+radius*cos(alpha) and y=y0+radius*sin(alpha). In this case I always want my arrow to have a radius of 50 and I always want it to be drawn in the center of the canvas.
function ProbeAngle(alpha) {
var x1 = canvas.width/2+50*cos(alpha)
var y1 = canvas.width/2+50*sin(alpha)
var x2 = canvas.width/2+50*cos(alpha+Math.PI)
var y2 = canvas.width/2+50*sin(alpha+Math.PI)
return [x1; y1; x2; y2]
}
// setup of the canvas
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
var x = canvas.width/2;
var y = canvas.height/2;
//refresh the canvas
function canvasDraw() {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#f00";
ctx.beginPath();
ctx.arc(x, y, RADIUS, 0, degToRad(360), true);
ctx.fill();
ctx.drawImage(img, x, y);
}
canvasDraw();
// pointer lock object forking for cross browser
canvas.requestPointerLock = canvas.requestPointerLock ||
canvas.mozRequestPointerLock;
document.exitPointerLock = document.exitPointerLock ||
document.mozExitPointerLock;
canvas.onclick = function() {
canvas.requestPointerLock();
canvasDraw();
};
// pointer lock event listeners
// Hook pointer lock state change events for different browsers
document.addEventListener('pointerlockchange', lockChangeAlert, false);
document.addEventListener('mozpointerlockchange', lockChangeAlert, false);
function lockChangeAlert() {
if (document.pointerLockElement === canvas ||
document.mozPointerLockElement === canvas) {
console.log('The pointer lock status is now locked');
document.addEventListener("mousemove", updatePosition, false);
} else {
console.log('The pointer lock status is now unlocked');
document.removeEventListener("mousemove", updatePosition, false);
}
}
//tracker shows x and y coordinates of "pseudo" cursor
var tracker = document.getElementById('tracker');
//border protection for our image not to move out of the canvas
var animation;
function updatePosition(e) {
x += e.movementX;
y += e.movementY;
if (x > canvas.width) {
x = canvas.width;
}
if (y > canvas.height) {
y = canvas.height;
}
if (x < 0) {
x = 0;
}
if (y < 0) {
y = 0;
}
tracker.textContent = "X position: " + x + ", Y position: " + y;
if (!animation) {
animation = requestAnimationFrame(function() {
animation = null;
canvasDraw();
//receive the ProbeCoords by using the functions CursorAngle and ProbeAngle and draw it!
var ProbeCoord = ProbeAngle(CursorAngle());
drawArrow(ProbeCoord[0],ProbeCoord[1],ProbeCoord[2],ProbeCoord[3],'white')
});
}
}
}
catch(err) {
document.getElementById("demo").innerHTML = err.message;
}
document.getElementById("Message").innerHTML = "potential Errorcode above";
</script>
</body>
</html>

Have you tried using Fabric JS? In the linked example you can click an object and a handle appears at the top. After that you can click handle and it will follow the mouse. I'm suggesting this because most likely there is a way to change the click event to a hover event and then get the handle to follow the mouse.

Related

Image in canvas leaves a tiled trail when panned

I am trying to create a pannable image viewer which also allows magnification. If the zoom factor or the image size is such that the image no longer paints over the entire canvas then I wish to have the area of the canvas which does not contain the image painted with a specified background color.
My current implementation allows for zooming and panning but with the unwanted effect that the image leaves a tiled trail after it during a pan operation (much like the cards in windows Solitaire when you win a game). How do I clean up my canvas such that the image does not leave a trail and my background rectangle properly renders in my canvas?
To recreate the unwanted effect set magnification to some level at which you see the dark gray background show and then pan the image with the mouse (mouse down and drag).
Code snippet added below and Plnkr link for those who wish to muck about there.
http://plnkr.co/edit/Cl4T4d13AgPpaDFzhsq1
<!DOCTYPE html>
<html>
<head>
<style>
canvas{
border:solid 5px #333;
}
</style>
</head>
<body>
<button onclick="changeScale(0.10)">+</button>
<button onclick="changeScale(-0.10)">-</button>
<div id="container">
<canvas width="700" height="500" id ="canvas1"></canvas>
</div>
<script>
var canvas = document.getElementById('canvas1');
var context = canvas.getContext("2d");
var imageDimensions ={width:0,height:0};
var photo = new Image();
var isDown = false;
var startCoords = [];
var last = [0, 0];
var windowWidth = canvas.width;
var windowHeight = canvas.height;
var scale=1;
photo.addEventListener('load', eventPhotoLoaded , false);
photo.src = "http://www.html5rocks.com/static/images/cors_server_flowchart.png";
function eventPhotoLoaded(e) {
imageDimensions.width = photo.width;
imageDimensions.height = photo.height;
drawScreen();
}
function changeScale(delta){
scale += delta;
drawScreen();
}
function drawScreen(){
context.fillRect(0,0, windowWidth, windowHeight);
context.fillStyle="#333333";
context.drawImage(photo,0,0,imageDimensions.width*scale,imageDimensions.height*scale);
}
canvas.onmousedown = function(e) {
isDown = true;
startCoords = [
e.offsetX - last[0],
e.offsetY - last[1]
];
};
canvas.onmouseup = function(e) {
isDown = false;
last = [
e.offsetX - startCoords[0], // set last coordinates
e.offsetY - startCoords[1]
];
};
canvas.onmousemove = function(e)
{
if(!isDown) return;
var x = e.offsetX;
var y = e.offsetY;
context.setTransform(1, 0, 0, 1,
x - startCoords[0], y - startCoords[1]);
drawScreen();
}
</script>
</body>
</html>
You need to reset the transform.
Add context.setTransform(1,0,0,1,0,0); just before you clear the canvas and that will fix your problem. It sets the current transform to the default value. Then befor the image is draw set the transform for the image.
UPDATE:
When interacting with user input such as mouse or touch events it should be handled independently of rendering. The rendering will fire only once per frame and make visual changes for any mouse changes that happened during the previous refresh interval. No rendering is done if not needed.
Dont use save and restore if you don't need to.
var canvas = document.getElementById('canvas1');
var ctx = canvas.getContext("2d");
var photo = new Image();
var mouse = {}
mouse.lastY = mouse.lastX = mouse.y = mouse.x = 0;
mouse.down = false;
var changed = true;
var scale = 1;
var imageX = 0;
var imageY = 0;
photo.src = "http://www.html5rocks.com/static/images/cors_server_flowchart.png";
function changeScale(delta){
scale += delta;
changed = true;
}
// Turns mouse button of when moving out to prevent mouse button locking if you have other mouse event handlers.
function mouseEvents(event){ // do it all in one function
if(event.type === "mouseup" || event.type === "mouseout"){
mouse.down = false;
changed = true;
}else
if(event.type === "mousedown"){
mouse.down = true;
}
mouse.x = event.offsetX;
mouse.y = event.offsetY;
if(mouse.down) {
changed = true;
}
}
canvas.addEventListener("mousemove",mouseEvents);
canvas.addEventListener("mouseup",mouseEvents);
canvas.addEventListener("mouseout",mouseEvents);
canvas.addEventListener("mousedown",mouseEvents);
function update(){
requestAnimationFrame(update);
if(photo.complete && changed){
ctx.setTransform(1,0,0,1,0,0);
ctx.fillStyle="#333";
ctx.fillRect(0,0, canvas.width, canvas.height);
if(mouse.down){
imageX += mouse.x - mouse.lastX;
imageY += mouse.y - mouse.lastY;
}
ctx.setTransform(scale, 0, 0, scale, imageX,imageY);
ctx.drawImage(photo,0,0);
changed = false;
}
mouse.lastX = mouse.x
mouse.lastY = mouse.y
}
requestAnimationFrame(update);
canvas{
border:solid 5px #333;
}
<button onclick="changeScale(0.10)">+</button><button onclick="changeScale(-0.10)">-</button>
<canvas width="700" height="500" id ="canvas1"></canvas>
Nice Code ;)
You are seeing the 'tiled' effect in your demonstration because you are painting the scaled image to the canvas on top of itself each time the drawScreen() function is called while dragging. You can rectify this in two simple steps.
First, you need to clear the canvas between calls to drawScreen() and second, you need to use the canvas context.save() and context.restore() methods to cleanly reset the canvas transform matrix between calls to drawScreen().
Given your code as is stands:
Create a function to clear the canvas. e.g.
function clearCanvas() {
context.clearRect(0, 0, canvas.width, canvas.height);
}
In the canavs.onmousemove() function, call clearCanvas() and invoke context.save() before redefining the transform matrix...
canvas.onmousemove = function(e) {
if(!isDown) return;
var x = e.offsetX;
var y = e.offsetY;
/* !!! */
clearCanvas();
context.save();
context.setTransform(
1, 0, 0, 1,
x - startCoords[0], y - startCoords[1]
);
drawScreen();
}
... then conditionally invoke context.restore() at the end of drawScreen() ...
function drawScreen() {
context.fillRect(0,0, windowWidth, windowHeight);
context.fillStyle="#333333";
context.drawImage(photo,0,0,imageDimensions.width*scale,imageDimensions.height*scale);
/* !!! */
if (isDown) context.restore();
}
Additionally, you may want to call clearCanvas() before rescaling the image, and the canvas background could be styled with CSS rather than .fillRect() (in drawScreen()) - which could give a performance gain on low spec devices.
Edited in light of comments from Blindman67 below
See Also
Canvas.context.save : https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/save
Canvas.context.restore : https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/restore
requestAnimationFrame : https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame
Paul Irish, requestAnimationFrame polyfill : http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
Call context.save to save the transformation matrix before you call context.fillRect.
Then whenever you need to draw your image, call context.restore to restore the matrix.
For example:
function drawScreen(){
context.save();
context.fillStyle="#333333";
context.fillRect(0,0, windowWidth, windowHeight);
context.restore();
context.drawImage(photo,0,0,imageDimensions.width*scale,imageDimensions.height*scale);
}
Also, to further optimize, you only need to set fillStyle once until you change the size of canvas.

How can I make different shapes of a canvas draggable and particular area of it droppable in the same canvas

I want to create a Canvas in which there will be two areas (Left and right), Left panel will contain some shapes which will be draggable(static as well) and on the right side I would be able to drop them, but I am facing following problem,
I am not able to make the shapes which i draw on the left side, draggable, because there is no id associated with them.
I do not know how to make some particular area droppable.
Here is code to visualize what I am trying to achieve-
<body>
<canvas id="myCanvas" width="800" height="600" style="border:1px solid #000000;">
</canvas>
<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.moveTo(250,0);
ctx.lineTo(250,600);
ctx.stroke();
ctx.fillStyle = "#FF0000";
ctx.fillRect(50,50,160,25);
ctx.fillStyle = "#0000FF";
ctx.font = "15px";
ctx.strokeText("Draggable Elements here",57,67);
ctx.fillStyle = "#FF0000";
ctx.fillRect(500,50,130,25);
ctx.font = "15px";
ctx.strokeText("Droppable area Here",510,67);
</script>
</body>
Here is the JS fiddle for the same -
http://jsfiddle.net/akki166786/4tfyy4o5/
so if anybody can shed some light on how can I achieve this, it will be a great help.
Thanks in Advance
Drag and drop in specifik area
UPDATE: Copy of box remains at original position while it's being moved.
First you need to be able to detect your rectangles. You do this by making then into objects in your code:
function box(x,y,w,h,rgb) {
this.x = x,
this.y = y;
this.xS = x; //saving x
this.yS = y; //saving y
this.w = w;
this.h = h;
this.rgb = rgb;
//to determine if the box is being draged
this.draging = false;
}
No you need to add an event listener to determine if someone is clicking, you also need to determine if the person clicked in one of your boxes.
c.addEventListener("mousedown",down);
c.addEventListener("mousemove",move);
c.addEventListener("mouseup",up);
So events have been made to detect when the mouse button is pressed down, released back up and if the mouse moves within the canvas. To these events we have functions, down(), move() and up(), ready to be executed.
All functions will be visible in the example below.
When we're happily draging our boxes and releasing our mouse button, we need to check if the box was dropped in the dropable area. We do this in the up()-function. If the drop was OK, the box can stay, otherwise we send it back to where it came from.
Working example
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
c.width = 600;
c.height = 300;
//My mouse coordinates
var x,y;
c.addEventListener("mousedown",down);
c.addEventListener("mousemove",move);
c.addEventListener("mouseup",up);
//I'll save my boxes in this array
var myBoxes = new Array();
//This function describes what a box is.
//Each created box gets its own values
function box(x,y,w,h,rgb) {
this.x = x,
this.y = y;
this.xS = x; //saving x
this.yS = y; //saving y
this.w = w;
this.h = h;
this.rgb = rgb;
//to determine if the box is being draged
this.draging = false;
}
//Let's make some boxes!!
myBoxes[0] = new box(10,10,50,100,"green");
myBoxes[1] = new box(80,50,100,75,"blue");
myBoxes[2] = new box(40,150,20,70,"yellow");
//here we draw everything
function draw() {
ctx.clearRect(0,0,c.width,c.height);
//Dropable area
ctx.fillStyle="red";
ctx.fillRect(c.width/2,0,c.width,c.height);
//Boxes!
for (var i = 0; i<myBoxes.length; i++) {
var b = myBoxes[i];
//NEW CODE FOR UPDATE
if (b.draging) { //box on the move
//Also draw it on the original spot
ctx.fillStyle="grey"; //I chose a different color to make it appear more as a shadow of the box that's being moved.
ctx.fillRect(b.xS,b.yS,b.w,b.h);
ctx.strokeRect(b.xS,b.yS,b.w,b.h);
}
//End of new code for update
ctx.fillStyle=b.rgb;
ctx.fillRect(b.x,b.y,b.w,b.h);
ctx.strokeRect(b.x,b.y,b.w,b.h);
}
//Let's keep re-drawing this
requestAnimationFrame(draw);
}
function down(event) {
event = event || window.event;
x = event.pageX - c.offsetLeft,
y = event.pageY - c.offsetTop;
for (var i = 0; i<myBoxes.length; i++) {
var b = myBoxes[i];
if (x>b.x && x<b.x+b.w && y>b.y && y<b.y+b.h) {
b.draging = true;
}
}
}
function move(event) {
event = event || window.event;
x = event.pageX - c.offsetLeft,
y = event.pageY - c.offsetTop;
for (var i = 0; i<myBoxes.length; i++) {
var b = myBoxes[i];
if (b.draging) {
b.x = x;
b.y = y;
}
}
}
function up(event) {
event = event || window.event;
x = event.pageX - c.offsetLeft,
y = event.pageY - c.offsetTop;
for (var i = 0; i<myBoxes.length; i++) {
var b = myBoxes[i];
if (b.draging) {
//Let's see if the rectangle is inside the dropable area
if (b.x>c.width/2) {
//Yes is it!
b.x = x;
b.y = y;
b.draging = false;
}
else {
//No it's not, sending it back to its ordiginal spot
b.x = b.xS;
b.y = b.yS;
b.draging = false;
}
}
}
}
draw();
canvas {
border: 1px solid black;
}
<canvas id="canvas"></canvas>
You're using just one canvas, maybe it would be better if you use two separate canvas, one for each element you want to handle on page. so you'll have one element ID for each one.
plus. if your drawing is simple, consider using a div for it instead a canvas
Once drawn to the canvas, shapes(or lines, images, everything) are no longer accessible.
What you will need to do is store each shape in an object in your code. For example:
var rectangle = {
width: 100,
height: 100,
x: 50,
y: 50
}
Then when you drag rectangle, you will need to update it's x and y properties on mouseup (or while it's being dragged if you want a drag preview).

HTML5 canvas zoom my drawings

Hello i create program like a paint on HTML5 canvas. I have problem i need create few tools drawing and zoom. I don't have idea how to create zoom without delay. Drawing example: http://jsfiddle.net/x5rrvcr0/
How i can zooming my drawings?
drawing code:
<style>
canvas {
background-color: #CECECE;
}
html, body {
background-color: #FFFFFF;
}
</style>
<script>
$(document).ready(function () {
var paintCanvas = document.getElementById("paintCanvas");
var paintCtx = paintCanvas.getContext("2d");
var size = 500;
paintCanvas.width = size;
paintCanvas.height = size;
var draw = false;
var prevMouseX = 0;
var prevMouseY = 0;
function getMousePos(canvas, evt) {
evt = evt.originalEvent || window.event || evt;
var rect = canvas.getBoundingClientRect();
if (evt.clientX !== undefined && evt.clientY !== undefined) {
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
}
$("#paintCanvas").on("mousedown", function(e) {
draw = true;
var coords = getMousePos(paintCanvas);
prevMouseX = coords.x;
prevMouseY = coords.y;
});
$("#paintCanvas").on("mousemove", function(e) {
if(draw) {
var coords = getMousePos(paintCanvas, e);
paintCtx.beginPath();
paintCtx.lineWidth = 10;
paintCtx.strokeStyle = "#000000";
paintCtx.moveTo(prevMouseX, prevMouseY);
paintCtx.lineTo(coords.x, coords.y);
paintCtx.stroke();
prevMouseX = coords.x;
prevMouseY = coords.y;
}
});
$("#paintCanvas").on("mouseup", function(e) {
draw = false;
});
});
</script>
<body>
<canvas id="paintCanvas"></canvas>
</body>
If you want to keep the pixelated effect in the zoom, you need to draw on a temp canvas, then only after copy that temp canvas to the main screen.
You no longer need to zoom in the temp canvas, just draw on 1:1 scale always. When copying to the view canvas, then you apply the zoom (and maybe translate) that you want.
Keep in mind that drawings are anti-aliased, so you when zooming you will see some shades of grey when drawing in black, for instance.
I kept the recording code of #FurqanZafar since it is a good idea to record things in case you want to perform undo : in that case just delete the last record entry and redraw everything.
http://jsfiddle.net/gamealchemist/x5rrvcr0/4/
function updatePaintCanvas() {
paintContext.clearRect(0, 0, paintContext.canvas.width, paintContext.canvas.height);
paintContext.save();
paintContext.translate(cvSize * 0.5, cvSize * 0.5);
paintContext.scale(scale, scale);
paintContext.drawImage(drawCanvas, -cvSize * 0.5, -cvSize * 0.5);
paintContext.restore();
}
Heres the updated fiddle: http://jsfiddle.net/x5rrvcr0/2/ with basic zooming functionality
If you draw multiple paths on mouse move then your sketch will appear broken or disconnected, instead you should only stroke a single path until "mouseup" event.
You can then store these paths in an array and later redraw them at different zoom levels:
function zoom(context, paths, styles, scale) {
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
context.save();
applyStyles(context, styles);
scaleFromCenter(context, scale);
for (var i = 0; i < paths.length; i++) {
context.beginPath();
context.moveTo(paths[i][0].x, paths[i][0].y);
for (var j = 1; j < paths[i].length; j++)
context.lineTo(paths[i][j].x, paths[i][j].y);
context.stroke();
}
context.restore();
};

Ecg graph with html5 canvas

My need is to draw a ECG graph on canvas for socket data per every data iteration.
I tried to look into several graph plugins which use canvas to plot graphs, tried http://www.flotcharts.org/ but didn't succeed.
I Tried to plot graph using below basic html5 canvas drawline with sample data.
var fps = 60;
var n = 1;
drawWave();
function drawWave() {
setTimeout(function() {
requestAnimationFrame(drawWave2);
ctx.lineWidth = "2";
ctx.strokeStyle = 'green';
// Drawing code goes here
n += 1;
if (n >= data.length) {
n = 1;
}
ctx.beginPath();
ctx.moveTo(n - 1, data[n - 1] * 2);
ctx.lineTo(n, data[n] * 2);
ctx.stroke();
ctx.clearRect(n + 1, 0, 10, canvas.height);
}, 1000 / fps);
}
But it is not giving me the exact graph view as attached image. I'm not able to understand how to achieve graph like ecg graph. Please help me to get rid of this problem.
The characteristics with an ECG is that is plots the signal horizontally headed by a blank gap. When the end of the right side is reached is returns to left side and overdraw the existing graph.
DEMO
Setup
var ctx = demo.getContext('2d'),
w = demo.width,
h = demo.height,
/// plot x and y, old plot x and y, speed and scan bar width
speed = 3,
px = 0, opx = 0,
py = h * 0.8, opy = py,
scanBarWidth = 20;
ctx.strokeStyle = '#00bd00';
ctx.lineWidth = 3;
/// for demo we use mouse as signal input source
demo.onmousemove = function(e) {
var r = demo.getBoundingClientRect();
py = e.clientY - r.top;
}
loop();
The main loop:
The loop will plot whatever the signal amplitude is at any moment. You can inject a sinus or some other signal or read from an actual sensor over Web socket etc.
function loop() {
/// move forward at defined speed
px += speed;
/// clear ahead (scan bar)
ctx.clearRect(px,0, scanBarWidth, h);
/// draw line from old plot point to new
ctx.beginPath();
ctx.moveTo(opx, opy);
ctx.lineTo(px, py);
ctx.stroke();
/// update old plot point
opx = px;
opy = py;
/// check if edge is reached and reset position
if (opx > w) {
px = opx = -speed;
}
requestAnimationFrame(loop);
}
To inject a value simply update py (outside loop).
It would be far more helpful, if you included an image of what it does produce, rather than stating that it doesn't do that. Anyhow, it looks like you're only drawing a single line per frame. You need to run a loop with lineTo inside, iterating through all values of n.
Something along the lines of the below except from a sound-synthesizer. Just pay attention to the fact that there's a drawing-loop. In my case, there are often 40,000 or 50,000 samples that need to be drawn on a canvas of only a few hundred pixels wide. It seems like redundant drawing in my case, but doing th intuitive thing, of a single point per pixel results in an inaccurate image. The output of this looks something (88200 samples per 1024 pixels)
function drawFloatArray(samples, canvas)
{
var i, n = samples.length;
var dur = (n / 44100 * 1000)>>0;
canvas.title = 'Duration: ' + dur / 1000.0 + 's';
var width=canvas.width,height=canvas.height;
var ctx = canvas.getContext('2d');
ctx.strokeStyle = 'yellow';
ctx.fillStyle = '#303030';
ctx.fillRect(0,0,width,height);
ctx.moveTo(0,height/2);
ctx.beginPath();
for (i=0; i<n; i++)
{
x = (i*width) / n;
y = (samples[i]*height/2)+height/2;
ctx.lineTo(x, y);
}
ctx.stroke();
ctx.closePath();
}
Since your live data is streaming non-stop, you need a plan to deal with a graph that overflows the canvas.
Here's one solution that pans the canvas to always show only the most recent data.
Demo: http://jsfiddle.net/m1erickson/f5sT4/
Here's starting code illustrating this solution:
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
#canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
// capture incoming socket data in an array
var data=[];
// TESTING: fill data with some test values
for(var i=0;i<5000;i++){
data.push(Math.sin(i/10)*70+100);
}
// x is your most recent data-point in data[]
var x=0;
// panAtX is how far the plot will go rightward on the canvas
// until the canvas is panned
var panAtX=250;
var continueAnimation=true;
animate();
function animate(){
if(x>data.length-1){return;}
if(continueAnimation){
requestAnimationFrame(animate);
}
if(x++<panAtX){
ctx.fillRect(x,data[x],1,1);
}else{
ctx.clearRect(0,0,canvas.width,canvas.height);
// plot data[] from x-PanAtX to x
for(var xx=0;xx<panAtX;xx++){
var y=data[x-panAtX+xx];
ctx.fillRect(xx,y,1,1)
}
}
}
$("#stop").click(function(){continueAnimation=false;});
}); // end $(function(){});
</script>
</head>
<body>
<button id="stop">Stop</button><br>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
I think this web component is both prettier and easier to use. It using canvas as a backend of draw. If you going use it all you need to do is call bang() on every appearing beat
document.body.innerHTML += '<ecg-line></ecg-line>';
ecgLine((bang) => setInterval(() => bang(), 1000));
Do you mean something like this?
var canvas = document.getElementById("dm_graphs");
var ctx = canvas.getContext("2d");
ls = 0;
d = 7; // sesitivity
function updateFancyGraphs() {
var gh = canvas.height;
var gh2 = gh / 2;
ctx.drawImage(canvas, -1, 0);
ctx.fillRect(graphX, 0, 1, canvas.height);
var size = Math.max(-gh, Math.min((3 * (Math.random() * 10 - 5)) * d, gh));
ctx.beginPath();
ctx.moveTo(graphX - 1, gh2 + ls / 2);
ctx.lineTo(graphX, gh2 + size / 2);
ctx.stroke();
ls = size;
}
function resizeCanvas() {
var w = window.innerWidth || document.body.offsetWidth;
canvas.width = w / 1.5;
canvas.height = window.innerHeight / 1.5;
graphX = canvas.width - 1;
ctx.lineWidth = 1; // 1.75 is nicer looking but loses a lot of information.
ctx.strokeStyle = "Lime";
ctx.fillStyle = "black";
}
window.addEventListener("resize", resizeCanvas);
resizeCanvas();
z = setInterval(() => updateFancyGraphs(), 20)
body {
min-height: 100ev;
}
body,
html,
canvas {
font: 15px sans-serif;
height: 100hv;
width: 100%;
margin: 0;
padding: 0;
background: #113355;
color: white;
overflow: hidden;
}
#dm_status,
footer {
text-align: center;
}
#dm_graphs {
image-rendering: optimizeSpeed;
background: rgba(0, 0, 0, 0.7);
}
<html>
<head>
<title>Zibri's Graph</title>
<meta name="viewport" content="width=device-width">
</head>
<body>
<canvas id="dm_graphs"></canvas>
</body>
</html>

Get Canvas X and Y coordinates and show on screen

I'm currently learning canvas touch event function,I 'm able to draw line on the canvas, now I want to get the x and y coordinates when I draw any lines and show on the screen.please help and teach me how to get the x and y values, thank You!
here is the coding
<!DOCTYPE html>
<html><head>
<style>
#contain {
width: 500px;
height: 120px;
top : 15px;
margin: 0 auto;
position: relative;
}
</style>
<script>
var canvas;
var ctx;
var lastPt=null;
var letsdraw = false;
var offX = 10, offY = 20;
function init() {
var touchzone = document.getElementById("layer1");
touchzone.addEventListener("touchmove", draw, false);
touchzone.addEventListener("touchend", end, false);
ctx = touchzone.getContext("2d");
}
function draw(e) {
e.preventDefault();
if (lastPt != null) {
ctx.beginPath();
ctx.moveTo(lastPt.x, lastPt.y);
ctx.lineTo(e.touches[0].pageX - offX,
e.touches[0].pageY - offY);
ctx.stroke();
}
lastPt = {
x: e.touches[0].pageX - offX,
y: e.touches[0].pageY - offY
};
}
function end(e) {
var touchzone = document.getElementById("layer1");
e.preventDefault();
// Terminate touch path
lastPt = null;
}
function clear_canvas_width ()
{
var s = document.getElementById ("layer1");
var w = s.width;
s.width = 10;
s.width = w;
}
</script>
</head>
<body onload="init()">
<div id="contain">
<canvas id="layer1" width="450" height="440"
style="position: absolute; left: 0; top: 0;z-index:0; border: 1px solid #ccc;"></canvas>
</div>
</body>
</html>
Still not entirely confident I understand your question.
In the code you posted, you are already obtaining coordinates using e.touches[0].pageX/Y. The main problem with that is that the pageX/Y values are relative to the page origin. You are then subtracting fixed offX/Y in your code to try and convert these to canvas-relative coordinates. Right idea, wrong values. You need to subtract off the position of the canvas element which can be obtained by summing the offsetX/Y values as you traverse the tree upward using the offsetParent reference.
Something like:
offX=0;offY=0;
node = document.getElementById ("layer1");
do {
offX += node.offsetX;
offY += node.offsetY;
node = node.offsetParent;
} while(node);
should give you a better value for offX and offY.
If you just want to locate the actual drawing at the end, it would be easiest just to track a bounding box while the user draws.

Categories

Resources