I'm working with Arduino and I'm using an Accelerometer. I want to make a 2D line based on the x and y variables from the accelerometer.
I'm trying to it with this code:
board.on("ready", () => {
const accelerometer = new Accelerometer({
controller: "MPU6050"
});
accelerometer.on("change", function () {
const {
acceleration,
inclination,
orientation,
pitch,
roll,
x,
y,
z
} = accelerometer;
const $yPos = y * 100 * 10;
const $canvas = document.querySelector(`.simulation__line`);
if ($canvas.childElementCount > 0) {
$canvas.innerHTML = ``;
}
const drawing = $canvas.getContext("2d");
drawing.beginPath();
drawing.moveTo(1000, 1000 - $yPos);
drawing.lineTo(0, 1000);
drawing.lineTo(-1000, 1000 + $yPos);
drawing.stroke();
drawing.clearRect(1000, $yTest, drawing.width, drawing.height);
});
});
So every time the accelerometer changes variables, it draws a new line. This results in a lot of lines, but I want only one which is constantly changing. I tried to do it with the if statement if ($canvas.childElementCount > 0), but this won't help.
Here is a very simple example of drawing something on a canvas with a variable.
I believe your issue is in the way you are using the clearRect to clear the canvas
<input type="range" min="1" max="99" value="10" id="slider" oninput="draw()">
<br/>
<canvas id="canvas"></canvas>
<script>
function draw() {
y = document.getElementById("slider").value
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillRect(0, y, 250, 3);
}
// init canvas
var canvas = document.getElementById('canvas');
canvas.height = (canvas.width = 250)/2;
var ctx = canvas.getContext('2d');
draw();
</script>
Run that code snippet to see it in action.
The slider controls the Y position of a "line" that I'm drawing in the canvas, there should be just one line visible on the screen at any time.
The key is to wipe the screen:
ctx.clearRect(0, 0, canvas.width, canvas.height);
before we draw anything
Looking at your code you are doing a few things inside the "accelerometer change" function that you should consider doing outside here is what I mean:
const $canvas = document.querySelector(`.simulation__line`);
const drawing = $canvas.getContext("2d");
Those should not be changing as the accelerometer changes so I would keep them out
The other sticky point is your call to:
drawing.clearRect(1000, $yTest, drawing.width, drawing.height);
that should be the first before you start drawing anything
not sure why to start at 1000, $yTest you should clear the entire canvas start at 0,0
and the drawing.width, drawing.height are not the same as canvas.width, canvas.height if I change that on my example it certainly will not wipe the canvas
Related
I have created a basic shape in HTML canvas element which works fine.
The problem occurs when I resize the canvas, all the drawing in the canvas disappears. Is this the normal behavior? or is there a function that can be used to stop this?
One way to fix this could be to call drawing function again on canvas resize however this may not be very efficient if there is huge content to be drawn.
What's the best way?
Here is the link to sample code https://gist.github.com/2983915
You need to redraw the scene when you resize.
setting the width or height of a canvas, even if you are setting it to the same value as before, not only clears the canvas but resets the entire canvas context. Any set properties (fillStyle, lineWidth, the clipping region, etc) will also be reset.
If you do not have the ability to redraw the scene from whatever data structures you might have representing the canvas, you can always save the entire canvas itself by drawing it to an in-memory canvas, setting the original width, and drawing the in-memory canvas back to the original canvas.
Here's a really quick example of saving the canvas bitmap and putting it back after a resize:
http://jsfiddle.net/simonsarris/weMbr/
Everytime you resize the canvas it will reset itself to transparant black, as defined in the spec.
You will either have to:
redraw when you resize the canvas, or,
don't resize the canvas
One another way is to use the debounce if you are concerned with the performance.
It doesnt resize or redraw every position you are dragging. But it will resize only when the it is resized.
// Assume canvas is in scope
addEventListener.("resize", debouncedResize );
// debounce timeout handle
var debounceTimeoutHandle;
// The debounce time in ms (1/1000th second)
const DEBOUNCE_TIME = 100;
// Resize function
function debouncedResize () {
clearTimeout(debounceTimeoutHandle); // Clears any pending debounce events
// Schedule a canvas resize
debounceTimeoutHandle = setTimeout(resizeCanvas, DEBOUNCE_TIME);
}
// canvas resize function
function resizeCanvas () { ... resize and redraw ... }
I had the same problem. Try following code
var wrapper = document.getElementById("signature-pad");
var canvas = wrapper.querySelector("canvas");
var ratio = Math.max(window.devicePixelRatio || 1, 1);
canvas.width = canvas.offsetWidth * ratio;
canvas.height = canvas.offsetHeight * ratio;
It keeps the drawing as it is
One thing that worked for me was to use requestAnimationFrame().
let height = window.innerHeight;
let width = window.innerWidth;
function handleWindowResize() {
height = window.innerHeight;
width = window.innerWidth;
}
function render() {
// Draw your fun shapes here
// ...
// Keep this on the bottom
requestAnimationFrame(render);
}
// Canvas being defined at the top of the file.
function init() {
ctx = canvas.getContext("2d");
render();
}
I had the same problem when I had to resize the canvas to adjust it to the screen.
But I solved it with this code:
var c = document.getElementById('canvas');
ctx = c.getContext('2d');
ctx.fillRect(0,0,20,20);
// Save canvas settings
ctx.save();
// Save canvas context
var dataURL = c.toDataURL('image/jpeg');
// Resize canvas
c.width = 50;
c.height = 50;
// Restore canvas context
var img = document.createElement('img');
img.src = dataURL;
img.onload=function(){
ctx.drawImage(img,20,20);
}
// Restote canvas settings
ctx.restore();
<canvas id=canvas width=40 height=40></canvas>
I also met this problem.but after a experiment, I found that Resizing the canvas element will automatically clear all drawings off the canvas!
just try the code below
<canvas id = 'canvas'></canvas>
<script>
var canvas1 = document.getElementById('canvas')
console.log('canvas size',canvas1.width, canvas1.height)
var ctx = canvas1.getContext('2d')
ctx.font = 'Bold 48px Arial'
var f = ctx.font
canvas1.width = 480
var f1 = ctx.font
alert(f === f1) //false
</script>
1). This animation has a a weird effect where it colors part of the screen darker than it should be (it should be more transparent not opaque), also
2). It resizes the screen in the wrong way where it gets smaller than it should be.
Here's the code:
const canvas = document.getElementById('c');
const context = canvas.getContext('2d');
var shiftingValue = 0.1;
var wavePos = 0;
function flowGrad() {
//Clearing between frames
context.clearRect(0, 0, canvas.height, canvas.width);
//Creating linear gradient
var lineargradient = context.createLinearGradient(canvas.width/2, 0, canvas.width/2, canvas.height);
lineargradient.addColorStop(0, 'rgba(255,0,0,.2)');
lineargradient.addColorStop(wavePos, 'rgba(0,255,0,.5)');
lineargradient.addColorStop(1, 'rgba(0,0,255,.2)');
context.fillStyle = lineargradient;
//Drawing triangle
context.beginPath();
context.moveTo(0, 0);
context.lineTo(canvas.width, 0);
context.lineTo(canvas.width/2, canvas.height);
context.fill();
shiftingValue += 0.01;
//Uses absolute value of sin to generate an oscilating value.
wavePos = Math.abs(Math.sin(shiftingValue));
requestAnimationFrame(flowGrad);
}
function init() {
// Register an event listener to call the resizeCanvas() function
// each time the window is resized.
window.addEventListener('resize', resizeCanvas, false);
// Draw canvas for the first time.
requestAnimationFrame(flowGrad);
}
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
init();
and codepen link. Help much appreciated. I'm using chrome if want the effect isn't happening for you.
1). This animation has a a weird effect where it colors part of the screen darker than it should be (it should be more transparent not opaque)
You have swapped height and width; change this line:
context.clearRect(0, 0, canvas.height, canvas.width);
to
context.clearRect(0, 0, canvas.width, canvas.height);
2). It resizes the screen in the wrong way where it gets smaller than it should be.
The animation loop is invoked several times asynchronously within the resize handler. Add a reference for the animation frame request in global scope, then cancel the request every time you resize:
var ref;
// ...
function init() {
window.addEventListener('resize', resizeCanvas, false);
cancelAnimationFrame(ref); // cancel current, if any
ref = requestAnimationFrame(flowGrad);
}
As well as in the loop itself:
function flowGrad() {
// ...
ref = requestAnimationFrame(flowGrad);
}
On top of that you could always add CSS rules to avoid the canvas being offset and therefor produce scroll bars:
html, body {
margin: 0;
overflow: hidden;
}
Also remember to set the canvas size the first time inside init() by calling resizeCanvas() manually.
I'm trying to increase the radius of a circle drawn in canvas using JavaScript functions.
There were many topics with similar issues but couldn't find an answer that would fix this one, I've tried using built-in methods that were suggested like setInterval, setTimeout, window.requestAnimationFrame and clearing the canvas to redraw the circle with the updated variable.
So far the setInterval method displayed the update but kept the previous iterations in the canvas, the clear method doesn't work.
Here's the example :
//Define globals
var radiusIncrement = 5;
var ballRadius = 20;
//Helper functions
function increaseRadius() {
ballRadius += radiusIncrement;
}
function decreaseRadius() {
if (ballRadius > 0) {
ballRadius -= radiusIncrement;
}
}
//Draw handler
function draw() {
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(canvas.height/2,canvas.width/2,ballRadius,0,2*Math.PI);
ctx.stroke();
}
//Event handler
setInterval(function() {
draw();
ctx.clearRect(0,0,canvas.width,canvas.height);
}, 100);
<!--Create frame and assign callbacks to event handlers-->
<button type="button" onclick="increaseRadius()">Increase Radius</button>
<button type="button" onclick="decreaseRadius()">Decrease Radius</button>
<canvas id="myCanvas" width="400" height="400" style="border:1px solid #000000;"></canvas>
Rather than using setInterval, is there a way to streamline the code using and use an event handler to refresh the canvas on every onlick ?
Thanks for your help.
Cheers.
If I understand you correctly, you only want to redraw on the button click, i.e. on a radius change. You don't need any timing functions for this, you can call the draw function whenever a radius change happens:
//Define globals
var radiusIncrement = 5;
var ballRadius = 20;
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
//initialize
draw();
//Helper functions
function increaseRadius() {
ballRadius += radiusIncrement;
draw();
}
function decreaseRadius() {
if (ballRadius > 0) {
ballRadius -= radiusIncrement;
draw();
}
}
//Draw handler
function draw() {
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.beginPath();
ctx.arc(canvas.height/2,canvas.width/2,ballRadius,0,2*Math.PI);
ctx.stroke();
}
As you can see I also extracted the variable definition for canvas and ctx out of the draw function, since you don't need to re-assign these on every draw call.
I have two pngs, one white and the other red.
<img class="rangeHorizontal" id="seek" src="http://i.imgur.com/hRHH9VO.png">
<img id="seekFill" src="http://i.imgur.com/WoJggN0.png">
When the song it not playing it should be white, and when the song is going it should fill red as the song progresses, and also when scrubbed backwards and forwards respectively.
I have been able to muddle through most of the play functionality except the Canvas portion.
Currently the two pngs are overlays on top of each other and when the song plays the whole red png overlays on top, .. instead of just showing a portion,.. its pretty hard to explain but I have a fiddle so things become clearer.
https://jsfiddle.net/tallgirltaadaa/q9qgyob0/
The desired result would like this player, it also uses a two png method:
http://codecanyon.net/item/zoomsounds-neat-html5-audio-player/full_screen_preview/4525354?ref=hkeyjun
If anyone could help me out a bit I would love it.. I have been masking and trying to use canvas all morning with no luck.
There is a bit too much code to go through, but here is one technique use can use to draw a clipped version of an image. Implement as needed -
Live Example
At each timeupdate:
Canvas is cleared
The white bottom image is drawn in (you can scale these as you wish)
Progress is calculated (currentTime / duration)
The red top image is drawn in using the clipping arguments:
ie.
ctx.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh);
Full example code (had to replace the music due to the API usage) -
var imgBg = new Image(),
imgFg = new Image(),
count = 2;
imgBg.onload = imgFg.onload = init;
imgBg.src = "http://i.imgur.com/hRHH9VO.png";
imgFg.src = "http://i.imgur.com/WoJggN0.png";
function init() {
if (--count) return; // makes sure both images are loaded
var canvas = document.querySelector("canvas"),
ctx = canvas.getContext("2d"),
audio = document.querySelector("audio");
canvas.width = imgBg.naturalWidth;
canvas.height = imgBg.naturalHeight;
render();
audio.volume = 0.5;
audio.addEventListener("timeupdate", render);
function render() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(imgBg, 0, 0);
// calc progress
var pst = audio.currentTime / audio.duration;
// draw clipped version of top image
if (pst > 0) {
ctx.drawImage(imgFg, 0, 0, (canvas.width * pst)|0, canvas.height, // source
0, 0, (canvas.width * pst)|0, canvas.height); // dst
}
}
}
body {background:#ccc}
canvas {width:600px;height:auto}
<audio src="https://raw.githubusercontent.com/epistemex/free-music-for-test-and-demo/master/music/kf_colibris.mp3" controls></audio>
<canvas></canvas>
I have a canvas that you can draw things with mouse.. When I click the button It has to capture the drawing and add it right under the canvas, and clear the previous one to draw something new..So first canvas has to be static and the other ones has to be created dynamically with the drawing that I draw .. What should I do can anybody help
here is jsfiddle
http://jsfiddle.net/dQppK/378/
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
painting = false,
lastX = 0,
lastY = 0;
You can create a new canvas the same way you’d create any element:
var newCanvas = document.createElement('canvas');
Then you can copy over your old canvas:
newCanvas.width = oldCanvas.width;
newCanvas.height = oldCanvas.height;
oldCanvas.parentNode.replaceChild(newCanvas, oldCanvas);
ctx = newCanvas.getContext('2d');
But if you’re just looking to clear your drawing surface, what’s wrong with clearRect?
ctx.clearRect(0, 0, canvas.width, canvas.height);
Or, in your case, another fillRect. Updated demo
here's the function i use for this, it is part of a library i made and use to ease a few things about canvas.
I just put it on github in case other function might be be of use, i'll have to make a readme later...
https://github.com/gamealchemist/CanvasLib
with namespaceing removed, the code is as follow to insert a canvas :
// insert a canvas on top of the current document.
// If width, height are not provided, use all document width / height
// width / height unit is Css pixel.
// returns the canvas.
insertMainCanvas = function insertMainCanvas (_w,_h) {
if (_w==undefined) { _w = document.documentElement.clientWidth & (~3) ; }
if (_h==undefined) { _h = document.documentElement.clientHeight & (~3) ; }
var mainCanvas = ga.CanvasLib.createCanvas(_w,_h);
if ( !document.body ) {
var aNewBodyElement = document.createElement("body");
document.body = aNewBodyElement;
};
document.body.appendChild(mainCanvas);
return mainCanvas;
}