I have been working on a canvas animation of a circle that moves in a random direction, and every second the direction changes. I do that by changing a velocity vector every second by repeatedly calling a function using setInterval, while changing the position of the circle's center and drawing it in another function using requestAnimationFrame. The result is an empty canvas, and I'm not sure why. The code is split into the following three files:
random_ball.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Random Ball</title>
<link rel="stylesheet" href="random_ball.css">
</head>
<body>
<canvas id="canvas"></canvas>
<script src="random_ball.js"></script>
</body>
</html>
random_ball.css
html, body{
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
overflow: hidden;
margin: 0;
padding: 0;
}
canvas{
position: absolute;
width: 50vw;
height: 50vh;
top: 25vh;
left: 25vw;
border-style: solid;
margin: 0;
padding: 0;
}
random_ball.js
const canvas = document.getElementById('canvas')
const bounds = canvas.getBoundingClientRect()
const TWO_PI = 2*Math.Pi
const R = 25
const MAX_Y = bounds.height - R
const MAX_X = bounds.width - R
const ctx = canvas.getContext('2d')
function randomInterval(min, max){
return Math.random()*(max - min) + min
}
function randomVelocity(){ // velocity is pixels/sec
VEL.X = randomInterval(-POS.X + R, MAX_X - POS.X)
VEL.Y = randomInterval(-POS.Y + R, MAX_Y - POS.Y)
console.log('VEL: ' + JSON.stringify(VEL))
}
var POS = {X: bounds.height/2, Y: bounds.width/2}
var VEL = {X: 0, Y: 0}
var LAST_TIME = window.performance.now()
function drawCircle(color){
ctx.strokeStyle = color
ctx.beginPath()
ctx.moveTo(POS.X, POS.Y)
ctx.arc(POS.X, POS.Y, R, 0, TWO_PI, true)
ctx.stroke()
}
function draw(timestamp){
var dt = (timestamp - LAST_TIME)/1000
var dx = VEL.X*dt
var dy = VEL.Y*dt
drawCircle('#FFFFFF') // erase the old drawing by making it white
POS.X += dx
POS.Y += dy
//console.log('POS: ' + JSON.stringify(POS))
drawCircle('#000000')
LAST_TIME = timestamp
requestAnimationFrame(draw)
}
randomVelocity()
drawCircle('#000000')
setInterval(randomVelocity, 1000) //change velocity every second
requestAnimationFrame(draw)
I suspect that drawing white over the old circle means the picture is changing too fast for me to see it.
You might want to familiarize yourself with JavaScript syntax rules, and possibly a code checker like JSHint.
The main problem with your code is that you're referencing a nonexistent property of the Math object. Math has no Pi property. It's spelled PI (all caps). JavaScript is case sensitive about these things.
Next: a more common practice for clearing the canvas is to use ctx.clearRect(0, 0, width, height). Here's a JSFiddle to help you see the difference: https://jsfiddle.net/Hatchet/dy2dognp/
Related
I started learning Three js and I was looking for a way to convert a color map into a normal map. What I want to do is to try and make the normal map based on this color map [image 1], by changing the pixels based on their color so it looks like this normal map [image 2]. I don't want to simply upload the files since I'm trying to minimize the weight of the project as much as possible. Here is what I already tried :
let img = new Image();
img.src = './texture/color.jpg';
img.onload = function () {
let canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
document.getElementById('body').appendChild(canvas)
const c = canvas.getContext('2d')
c.clearRect(0, 0, canvas.width, canvas.height);
c.fillStyle = '#EEEEEE';
c.fillRect(0,0,canvas.width, canvas.height);
//draw background image
c.drawImage(img, 0, 0);
//draw a box over the top
c.fillStyle = "rgba(200, 0, 0, 0)";
c.fillRect(0, 0, canvas.width, canvas.height);
draw(c, canvas);
};
function draw(c, canvas)
{
let img2 = c.getImageData(0, 0, canvas.width,canvas.height);
console.log(img2.data)
let d = img2.data;
for (let i=0; i<d.length; i+=4) {
let r = d[i];
let g = d[i+1];
let b = d[i+2];
v1 = r < 75 ? r / (50 - r) : r * (255 - r);
v2 = g > 75 ? g / (50 - g) : g * (255 - g);
v3 = b > 75 ? b / (50 - b) : b * (255 - b);
d[i] = v1;
d[i+1] = v2;
d[i+2] = v3;
}
console.log(img2.data)
c.putImageData(img2, 0, 0);
}
I can't say what Three.js can or can't do because all I really know of it is that it makes integrating 3d assets with canvases a breeze.
Aside from that, I wrote a pure javascript function that serves the purpose of generating normal maps from color maps quite effectively. Keep in mind, however, that this is a quick port to js of a function I wrote for C# winforms about 4 years ago, one that loops through all the pixels of a given image to extrapolate the data required for the conversion. It's slow. Seriously, painfully slow and it is so because getting nice, crisp, accurate normal maps from recursive algorithms is painfully slow.
But it does exactly what you want it to do; generate a very nice, clean, precise normal map from a given color map and it's simple enough to understand its functionality.
I've set this up as a live demo so you can see it / feel it in action.
There is, of course, a much faster solution involving making a single call for pixel data, iterating over its corresponding 1d array, saving calculated data back to that array then plopping the entire array, itself, down on the output canvas all at once but that involves some interesting virtual multi-dimensional trickery for Sobel but, for the sake of providing a clear, understandable example that works, I'm going with old-school nested recursion so you can see Sobel in action and how pixels are manipulated to arrive at normalization.
I did not implement any fancy asynchronous updates so you'll only know this is processing because, once initiated, the hand cursor used for the button won't return to the default arrow until map generation is complete.
I've also included 4 variations of your original image to play with, all in code with 3 of the 4 commented out. The app starts at 256x256 as the time it takes to generate a normal map from that with recursion is reasonable. Their sizes range from 128 to the original 1024, though I highly advise not engaging the full scale variant as your browser may whine about how long the operation takes.
As with the C# variant, you can implement a means by which client can control the intensity of the resulting normal calculations by adjusting the brightness parameters. Beyond the C# variant, this definitely can be a basis for generating normal maps to visualize in real-time as applied to geometry with Three.js. And by "real-time", I mean however long it takes to generate an x-scale map with nested recursion because the actual application of a completed map to geometry occurs in milliseconds.
Here's a screenshot of the results after processing the 256x256:
To accompany the live demo, here's the code:
normalize.htm
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Normalizer</title>
<link rel="stylesheet" href="css/normalize.css">
</head>
<body onload="startup()">
<canvas id="input" class="canvas"></canvas>
<canvas id="output" class="canvas"></canvas>
<div class="progress">
<input type="button" class="button" onclick="totallyNormal()" value="Normalize!" />
<span id="progress">Ready to rock and / or roll on your command!</span>
</div>
<script src="js/normalize.js"></script>
</body>
</html>
normalize.css
html {
margin: 0px;
padding: 0px;
width: 100vw;
height: 100vh;
}
body {
margin: 0px;
padding: 0px;
width: 100vw;
height: 100vh;
overflow: hidden;
font-family: "Arial Unicode MS";
background: linear-gradient(330deg, rgb(150, 150, 150), rgb(200, 200, 200));
background-color: rgb(200, 200, 200);
display: flex;
align-items: center;
justify-content: center;
}
.canvas {
outline: 1px solid hsla(0, 0%, 0%, 0.25);
}
.progress {
position: absolute;
top: 0px;
left: 0px;
width: 100%;
height: 40px;
display: flex;
}
.progress span {
width: calc(100% - 160px);
height: 40px;
line-height: 40px;
color: hsl(0, 0%, 0%);
text-align: center;
}
input[type="button"] {
margin: 0px;
width: 120px;
height: 40px;
cursor: pointer;
display: inline;
}
normalize.js
// Javascript Normal Map Generator
// Copyright © Brian "BJS3D" Spencer 2022
// Incorporating W3C proposed algorithm to
// calculate pixel brightness in conjunction
// with the Sobel Operator.
var input, output, ctx_i, ctx_o, w, h;
function startup() {
var img;
input = document.getElementById("input");
ctx_i = input.getContext("2d");
ctx_i.clearRect(0, 0,input.width, input.height);
img = new Image();
img.crossOrigin = "Anonymous";
//img.src = "https://i.imgur.com/a4N2Aj4.jpg"; //128x128 - Tiny but fast.
img.src = "https://i.imgur.com/wFe4EG7.jpg"; //256x256 - Takes about a minute.
//img.src = "https://i.imgur.com/bm4pXrn.jpg"; //512x512 - May take 5 or 10 minutes.
//img.src = "https://i.imgur.com/aUIdxHH.jpg"; //original - Don't do it! It'll take hours.
img.onload = function () {
w = img.width - 1;
h = img.height - 1;
input.width = w + 1;
input.height = h + 1;
ctx_i.drawImage(img, 0, 0);
output = document.getElementById("output");
ctx_o = output.getContext("2d");
output.width = w + 1;
output.height = h + 1;
};
}
function totallyNormal() {
var pixel, x_vector, y_vector;
for (var y = 0; y < w + 1; y += 1) {
for (var x = 0; x < h + 1; x += 1) {
var data = [0, 0, 0, 0, x > 0, x < w, y > 1, y < h, x - 1, x + 1, x, x, y, y, y - 1, y + 1];
for (var z = 0; z < 4; z +=1) {
if (data[z + 4]) {
pixel = ctx_i.getImageData(data[z + 8], data[z + 12], 1, 1);
data[z] = ((0.299 * (pixel.data[0] / 100)) + (0.587 * (pixel.data[1] / 100)) + (0.114 * (pixel.data[2] / 100)) / 3);
} else {
pixel = ctx_i.getImageData(x, y, 1, 1);
data[z] = ((0.299 * (pixel.data[0] / 100)) + (0.587 * (pixel.data[1] / 100)) + (0.114 * (pixel.data[2] / 100)) / 3);
}
}
x_vector = parseFloat((Math.abs(data[0] - data[1]) + 1) * 0.5) * 255;
y_vector = parseFloat((Math.abs(data[2] - data[3]) + 1) * 0.5) * 255;
ctx_o.fillStyle = "rgba(" + x_vector + "," + y_vector + ",255,255)";
ctx_o.fillRect(x, y, 1, 1);
}
}
document.getElementById("progress").innerHTML = "Normal map generation complete.";
}
I am creating a game for coursework, two tanks placed on a canvas with input boxes for the initial velocity and angle of the turret, then a button to fire a projectile (currently a div element in the shape of a circle), which calls a function in this case it is fire1. I have messed around for a few days and can't seem to get it to work, "bullet" is my div element.
function fire1 () {
var canvas = document.getElementById("canvas")
var bullet = document.getElementById("bullet");
bullet.style.visibility = "visible"
var start = null;
var intialVelocity = velocity1.value
var angle = angle1.value
var g = 9.81;
var progress, x, y;
function step(timestamp) {
if(start === null) start = timestamp;
progress = (timestamp - start)/1000;
x = (turret1.x + 80) + (intialVelocity*progress)
y = (turret1.y - 400) + (intialVelocity*progress)*Math.sin(angle*toRadians) - (0.5*g*(progress^2));//)
bullet.style.left = x + "px";
bullet.style.bottom = y + "px";
requestAnimationFrame(step);
}
requestAnimationFrame(step);
}
Below is my css bit of my bullet.
#bullet {
position: absolute;
left: 0;
bottom: 50%;
width: 1em;
height: 1em;
border-radius: 0.5em;
background: red;
visibility: hidden;
}
I am very new to javascript, css and html so help would be very appriciated, I'm trying to incorporate the trajectory formula will this work? I also want it to be animated so it follows a path when fired. Thanks
I fixed this a long time ago but forgot to update with solution, below is how x and y are calculated for the trajectory:
x = ((turret.anchorX + negative*(initialVelocity*progress*Math.cos(angle*toRadians)))); //x-coordinate for bullet calculated by using x=ut.
y = ((720 - turret.anchorY + (initialVelocity*progress*Math.sin(angle*toRadians)) + (0.5*g*(Math.pow(progress,2))))); //y-coordinate for bullet calculated by usnig ut+0.5at^2.
I've just gotten started with coding on my new school, and I'm making a website as my first project. Now I'm trying to make this "matrix" animation appear as my background (made mostly in javascript), but I just cant get it to work. Could anyone help me with this issue? If it's even possible, that is. Thanks. (I know the code is kind of messy... first time posting, no idea how to do this)
HTML
<div class="inhoud">
<div id="container">
<div id="over_stuff">
Here's some stuff over #pixie!
</div>
<canvas id="c"></canvas>
</div>
</div>
CSS
/*basic reset*/
#container {
overflow: hidden;
position: relative;
z-index: 1;
}
#c {
z-index: 0;
background: #010222;
background:
}
#over_stuff {
color: white;
position: absolute;
top: 0px;
left: 0px;
z-index: 5;
padding: 10px;
}
* {
margin: 0;
padding: 0;
}
/*adding a black bg to the body to make things clearer*/
body {
background: black;
}
canvas {
display: block;
}
Javascript
var c = document.getElementById("c");
var ctx = c.getContext("2d");
//making the canvas full screen
c.height = window.innerHeight;
c.width = window.innerWidth;
//chinese characters - taken from the unicode charset
var chinese = "dirk";
//converting the string into an array of single characters
chinese = chinese.split("");
var font_size = 10;
var columns = c.width / font_size; //number of columns for the rain
//an array of drops - one per column
var drops = [];
//x below is the x coordinate
//1 = y co-ordinate of the drop(same for every drop initially)
for (var x = 0; x < columns; x++)
drops[x] = 1;
//drawing the characters
function draw() {
//Black BG for the canvas
//translucent BG to show trail
ctx.fillStyle = "rgba(0, 0, 0, 0.05)";
ctx.fillRect(0, 0, c.width, c.height);
ctx.fillStyle = "#555"; //green text
ctx.font = font_size + "px arial";
//looping over drops
for (var i = 0; i < drops.length; i++) {
//a random chinese character to print
var text = chinese[Math.floor(Math.random() * chinese.length)];
//x = i*font_size, y = value of drops[i]*font_size
ctx.fillText(text, i * font_size, drops[i] * font_size);
//sending the drop back to the top randomly after it has crossed the screen
//adding a randomness to the reset to make the drops scattered on the Y axis
if (drops[i] * font_size > c.height && Math.random() > 0.975)
drops[i] = 0;
//incrementing Y coordinate
drops[i]++;
}
}
setInterval(draw, 33);
I FIXED IT! all it took was to add
Postion:fixed to the code, and then give that a z-index of -10. thanks for taking the time to look at my code anyway ;)
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>
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.