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.
Related
How I can make it work accurately so that each picture is exactly shown 50ms after the last one? (And so that the calculated time between seeing the picture and clicking is accurate?)
Background
I want to have a slideshow of images. The images are stored in the images directory and their names are sequential. The slideshow should start after the user clicks on the play button. And the user will click on the stop button a little after the 100th picture. And the code will show the user how many milliseconds after seeing the 100th picture they have clicked on the stop button. The problem is that when I run the code it doesn't work so accurate. It has some lag on some pictures. I was wondering
Here is my code:
<!DOCTYPE html>
<html>
<head>
<script>
var player;
var timestamp;
function preloadImage()
{
var i = 1
while(true){
var img = new Image();
img.src="images/" + i;
i = i + 1;
if (img.height == 0) { break; }
}
}
function next(){
var fullPath=document.getElementById("image").src;
var filename = fullPath.split("/").pop();
var n=parseInt(filename, 10) + 1;
document.getElementById("image").src = "images/" + n;
if (document.getElementById("image").height == 0) { clearInterval(player); }
if (n == 100) { timestamp = new Date().getTime(); }
}
function start(){
clearInterval(player);
document.getElementById("image").src = "images/1";
player = setInterval(function(){ next(); }, 50)
}
function stop(){
clearInterval(player);
alert("You clicked after " + (new Date().getTime() - timestamp) + "ms.")
}
preloadImage()
</script>
</head>
<body>
<img src="images/0" id="image"><br/>
<button type="button" onclick='start()'>start</button>
<button type="button" onclick='stop()'>stop</button>
</body>
</html>
As I stated in my comment, I believe your preloadImage() is not working as you expect. Try running the stack snippet below as a demonstration, and possibly make sure your cache is cleared:
function badPreloadImage () {
var img = new Image();
img.onload = function () {
// check it asynchronously
console.log('good', this.height);
};
img.src = 'https://www.w3schools.com/w3css/img_lights.jpg';
// don't check height synchronously
console.log('bad', img.height);
}
badPreloadImage();
To preload all your images properly, you must do so asynchronously:
function preloadImage (done, i) {
if (!i) { i = 1; }
var img = new Image();
img.onloadend = function () {
if (this.height == 0) { return done(); }
preloadImage(done, i + 1);
};
img.src = "images/" + i;
}
// usage
preloadImage(function () { console.log('images loaded'); });
Other concerns
You should consider using performance.now() instead of new Date().getTime(), as it uses a more precise time resolution (currently 20us, as pointed out in this comment).
You might also consider storing references to each image as an array of Image objects, rather than loading each frame via specifying the src property of an HTMLImageElement, so that the browser can just load the data from memory, rather than loading from cache on the hard drive, or even making a new HTTP request when caching is not enabled.
Addressing each of these issues will allow you to measure timing more precisely by using the proper API and eliminating lag spikes on the DOM thread due to inefficient animation.
I have the following two pieces of code (awful but I have no idea what I'm doing):
var stage = new createjs.Stage("canvas");
createjs.Ticker.on("tick", tick);
// Simple loading for demo purposes.
var image = document.createElement("img");
image.src = "http://dossierindustries.co/wp-content/uploads/2017/07/DossierIndustries_Cactus-e1499205396119.png";
var _obstacle = new createjs.Bitmap(image);
setInterval(clone, 1000);
function clone() {
var bmp = _obstacle.clone();
bmp.x= Math.floor((Math.random() * 1920) + 1);
bmp.y = Math.floor((Math.random() * 1080) + 1);
stage.addChild(bmp);
}
function tick(event) {
stage.update(event);
}
<script>
$j=jQuery.noConflict();
jQuery(document).ready(function($){
var interval = 1;
setInterval(function(){
if(interval == 3){
$('canvas').show();
interval = 1;
}
interval = interval+1;
console.log(interval);
},1000);
$(document).bind('mousemove keypress', function() {
$('canvas').hide();
interval = 1;
});
});
<script src="https://code.createjs.com/easeljs-0.8.2.min.js"></script>
<canvas id="canvas" width="1920" height="1080"></canvas>
Basically what I'm hoping to achieve is that when a user is inactive for x amount of time the full page (no matter on size) slowly fills with the repeated image. When anything happens they all clear and it begins again after the set amount of inactivity.
The code above relies on an external resource which I'd like to avoid and needs to work on Wordpress.
Site is viewable at dossierindustries.co
Rather than interpret your code, I made a quick demo showing how I might approach this.
The big difference is that drawing new images over time is going to add up (they have to get rendered every frame), so this approach uses a cached container with one child, and each tick it just adds more to the cache (similar to the "updateCache" demo in GitHub.
Here is the fiddle.
http://jsfiddle.net/dcs5zebm/
Key pieces:
// Move the contents each tick, and update the cache
shape.x = Math.random() * stage.canvas.width;
shape.y = Math.random() * stage.canvas.height;
container.updateCache("source-over");
// Only do it when idle
function tick(event) {
if (idle) { addImage(); }
stage.update(event);
}
// Use a timeout to determine when idle. Clear it when the mouse moves.
var idle = false;
document.body.addEventListener("mousemove", resetIdle);
function resetIdle() {
clearTimeout(this.timeout);
container.visible = false;
idle = false;
this.timeout = setTimeout(goIdle, TIMEOUT);
}
resetIdle();
function goIdle() {
idle = true;
container.cache(0, 0, stage.canvas.width, stage.canvas.height);
container.visible = true;
}
Caching the container means this runs the same speed forever (no overhead), but you still have control over the rest of the stage (instead of just turning off auto-clear). If you have more complicated requirements, you can get fancier -- but this basically does what you want I think.
I am learning html canvas and below is my html code.
<!DOCTYPE html>
<html>
<head>
<script language="javascript">
function moveImage(x) {
var context = document.getElementById('myCanvas').getContext("2d");
var img = new Image();
img.onload = function () {
context.drawImage(img, x, 259);
}
img.src = "flower.jpg";
}
function startDrawing() {
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
context.beginPath();
context.moveTo(50, 300);
context.lineTo(950, 300);
context.stroke();
var x=50;
setInterval(function() {
x = x+20;
moveImage(x);
}, 1000);
}
</script>
</head>
<body onload="startDrawing()">
<canvas id="myCanvas" width="1000" height="1000">
</body>
</html>
Please find the below output from this code:
How can I remove the traces of 'older frames' (of the flower), as you could see lots of flowers while it is moving from left to right in the screen shot ? Please help the code changes required.
Thanks.
The problem is you aren't clearing the canvas before drawing on it again. You can clear it using clearRect.
setInterval(function() {
// Clear the canvas
context.clearRect(0, 0, canvas.width, canvas.height);
x += 20;
moveImage(x);
}, 1000);
Keep in mind that this will clear the entire canvas. If you're rendering anything else, you'll want to redraw it after the clear.
You need to re-render the frame. Here's some code from an incomplete game I wrote:
var main = function () {
var now = Date.now(),
delta = now - then;
update(delta / 1000);
render();
then = now;
// Request to do this again ASAP
requestAnimationFrame(main);
};
GitHub repo code
My render function contains the context.drawImg() work (getting properly re-rendered), and that's similar to your moveImage function.
Edit: A little explanation. The image traces are previous renderings of the your image at each updated position. Without the frame itself being reset, each move of the image is preserved on the screen, giving the appearance of a trail of images.
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.
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.