I am trying to make a simple game in HTML5 Canvas. I want, at most, two keyboard input per second.
This is my code so far:
function move( x, y, r ) {
var canid=document.getElementById("draw");
canid.addEventListener('keydown',readkey,false);
function readkey(e) {
if(e.keyCode == 37) {
clearField();
x = x-draw.width / 10;
redrawpic(x,y,r);
}
else if(e.keyCode == 38){
clearField();
y = y-draw.height / 10;
redrawpic( x, y, r );
}
//..........etc
}
}
The function move is used to move picture from one cell to another. How to set delay a between two moves?
You can use a timestamp to check when the last event has occured:
function move(x,y,r){
/* your variable declarations*/
var d1 = Date.now();
var d2;
function readkey(e){
d2 = Date.now();
// difference between timestamps needs to be 500ms
if(d2-d1 > 500){
// set old Timestamp to new one
d1 = d2;
/*
Rest of your code
*/
}
This allows one key event every 500ms. Not exactly the same like 2 events in 1 second (which could occure in 50ms and then pause 950ms), but maybe close enough?
Timeout/Interval is possible too, but I personally dislike the overhead of consecutive (possibly unnecessary) timeout calls.
var throttle = false;
function readkey(e) {
if (throttle)
return;
throttle = true;
setTimeout(function () { throttle = false; }, 500);
/* the rest of your code */
500ms is two inputs per second, but they are throttled individually. You could also keep a count of how many inputs there have been in a full second. Something like
if (throttle > 1)
return;
throttle++;
setTimeout(function () { throttle = 0; }, 1000);
Related
Im trying to finetune the controls of a javascript game (p5 library).
keyIsDown() is the type of control/feel im looking for but it's timing is too fast.
By timing is too fast, I mean when I hold down the key, the key repeats too fast. Im trying to control the timing speed of the key repeat when holding down the key.
I tried to make my own vertion with keytyped() and setInterval to time my move function. Then stopping it with keyReleased(). But it jams up.
I have also tried setTimeout but could not get it to work on keyboard input like this.
var controlInterval;
function keyReleased() {
if (key === 'a') {
clearInterval(controlInterval);
} else if (key === 'd') {
clearInterval(controlInterval);
}
//return false; // prevent any default behavior
}
function keyTyped() {
if (key === 'a') {
controlInterval = setInterval(left, 50);
} else if (key === 'd') {
controlInterval = setInterval(right, 50);
}
}
function left(){
var x = -1;
move(x);
}
function right(){
var x = 1;
move(x);
}
code I prefer to use:
if (keyIsDown(LEFT_ARROW)){
var west = -1;
move(west);
}
if (keyIsDown(RIGHT_ARROW)){
var east = 1;
move(east);
Take a look at debouncing and throttling principles which I think is what you are looking for here - limit the execution count of event.
The best explanation IMHO related to this subject is this article.
There are already libraries to help you like lodash. Go to their documentation page and search the functions - debounce or throttle and it there will be examples how to use them.
someDiv.addEventListener('keyup', _.debounce(handleKeyUp, 300));
function handleKeyUp(event) {
if (event.keyCode === 65 /* A */) left();
...
}
Arrow keys can be used to signal a direction, frame rate can be used to control speed and a delta variable used to control the amount of movement.
var xPos = 0;
var xDelta = 1;
function setup(){
createCanvas(200,200);
frameRate(10); // 10 frames per second, increase to move faster
}
function draw(){
background(100);
if (keyIsDown(LEFT_ARROW)){
var west = -1;
move(west);
}
if (keyIsDown(RIGHT_ARROW)){
var east = 1;
move(east);
}
rect(xPos, 100, 10,10);
}
function move(dir){
if (dir == -1){
xPos = xPos - xDelta;
}
else {
xPos = xPos + xDelta;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/p5.min.js"></script>
I have a class that takes some coordinate and duration data. I want to use it to animate an svg. In more explicit terms, I want to use that data to change svg attributes over a time frame.
I'm using a step function and requestAnimationFrame outside the class:
function step(timestamp) {
if (!start) start = timestamp
var progress = timestamp - start;
var currentX = parseInt(document.querySelector('#start').getAttribute('cx'));
var moveX = distancePerFrame(circleMove.totalFrames(), circleMove.xLine);
document.querySelector('#start').setAttribute('cx', currentX + moveX);
if (progress < circleMove.duration) {
window.requestAnimationFrame(step);
}
}
var circleMove = new SingleLineAnimation(3000, startXY, endXY)
var start = null
function runProgram() {
window.requestAnimationFrame(step);
}
I can make it a method, replacing the circleLine with this. That works fine for the first run through, but when it calls the this.step callback a second time, well, we're in a callback black hole and the reference to this is broken. Doing the old self = this won't work either, once we jump into the callback this is undefined(I'm not sure why). Here it is as a method:
step(timestamp) {
var self = this;
if (!start) start = timestamp
var progress = timestamp - start;
var currentX = parseInt(document.querySelector('#start').getAttribute('cx'));
var moveX = distancePerFrame(self.totalFrames(), self.xLine);
document.querySelector('#start').setAttribute('cx', currentX + moveX);
if (progress < self.duration) {
window.requestAnimationFrame(self.step);
}
}
Any ideas on how to keep the "wiring" inside the Object?
Here's the code that more or less works with the step function defined outside the class.
class SingleLineAnimation {
constructor(duration, startXY, endXY) {
this.duration = duration;
this.xLine = [ startXY[0], endXY[0] ];
this.yLine = [ startXY[1], endXY[1] ];
}
totalFrames(framerate = 60) { // Default to 60htz ie, 60 frames per second
return Math.floor(this.duration * framerate / 1000);
}
frame(progress) {
return this.totalFrames() - Math.floor((this.duration - progress) / 17 );
}
}
This will also be inserted into the Class, for now it's just a helper function:
function distancePerFrame(totalFrames, startEndPoints) {
return totalFrames > 0 ? Math.floor(Math.abs(startEndPoints[0] - startEndPoints[1]) / totalFrames) : 0;
}
And click a button to...
function runProgram() {
window.requestAnimationFrame(step);
}
You need to bind the requestAnimationFrame callback function to a context. The canonical way of doing this is like this:
window.requestAnimationFrame(this.step.bind(this))
but it's not ideal because you're repeatedly calling .bind and creating a new function reference over and over, once per frame.
If you had a locally scoped variable set to this.step.bind(this) you could pass that and avoid that continual rebinding.
An alternative is this:
function animate() {
var start = performance.now();
el = document.querySelector('#start');
// use var self = this if you need to refer to `this` inside `frame()`
function frame(timestamp) {
var progress = timestamp - start;
var currentX = parseInt(el.getAttribute('cx'));
var moveX = distancePerFrame(circleMove.totalFrames(), circleMove.xLine);
el.setAttribute('cx', currentX + moveX);
if (progress < circleMove.duration) {
window.requestAnimationFrame(frame);
}
}
window.requestAnimationFrame(frame);
}
i.e. you're setting up the initial state, and then doing the animation within a purely locally scoped function that's called pseudo-recursively by requestAnimationFrame.
NB: either version of the code will interact badly if you inadvertently call another function that initiates an animation at the same time.
I have a script for a gambling site.
What I need is that after 2 calls to the function multiply, it bets the maximum stake possible and after that calls the function reset, I mean in each two sequential loss it bets the full balance in my account reset for the minimum stake and continue playing, Because I realized that in odds of 1.1 on 'manual bet' in each 2 loss the next will be a won.
It is like: after 2 multiplyCalls bet the full balance (it is the "MAX" button in the image below) and reset the game to continue playing. Am I being clear enough?
I tried to create a function for this but did not work
The "Maximum stake" button element code is:
MAX
this is the printscreen
The part of the script I want to modify is this, the multiplyCalls function is already created. I changed the var multiply = (current * 2).toFixed(8); to var multiply = (current * 1).toFixed(8); because my strategy does not have martingale.
function multiply(){
if(multiplyCalls < 2){ // test multiply
var current = $('#double_your_btc_stake').val();
var multiply = (current * 2).toFixed(8);
$('#double_your_btc_stake').val(multiply);
multiplyCalls++; // increment
}else{
reset();
console.log('=== RESETING ===');
}
}
This is the full script:
var startValue = '0.00000001', // Don't lower the decimal point more than 4x of current balance
stopPercentage = 0.001, // In %. I wouldn't recommend going past 0.08
maxWait = 500, // In milliseconds
stopped = false,
stopBefore = 3; // In minutes
multiplyCalls = 0; // <--- Added this global
var $loButton = $('#double_your_btc_bet_lo_button'),
$hiButton = $('#double_your_btc_bet_hi_button');
function multiply(){
if(multiplyCalls < 2){ // test multiply
var current = $('#double_your_btc_stake').val();
var multiply = (current * 1).toFixed(8);
$('#double_your_btc_stake').val(multiply);
multiplyCalls++; // increment
}else{
reset();
console.log('=== RESETING ===');
}
}
function getRandomWait(){
var wait = Math.floor(Math.random() * maxWait ) + 100;
console.log('Waiting for ' + wait + 'ms before next bet.');
return wait ;
}
function startGame(){
console.log('Game started!');
reset();
$loButton.trigger('click');
}
function stopGame(){
console.log('Game will stop soon! Let me finish.');
stopped = true;
}
function reset(){
$('#double_your_btc_stake').val(startValue);
}
// quick and dirty hack if you have very little bitcoins like 0.0000001
function deexponentize(number){
return number * 1000000;
}
function iHaveEnoughMoni(){
var balance = deexponentize(parseFloat($('#balance').text()));
var current = deexponentize($('#double_your_btc_stake').val());
return ((balance*2)/100) * (current*2) > stopPercentage/100;
}
function stopBeforeRedirect(){
var minutes = parseInt($('title').text());
if( minutes < stopBefore )
{
console.log('Approaching redirect! Stop the game so we don\'t get redirected while loosing.');
stopGame();
return true;
}
return false;
}
// Unbind old shit
$('#double_your_btc_bet_lose').unbind();
$('#double_your_btc_bet_win').unbind();
// Loser
$('#double_your_btc_bet_lose').bind("DOMSubtreeModified",function(event){
if( $(event.currentTarget).is(':contains("lose")') )
{
console.log('You LOST! Multiplying your bet and betting again.');
multiply();
setTimeout(function(){
$loButton.trigger('click');
}, getRandomWait());
//$loButton.trigger('click');
}
});
// Winner
$('#double_your_btc_bet_win').bind("DOMSubtreeModified",function(event){
if( $(event.currentTarget).is(':contains("win")') )
{
if( stopBeforeRedirect() )
{
return;
}
if( iHaveEnoughMoni() )
{
console.log('You WON! But don\'t be greedy. Restarting!');
reset();
if( stopped )
{
stopped = false;
return false;
}
}
else
{
console.log('You WON! Betting again');
}
setTimeout(function(){
$loButton.trigger('click');
}, getRandomWait());
multiplyCalls = 0; // reset value
}
});startGame
So basically, you want to max the bet after two losses. Because multiply calls only occur after a loss, we can assume that the if(multiplyCalls < 2) bit takes care of that. So in the following else, all you really need to do is hit max bet instead of call reset(). Based on what I understand the code to be doing, this should be sufficient, correct?
function multiply(){
if(multiplyCalls < 2){ // test multiply
var current = $('#double_your_btc_stake').val();
var multiply = (current * 1).toFixed(8);
$('#double_your_btc_stake').val(multiply);
multiplyCalls++; // increment
}else{
//reset(); /* instead of resetting here, let's max the bet. */
$('#double_your_btc_max').trigger('click');
console.log('=== RESETING ===');
}
}
For the sake of learning I am prototyping an animate function for all HTMLElements, inspired by jQuery. The animation starts up just fine, but I want it to stop after the requestAnimationFrame's time = the duration given in the function. I am using cancelAnimationFrame inside the animation loop, but it doesn't stop the loop.
HTMLElement.prototype.animate = function(properties,duration){
for(prop in properties){
var last = 0,
fps = 60;
function ani(time){
requestAnimationFrame(ani);
if ((time - last) > fps ){
last = time
console.log(time)
if(time >= (duration*1000)){
window.cancelAnimationFrame(aniLoop)
}
}
}
var aniLoop = requestAnimationFrame(ani)
}
}
The function is called like this
c.animate({"property":"value"},1)
At the core of the problem lies that fact that you're only getting the ID of the first animation frame (the var aniLoop = (...) line) and that's what you're trying to cancel - except that each call to requestAnimationFrame has a different ID, thus you'd need to store the return value of the last call and cancel that instead:
HTMLElement.prototype.animate = function(properties,duration) {
"use strict";
var aniLoop,
prop,
last = 0,
fps = 60;
for (prop in properties) {
function ani(time) {
aniLoop = requestAnimationFrame(ani);
if ((time - last) > fps) {
last = time;
console.log(time);
if (time >= (duration * 1000)) {
cancelAnimationFrame(aniLoop);
}
}
}
aniLoop = requestAnimationFrame(ani);
}
};
There are, however, several other problems with your code that need to be tackled as well, otherwise your function will blow up rather thoroughly:
:1 Function declaration in a loop
I would recommend reading about differences between function declaration and expression to get a better picture, but the problem here is that you're doing function declaration in a loop, which is considered undefined behaviour (some engines will replace the functions, some will not, some will blow up). Given that the animations have only single duration given and thus, are probably synchronised, it'd be a better option to iterate over properties to animate inside of a single animation function, like so:
HTMLElement.prototype.animate = function(properties,duration) {
"use strict";
var aniLoop,
last = 0,
fps = 60;
function ani(time) {
var prop;
aniLoop = requestAnimationFrame(ani);
if ((time - last) > fps) {
last = time;
for (prop in properties) {
console.log(prop + ': ' + time);
}
if (time >= (duration * 1000)) {
cancelAnimationFrame(aniLoop);
}
}
}
aniLoop = requestAnimationFrame(ani);
}
:2 Animation timestamping
As it looks currently, your animation function will probably not run more than one frame anyway - if you look at requestAnimationFrame documentation on MDN, you'll notice that the callback given to requestAnimationFrame is given a timestamp, i.e. value in milliseconds from the beginning of UNIX epoch (1st of January 1970) - therefore the condition of time >= (duration * 1000) will always be true. Instead of that, register the starting time when you kick the animation off and compare the timestamp within the callback to it, like so:
HTMLElement.prototype.animate = function(properties,duration) {
"use strict";
var aniLoop,
start,
last = 0,
fps = 60;
function ani(time) {
var prop,
progress;
aniLoop = requestAnimationFrame(ani);
if ((time - last) > fps) {
last = time;
progress = time - start;
for (prop in properties) {
console.log(prop + ': ' + progress + ' out of ' + (duration * 1000));
}
// This is where we get a difference between current and starting time
if (progress >= (duration * 1000)) {
cancelAnimationFrame(aniLoop);
}
}
}
start = Date.now();
aniLoop = requestAnimationFrame(ani);
}
:3 Animation throttling
This one is not as crucial, but still worth a consideration - requestAnimationFrame is intended to be automatically throttled and regulated by the browser, thus you don't need to apply your own conditions on whether animation should run (it won't go over 60FPS anyway, as that's the specification's ceiling). Instead, it should simply work on difference of current time from starting time, to make sure your animation still ends up in correct place even if for some reason, there is a lag in animation:
HTMLElement.prototype.animate = function(properties,duration) {
"use strict";
var aniLoop,
start;
function ani(time) {
var prop,
progress;
aniLoop = requestAnimationFrame(ani);
progress = time - start;
for (prop in properties) {
console.log(prop + ': ' + progress + ' out of ' + (duration * 1000));
}
// This is where we get a difference between current and starting time
if (progress >= (duration * 1000)) {
cancelAnimationFrame(aniLoop);
}
}
start = Date.now();
aniLoop = requestAnimationFrame(ani);
}
I have the current JavaScript problem. I have four divisions next to each other on my website that constantly rotate images on a 10 seconds interval. I need these intervals to keep rotating images at the current interval but start 5 seconds apart from each other in order to obtain a nice waterfall effect. How can I accomplish this using JavaScript?
image of how it looks on my websites' header
This is an example of the code I am currently using to display a single division and handle the rotation of the images.
<div class = "TestRotator">
<img src="http://bushveld.info/wp-content/uploads/2013/07/image1.png" alt="rotating" width="100" height="232" id="rotator">
<script type="text/javascript">
(function () {
var rotator = document.getElementById('rotator'); // change to match image ID
var imageDir = 'http://bushveld.info/wp-content/uploads/2013/07/';
var delayInSeconds = 5;
// set number of seconds delay
// list image names
var images = ['image2.png', 'image3.png', 'image4.png'];
var num = 0;
var changeImage = function () {
var len = images.length;
rotator.src = imageDir + images[num++];
if (num == len) {
num = 0;
}
};
setInterval(changeImage, delayInSeconds * 1000);
})();
</script>
</div>
I've fiddled it a lot! (I changed it big time.)
chenged setInterval() with setTimeout() and many others.
Is this what you wanted?
PS: state holds the 1st image to which the imgs change. and the difference in the timeout (200 milliseconds is in order to just to make some difference in between them, yuo can change it to a round number if you want to).
If I've understood your question correctly, you need something like this:
window.onload = function () {
var // Customable parameters
imageDir = 'http://bushveld.info/wp-content/uploads/2013/07/',
interval = 2, // Interval between "flushes" in seconds. Must be > speed * maxScreens
speed = 0.1, // "Flush" speed in seconds
maxScreens = 4, // amount of used image tags
images = 4, // amount of image sources, can be > maxScreens
dir = 1, // 1 = from left to right, -1 = from right to left
// Program
flush,
target = (dir > 0) ? 1 : maxScreens,
targetOrigo = target,
changeImage = function() {
var img = document.getElementById('rotator' + target),
id = parseInt(img.src.substr(img.src.length - 5, 1), 10) - dir;
if (id < 1) {
id = images;
}
if (id > images) {
id = 1;
}
img.src = imageDir + 'image' + id + '.png';
if (target !== maxScreens - targetOrigo + 1) {
target += dir;
setTimeout(changeImage, speed * 1000);
} else {
target = targetOrigo;
setTimeout(runRotation, interval * 1000);
}
return;
},
runRotation = function () {
setTimeout(changeImage, speed * 1000);
};
setTimeout(runRotation, 1000);
}
A live demo at jsFiddle
Notice, that I've put the function at window.onload, looks better when all the images are already loaded, before the rotation starts.
The snippet doesn't use setInterval() at all, instead it's based on nested setTimeout()s. This way you can avoid a mess, which you might get (depends on used browser), if user visits at other tab and then comes back to your site.
You can play with interval, "flush" speed, number of images you have on the rotation and even how many different images you like to use (max. = 9). You can also switch the direction of the rotation.
If you want to avoid two similar images to be shown at the same time, you can add image5.png to your image folder, and set images = 5.
Also version using an image source array available.
Thanx alot for the input. I solved this issue by adapting the code in this manner...
(function() {
var rotator3 = document.getElementById('rotator3'); // change to match image ID
var imageDir = 'http://bushveld.info/wp-content/uploads/2013/07/';
// set number of seconds delay
// list image names
var images = ['image2.png', 'image3.png', 'image4.png', 'image1.png'];
// don't change below this line
var num = 0;
var changeImage = function()
{
var len = images.length;
rotator3.src = imageDir + images[num++];
if (num == len)
{
num = 0;
}
};
function SwImg() {
var rotate = setInterval(changeImage, 20000);
}
setTimeout(SwImg,15000);
})();
This tweak basically creates an initial delay of 5++ seconds at each division with the standard 20 seconds delay interval between switches, rotating each image in each division 5 seconds after the other. Here is a link to the website , will be done end of this week. Thanks again for the input, really awesome and creative ways of solving this issue!
Cheers