I'm new on learning javascript. I'm working on my school project and I faced some problems. I want to create a countdown timer and also display it out indicating "3,2,1...". Creating this for a photo capturing system by using HTML5 and JS.
When user click on the button that said CAPTURE, there will be a line of word written down "Photo will be captured in (3,2,1)". Then call the function below.
context.drawImage(video, 0, 0, 640, 480);
I know can done by using
setTimeout(function(){context.drawImage(video, 0, 0, 640, 480)},3000);
but I don't know how to link it with the button that written "CAPTURE", and display the indicator by saying 3, 2, and then 1.
sorry for the poor english that I wrote.
Try following code
var currectValue = 0;
var timer = -1;
function onTimer(){
if(currentValue == 0){
clearTimeout(timer);
return;
}
currentValue -= 1;
//Your code
}
function startCountdown(seedValue){
currentValue = seedValue;
if(-1 != timer){
clearTimeout(timer);
timer = -1;
}
timer = setInterval(onTimer, 1000);
}
startCountdown(3);
Try this example,
function timer(object){
if(object==0)
context.drawImage(video, 0, 0, 640, 480);
else{
object = object -1;
setTimeout("timer('"+object+"')",1000);
}
}
Externally call like onclick="timer(3)"
Related
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.
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 want to capture a frame from video every 5 seconds.
This is my JavaScript code:
video.addEventListener('loadeddata', function() {
var duration = video.duration;
var i = 0;
var interval = setInterval(function() {
video.currentTime = i;
generateThumbnail(i);
i = i+5;
if (i > duration) clearInterval(interval);
}, 300);
});
function generateThumbnail(i) {
//generate thumbnail URL data
var context = thecanvas.getContext('2d');
context.drawImage(video, 0, 0, 220, 150);
var dataURL = thecanvas.toDataURL();
//create img
var img = document.createElement('img');
img.setAttribute('src', dataURL);
//append img in container div
document.getElementById('thumbnailContainer').appendChild(img);
}
The problem I have is the 1st two images generated are the same and the duration-5 second image is not generated. I found out that the thumbnail is generated before the video frame of the specific time is displayed in < video> tag.
For example, when video.currentTime = 5, image of frame 0s is generated. Then the video frame jump to time 5s. So when video.currentTime = 10, image of frame 5s is generated.
Cause
The problem is that seeking video (by setting it's currentTime) is asynchronous.
You need to listen to the seeked event or else it will risk take the actual current frame which is likely your old value.
As it is asynchronous you must not use the setInterval() as it is asynchronous too and you will not be able to properly synchronize when the next frame is seeked to. There is no need to use setInterval() as we will utilize the seeked event instead which will keep everything is sync.
Solution
By re-writing the code a little you can use the seeked event to go through the video to capture the correct frame as this event ensures us that we are actually at the frame we requested by setting the currentTime property.
Example
// global or parent scope of handlers
var video = document.getElementById("video"); // added for clarity: this is needed
var i = 0;
video.addEventListener('loadeddata', function() {
this.currentTime = i;
});
Add this event handler to the party:
video.addEventListener('seeked', function() {
// now video has seeked and current frames will show
// at the time as we expect
generateThumbnail(i);
// when frame is captured, increase here by 5 seconds
i += 5;
// if we are not past end, seek to next interval
if (i <= this.duration) {
// this will trigger another seeked event
this.currentTime = i;
}
else {
// Done!, next action
}
});
If you'd like to extract all frames from a video, see this answer. The example below assumes that you want to extract a frame every 5 seconds, as OP requested.
This answer requires WebCodecs which is supported in Chrome and Edge as of writing.
<canvas id="canvasEl"></canvas>
<script type="module">
import getVideoFrames from "https://deno.land/x/get_video_frames#v0.0.8/mod.js"
let ctx = canvasEl.getContext("2d");
// `getVideoFrames` requires a video URL as input.
// If you have a file/blob instead of a videoUrl, turn it into a URL like this:
let videoUrl = URL.createObjectURL(fileOrBlob);
const saveFrameEverySeconds = 5;
let elapsedSinceLastSavedFrame = 0;
await getVideoFrames({
videoUrl,
onFrame(frame) { // `frame` is a VideoFrame object:
elapsedSinceLastSavedFrame += frame.duration / 1e6; // frame.duration is in microseconds, so we convert to seconds
if(elapsedSinceLastSavedFrame > saveFrameEverySeconds) {
ctx.drawImage(frame, 0, 0, canvasEl.width, canvasEl.height);
elapsedSinceLastSavedFrame = 0;
}
frame.close();
},
onConfig(config) {
canvasEl.width = config.codedWidth;
canvasEl.height = config.codedHeight;
},
});
URL.revokeObjectURL(fileOrBlob); // revoke URL to prevent memory leak
</script>
Demo: https://jsbin.com/qovapeziqi/edit?html,output
Github: https://github.com/josephrocca/getVideoFrames.js
How can I change a number in a time interval with Cocos2d Javascript engine?
With pure js I could use setInterval, but does any function in the cocos2d library do it?
You didn't write any code so I'll be using the one posted here.
To display a number in the center of the screen, that increases every second, I'd add this to MainLayer.js:
var MainLayer = cc.LayerColor.extend({
_labelNumber:null,
_number:0,
_updateRate:1.0,
onEnter:function () {
_number = 0;
var labelName = ""+_number;
_labelNumber = cc.LabelTTF.create(labelName, "Arial", 32);
_labelNumber.setColor(cc.c3(64, 64, 64));
_labelNumber.setPosition(winSize.width/2, winSize.height/2);
_updateRate = 1.0;
this.addChild(_labelNumber);
this.schedule(this.updateNumber, _updateRate);
},
updateNumber:function() {
_number++;
if(_labelNumber == null) return;
_labelNumber.setString(""+_number);
}
});
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.