How to add sleep and wait logic in javascript - javascript

We are working on visualization of sorting algorithms, required to add sleep and wait logic to help visualize the selected element and the element to which it is compared. After searching li'l bit, we found a code "function sleep(milliseconds){...}" which should work as desired but has failed so far.
In function insertionSort(){...}, the current element is depicted with color red and the element to which it is compared with is depicted with color blue, once the current element is swapped with the other the color of the element is again changed to white from blue (working correctly, verified using debugger), However during execution, these color transformations were not visible (only the element in red is displayed after each iteration)
var element = function(value, color)
{
this.value = value;
this.color = color;
};
var x = [];
x[0] = new element(2, "white");
x[1] = new element(1, "white");
x[2] = new element(5, "white");
x[3] = new element(4, "white");
x[4] = new element(3, "white");
x[5] = new element(7, "white");
x[6] = new element(6, "white");
x[7] = new element(8, "white");
x[8] = new element(10, "white");
x[9] = new element(9, "white");
var i = 1;
var context;
var delayTime = 1000;
function myFunction()
{
var bar = document.getElementById("bar");
width = bar.width;
height = bar.height;
context = bar.getContext("2d");
window.setInterval(insertionSort, 3000);
}
function insertionSort()
{
if(i>=0 && i<x.length)
{
var j = i;
x[j].color = "red";
drawGraph(j);
while(j>0 && x[j-1].value > x[j].value)
{
x[j-1].color = "blue";
x[j].color = "red";
drawGraph();
//need to add delay here
sleep(delayTime);
//swap
var temp = x[j];
x[j] = x[j-1];
x[j-1] = temp;
drawGraph();
// and here...
sleep(delayTime);
x[j].color = "white";
drawGraph();
j = j-1;
}
x[j].color = "white";
i++;
}
else if(i>=x.length)
{
for(k=0;k<x.length;k++)
{
x[k].color = "white";
}
drawGraph();
i=-1;
}
}
function sleep(milliseconds)
{
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++)
{
if ((new Date().getTime() - start) > milliseconds)
{
break;
}
}
}
function drawGraph()
{
context.StrokeStyle = "black";
context.clearRect ( 0 , 0 , width, height);
for(k=0;k<x.length;k++)
{
context.fillStyle = x[k].color;
//x and y coordinate of top left corner of rectangle
context.strokeRect(400+k*20, 18, 20, x[k].value*10);
context.fillRect(400+k*20, 18, 20, x[k].value*10);
}
}
<html>
<head>
<script language="javascript" type="text/javascript" src="../p5.js"></script>
<!-- uncomment lines below to include extra p5 libraries -->
<!--<script language="javascript" src="../addons/p5.dom.js"></script>-->
<!--<script language="javascript" src="../addons/p5.sound.js"></script>-->
<script language="javascript" type="text/javascript" src="sketch.js"></script>
<!-- this line removes any default padding and style. you might only need one of these values set. -->
<style> body {padding: 0; margin: 0;} </style>
</head>
<body>
<button onclick="myFunction()">Try it</button>
<canvas id="bar" width="1000" height="400" style="border:2px"></canvas>
</body>
</html>

The approach to used in that implementation of sleep() would be terrible in any programming language, because it consumes a lot of CPU while waiting. In JavaScript, however, it's especially bad, because a JavaScript program is required to relinquish control frequently; it is not permitted to keep computing for an extended period of time. In Chrome browser, for example, Chrome will consider the program to be unresponsive, and will suggest to the user that they kill it.
But even if that weren't the case, it won't produce the desired effect, which I assume is that some animation happens on the screen, with some delay from one step to the next. The way JavaScript works in the browser, is that any changes you make to the page get rendered when your program relinquishes control; nothing updated on-screen while any JavaScript code is running. If you call a sleep function like that one, you are not relinquishing control, you are running JavaScript the whole time, and therefore the browser will not update the screen during that time. It will only update when your entire insertionSort method returns, and the browser has that 3000ms time window (from your setInterval) to take care of its own stuff (rendering).
Unfortunately, you will have to find a way to split up that algorithm, so that each step that you want to be distinctly visible to the user happens in its own timed callback.
It will probably be something along the lines of:
function stepOne() {
do the first bit;
setTimeout(secondStep, delay)
}
secondStep() {
do some more stuff;
setTimeout(thirdStep, delay)
}
and so on. The way you control the speed of the animation is with the delay parameter from one step to the next.
It's going to be tricky, especially because you aren't just trying to animate Insertion Sort, but various algorithms. So then, do you break them all up as in: insertionSortStepOne/Two/Three, shellSortStepOne/Two/Three? that would be quite ugly.
Depending on how ambitious you are, and how much you want to get out of this assignment, you might explore this feature of ES6 (a newer version of JavaScript)
function*
What this lets you do is let your function, with all its nested loops, remain structured pretty much as it is, but you insert points where it relinquishes control. Later, it is called back to continue from the point where it left off. You would use setTimeout or setInterval to do that. I've not experimented with this myself, but it seems super-cool.

Related

Making a racing game in javascript. When trying to simulate oncoming cars, I am not able to create more cars when the other cars have reached the end

I am trying to make a racing game where the user must try to avoid the oncoming cars. To create the oncoming cars, I have created a class called obstacles. In addition, I have a function that draws the obstacles and in that function, I have an if statement that checks whether the cars have reached close to the bottom of the canvas so that I can create the next 3 obstacles(Cars). However, the program seems to be inconsistent with creating the new cars and sometimes doesn't even work at all. How should I fix this? Thank you in Advance. Here is my code so far:
<body>
<p><span id="val">here</span></p>
<canvas width="400" height="500" id="myCanvas"></canvas>
<script>
"use strict"
var a=document.getElementById("myCanvas");
var c=a.getContext("2d");
var obs=[];
function randomNumber(){
var rand=(Math.random()*400)+1;
if(rand<400/3){return 50}
else if(rand>400/3&&rand<400*2/3){return 180}
else if(rand>rand<400*2/3){return 300}
}
class obstacles{
constructor(x){
this.x=x;
this.y=-(Math.floor(Math.random()*300)+1);
}
show(){
c.fillStyle="blue";
c.beginPath();
c.rect(this.x,this.y,40,70);
c.fill();
c.closePath();
}
move(){
this.y+=3;
}
}
createObstacles();
function createObstacles(){
var x;
for(var i=0;i<3;i++){
x=randomNumber();
obs[i]=new obstacles(x);
}
}
function draw(){
c.clearRect(0,0,400,500);
var x;
for(var j=0;j<obs.length;j++){
x=randomNumber();
obs[j].show();
obs[j].move();
}
for(var i=0;i<obs.length;i++){
if(obs[i].y==350){
obs.push(new obstacles(x))
}
}
}
window.setInterval(draw,17);
</script>
</body>
When you create an obstacle, it is given a random y position. Then, whenever it moves, its y changes by 3 in your move function. However, when you go to check if it has reached the bottom, you only check if the y is exactly 350: obs[i].y==350. It is completely possible that, given where the obstacle started, on one of its moves it moved 3 spaces and completely skipped 350! In this case your code would never trigger.
An easy solution to this is simply to change your == to a > so that it checks if the y ever exceeds 350, not just when it is exactly equal to it:
for(var i=0;i<obs.length;i++){
if(obs[i].y>350){
obs.push(new obstacles(x))
}
}
Hopefully this solves your problem. I haven't actually test-run your code though, so there might be other bugs. Good luck with your project!

How to briefly display a value consistently across browsers

I am trying to create a test that explores the boundaries of our subconscious. I want to briefly display a number and see if the user can use their intuition to guess the value - is their subconscious able to read the number faster than their conscious self. So I am trying to flash a number onto the screen for a few milliseconds. Chrome does not seem to behave as well as Edge in with this code. How can I make it work more consistently across browsers?
I have tried various ways of hiding and revealing the number. Finally ending up with this version.
<script>
function onLoad() {
numberOfPoints = Math.floor(Math.random() * (99 - 9 + 1)) + 9;
document.f.points.value = numberOfPoints;
setTimeout(hideRun, 3000);
}
function hideRun() {
hide();
document.getElementById("hiddenNumber").innerHTML = numberOfPoints;
document.getElementById("hiddenNumber").style.display = 'block';
setTimeout(hide, 5);
}
function hide() {
document.getElementById("hiddenNumber").style.display = 'none';
}
</script>
<body onload="onLoad()">
<div id=hiddenNumber style="display: block;">GET READY</div>
</body>
In this case I am hoping to display the Get Ready text for 3 seconds, then show a random number for 5 milliseconds. Although I have no way to actually measure it, the 5 milliseconds on a chrome browser is a lot longer than with the Edge browser.
You can try it yourself here: Test Timer
Thinking in terms of time is not reliable here, because you don't know when the browser will paint to screen, nor when the screen will do its v-sync.
So you'd better think of it in term of frames.
Luckily, we can hook callbacks to the before-paint event, using requestAnimationFrame method.
let id = 0;
btn.onclick = e => {
cancelAnimationFrame(id); // stop potential previous long running
let i = 0,
max = inp.value;
id = requestAnimationFrame(loop);
function loop(t) {
// only at first frame
if(!i) out.textContent = 'some dummy text';
// until we reached the required number of frames
if(++i <= max) {
id= requestAnimationFrame(loop);
}
else {
out.textContent = '';
}
}
};
Number of frames: <input type="number" min="1" max="30" id="inp" value="1"><button id="btn">flash</button>
<div id="out"></div>
Can you try a 2D canvas and see if that helps?
<html>
<head>
<script>
var numberOfPoints;
var canvas;
var context;
function onLoad() {
canvas = document.getElementById("myCanvas");
context = canvas.getContext("2d");
context.font = "30px Arial";
// context.fillText("...", 10, 50);
numberOfPoints = Math.floor(Math.random() * (99 - 9 + 1) ) + 9;
setTimeout(hideRun, 3000);
}
function hideRun() {
context.fillText(numberOfPoints, 10, 50);
setTimeout(hide, 5);
}
function hide() {
context.clearRect(0, 0, canvas.width, canvas.height);
}
</script>
</head>
<body onload="onLoad()">
<canvas id="myCanvas"></canvas>
</body>
</html>
In my tests, it seems to show the number more consistently versus the CSS property, but to be absolutely sure, I would recommend a 60fps screen reader to record and validate the cross-browser accuracy.

How would I create a "Choose your Own Adventure game" on Canvas?

New to coding, and I decided to create an interactive, onclick adventure game using 6 images I pulled from online.
I added the the first image to canvas and put a cool little animation in front of the image on canvas, but now I'm left with a big problem.
This whole game is done on ONE canvas, with MULTIPLE, as well as BRANCHING on click events.
Example: On the title screen, the user can either select "Start Game", or "Credits" (done as clickable text).
If the user selects "Start Game", the screen transitions to picture number 2, after clicking again, picture number 2 blurs out and narration text will appear in front of the user. However, if they select "Credits", the screen transitions instead to a different picture, picture number 3. On picture number 3, the credits display in an slow automatic scrolling up fashion.
I've looked this up online, but I usually get answers in a general ballpark of what I'm looking for, some results I've found are switch cases(not much experience with them, let alone make a BRANCHING switch case), the Undum framework for interactive story telling, event handlers, etc.
I haven't worked much with Javascript or with canvas, and before I tackled this project, I thought it best to present my project question outright and get a experienced opinion on it, as well as break the project down piece by piece and form some kind of method of attack.
Knowing how this project is going to work, what resources or coding methods would you guys recommend for me to use? What would this "branching" animation tree even look like?
Here is what my code is so far:
-- HTML --
<html>
<head>
<title>Detective</title>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= csrf_meta_tags %>
<!-- load in the jquery -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0-alpha1/jquery.js">
</script>
</head>
<body>
</body>
</html>
-- CSS --
body{
background:#000;
width: 100%;
height: 100%;
overflow:hidden;
}
canvas{
/*the code below this activates the canvas border, turn it on to visibly see how big the canvas is */
/* border: 1px solid red;*/
position: absolute;
top:0;
bottom: 0;
left: 0;
right: 0;
margin:auto;
display: none;
}
#font-face{
font-family: 'Betty Noir Regular';
src: url('/assets/bettynoir.ttf');
}
-- JS --
var canvas = document.createElement('canvas');
var w = canvas.width = 800,
h = canvas.height = 400;
var c = canvas.getContext('2d');
var img = new Image();
img.src = 'http://oi41.tinypic.com/4i2aso.jpg';
var background = new Image();
background.src = "https://i2.wp.com/i2.listal.com/image/2669447/500full.jpg";
var position = {x : 410, y : 238};
document.body.appendChild(canvas);
var particles = [];
var random = function(min, max){
return Math.random()*(max-min)*min;
};
function Particle(x, y){
this.x = x;
this.y = y;
this.velY = -2;
this.velX = (random(1, 10)-5)/10;
this.size = random(3, 5)/10;
this.alpha = 1;
this.update = function(){
this.y += this.velY;
this.x += this.velX;
this.velY *= 0.99;
if(this.alpha < 0){this.alpha = 0;}
c.globalAlpha = this.alpha;
c.save();
c.translate(this.x, this.y);
c.scale(this.size, this.size);
c.drawImage(img, -img.width/2, -img.height/2);
c.restore();
this.alpha *= 0.96;
this.size += 0.02;//
};
}
var draw = function(){
var p = new Particle(position.x, position.y);
particles.push(p);
// draw the background image before you draw the particles
c.drawImage(background,160,0);
c.font="20px Betty Noir Regular";
c.fillStyle = "white";
c.fillText("Start",500,200);
c.font="20px Betty Noir Regular";
c.fillStyle = "white";
c.fillText("Credits",500,250);
while(particles.length > 500) particles.shift();
for(var i = 0; i < particles.length; i++)
{
particles[i].update();
}
};
setInterval(draw, 3500/60);
$(document).ready(function() {
$("canvas").fadeIn(7000);
});
$(document).ready(function() {
$("#noir-song").get(0).play();
});
Looks good.
I would say get rid of setInterval as it will only cause you problems in the long run. As you have a particle system, some devices may not handle the load that well and as setInterval does not check to see if the last render job is done befor it puts a call on the call stack you could end up overflowing the call stack and crashing the app.
Use window.requestAnimationFrame(functionName)
As follows
// your main render function
function draw(){
// do your stuff
requestAnimationFrame(draw); // needs to be call for every new frame
}
draw(); // start it all happening.
requestAnimationFrame is sensitive to rendering load, will try its best to maintain an even frame rate 1/60th or 60fps. Syncs with monitor refresh rates if possible so you don't get shearing. It is designed for animations (hence the name) so give it a try.
I would ask "why use jquery?" you will be using browsers that support canvas and jquery offers no real benefit but that of legacy browser support, and only adds to the complexity of the pages and increases the load time. You only use it for page ready, so seems a bit of a waste. Remove jQuery and use onload to replace the ready calls
window.addEventListener("load",function(){
// your startup up code
});
A better way of creating particles. Using new is slow especially when you are creating many instances of the same thing.
Try
// define the function to update once. In the new Particle() version
// javascript actually has to create this function every time you create a particle.
// This makes it quicker.
var particleUpdate = function(){
this.y += this.velY;
this.x += this.velX;
this.velY *= 0.99;
// a quicker way to clamp a value
// if(this.alpha < 0){this.alpha = 0;} // done in the following line
c.globalAlpha = Math.max(0,this.alpha); // returns the max value so while
// alpha > 0 it returns alpha
// when alpha < 0 returns max
// which is 0;
//c.save(); // dont need this here do it outside the loop that renders
// the particles. Save and restore are expensive in terms
// of GPU performance and should be avoided if possible.
// the translate and scale can be done in one call halving the time
// to do the same thing
//c.translate(this.x, this.y);
//c.scale(this.size, this.size);
// use setTransform does the same as translate and scale but in one step
c.setTransform(this.size,0,0,this.size,this.x,this.y)
c.drawImage(img, -img.width/2, -img.height/2);
// c.restore(); see above
this.alpha *= 0.96;
this.size += 0.02;//
};
var createParticle = function(x,y){
return {
x:x,
y:y,
velY:-2,
velX:(random(1, 10)-5)/10,
size:random(3, 5)/10,
alpha = 1,
update:particleUpdate,
}
}
Then to create
particles.push(createParticle(position.x, position.y));
Update and cull as you are already doing just add the save and restore outside the loop. Also getting "particles.length" is slower than a direct variable. So rewrite the loop as follows
var len = particles.length
c.save()
for(var i = 0; i < len; i++){
particles[i].update();
}
c.restore();
It will not make much difference in this case as there is not that much going on. But as you push for more and more fx, more bang per frame performance will become a major problem. Learning efficient methods early on will help.

Capture group of HTML5 video screenshots

I am trying to generate a group of thumbnails in the browser out of a HTML5 video using canvas with this code:
var fps = video_model.getFps(); //frames per second, comes from another script
var start = shot.getStart(); //start time of capture, comes from another script
var end = shot.getEnd(); //end time of capture, comes from another script
for(var i = start; i <= end; i += 50){ //capture every 50 frames
video.get(0).currentTime = i / fps;
var capture = $(document.createElement("canvas"))
.attr({
id: video.get(0).currentTime + "sec",
width: video.get(0).videoWidth,
height: video.get(0).videoHeight
})
var ctx = capture.get(0).getContext("2d");
ctx.drawImage(video.get(0), 0, 0, video.get(0).videoWidth, video.get(0).videoHeight);
$("body").append(capture, " ");
}
The the amount of captures is correct, but the problem is that in Chrome all the canvases appear black and in Firefox they always show the same image.
Maybe the problem is that the loop is too fast to let the canvases be painted, but I read that .drawImage() is asynchronous, therefore, in theory, it should let the canvases be painted before jumping to the next line.
Any ideas on how to solve this issue?
Thanks.
After hours of fighting with this I finally came up with a solution based on the "seeked" event. For this to work, the video must be completely loaded:
The code goes like this:
var fps = video_model.getFps(); //screenshot data, comes from another script
var start = shot.getStart();
var end = shot.getEnd();
video.get(0).currentTime = start/fps; //make the video jump to the start
video.on("seeked", function(){ //when the time is seeked, capture screenshot
setTimeout( //the trick is in giving the canvas a little time to be created and painted, 500ms should be enough
function(){
if( video.get(0).currentTime <= end/fps ){
var capture = $(document.createElement("canvas")) //create canvas element on the fly
.attr({
id: video.get(0).currentTime + "sec",
width: video.get(0).videoWidth,
height: video.get(0).videoHeight
})
.appendTo("body");
var ctx = capture.get(0).getContext("2d"); //paint canvas
ctx.drawImage(video.get(0), 0, 0, video.get(0).videoWidth, video.get(0).videoHeight);
if(video.get(0).currentTime + 50/fps > end/fps){
video.off("seeked"); //if last screenshot was captured, unbind
}else{
video.get(0).currentTime += 50/fps; //capture every 50 frames
}
}
}
, 500); //timeout of 500ms
});
This has worked for me in Chrome and Firefox, I've read that the seeked event can be buggy in some version of particular browsers.
Hope this can be useful to anybody. If anyone comes up with a cleaner, better solution, it would be nice to see it.

javascript / jquery / processing.js - how to most effectively create and animate canvas elements

I'm trying to build an application which, based on various user interactions, allows for various ellipse based visuals to be added to the stage and then animated very simply. I've currently got a basic demo set up where javascript / jquery communicates with processing.js, but it just seems like really inefficient code, because processing relies on running a continuous loop in order to draw to the screen. I'm wondering, one, if the way I'm doing it will be effective on a larger scale, and two, if there's a better technology or method to use. I come from a flash background where nothing on screen is changed or drawn/animated unless a function is triggered telling it to animate, which seems sensible. Anyway, here's my code:
HTML / JS:
<!DOCTYPE HTML>
<html lang="en">
<head>
<title>Processing</title>
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js" type="text/javascript"></script>
<script src="js/processing-1.3.6.min.js"></script>
<script src="processing/Tween.lib"></script>
</head>
<body>
<canvas id="circles" data-processing-sources="js/drawCircles.js"></canvas>
<div id="clicker">Click</div>
<script>
window.Processing.data = {};
var dataRef = window.Processing.data;
var animInterval;
dataRef.circleArray = new Array();
$('#clicker').click(function(){
var circle = {};
circle.radius = 50;
dataRef.circleArray.push(circle)
var from = {property: 50};
var to = {property: 75};
jQuery(from).animate(to, {
duration: 300,
step: function() {
for (var i in dataRef.circleArray){
circle.radius = this.property;
}
}
});
})
</script>
</body>
</html>
PROCESSING.JS
// Global variables
float radius = 1.0;
int X, Y;
int nX, nY;
int delay = 16;
// Setup the Processing Canvas
void setup(){
// Fill canvas grey
background( 100 );
size( 200, 200 );
strokeWeight( 10 );
frameRate( 15 );
X = width / 2;
Y = width / 2;
nX = X;
nY = Y;
}
// Main draw loop
void draw(){
var dataRef = window.Processing.data;
for (var i in window.Processing.data.circleArray){
radius = dataRef.circleArray[i].radius;
// Set fill-color to blue
fill( 0, 121, 184 );
// Set stroke-color white
stroke(255);
// Draw circle
ellipse( X+(i*10), Y, radius, radius );
}
}
If you want to control when Processing.js draws to the canvas, you have two options. In both cases, the first thing you'll want to do is get access to the Processing instance:
var p = Processing.instances[0];
Now you can make all the Processing API calls you want from JavaScript. You could call noLoop() in your sketch's setup() function, and then inside your jQuery animation loop you could call p.redraw(), which will animate one frame.
In Processing.js we attach all of the functions to the Processing instance. So another option is creating your own function in the sketch, and call it with:
var p = Processing.instances[0];
p.drawEllipses(radius);
You could even pass the data to it in the function parameters, removing the need for windows.Processing.data.
For what you want to do, you might prefer using another library such as paperjs http://paperjs.org/

Categories