Custom cursor effect on Wordpress Theme - javascript

I am trying to add a custom cursor effect similar to below that is in Javascript to a Wordpress theme I am working on. I am new to creating my own theme and I am not sure where to apply the code. Should it go in functions.php or should I first create a sort of custom_script.js file and add the code in there?
Not sure which direction I should go into so any help would be appreciated.
(function fairyDustCursor() {
var possibleColors = ["#D61C59", "#E7D84B", "#1B8798"]
var width = window.innerWidth;
var height = window.innerHeight;
var cursor = {
x: width / 2,
y: width / 2
};
var particles = [];
function init() {
bindEvents();
loop();
}
// Bind events that are needed
function bindEvents() {
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('touchmove', onTouchMove);
document.addEventListener('touchstart', onTouchMove);
window.addEventListener('resize', onWindowResize);
}
function onWindowResize(e) {
width = window.innerWidth;
height = window.innerHeight;
}
function onTouchMove(e) {
if (e.touches.length > 0) {
for (var i = 0; i < e.touches.length; i++) {
addParticle(e.touches[i].clientX, e.touches[i].clientY, possibleColors[Math.floor(Math.random() * possibleColors.length)]);
}
}
}
function onMouseMove(e) {
cursor.x = e.clientX;
cursor.y = e.clientY;
addParticle(cursor.x, cursor.y, possibleColors[Math.floor(Math.random() * possibleColors.length)]);
}
function addParticle(x, y, color) {
var particle = new Particle();
particle.init(x, y, color);
particles.push(particle);
}
function updateParticles() {
// Updated
for (var i = 0; i < particles.length; i++) {
particles[i].update();
}
// Remove dead particles
for (var i = particles.length - 1; i >= 0; i--) {
if (particles[i].lifeSpan < 0) {
particles[i].die();
particles.splice(i, 1);
}
}
}
function loop() {
requestAnimationFrame(loop);
updateParticles();
}
/**
* Particles
*/
function Particle() {
this.character = "*";
this.lifeSpan = 120; //ms
this.initialStyles = {
"position": "absolute",
"display": "block",
"pointerEvents": "none",
"z-index": "10000000",
"fontSize": "16px",
"will-change": "transform"
};
// Init, and set properties
this.init = function(x, y, color) {
this.velocity = {
x: (Math.random() < 0.5 ? -1 : 1) * (Math.random() / 2),
y: 1
};
this.position = {
x: x - 10,
y: y - 20
};
this.initialStyles.color = color;
console.log(color);
this.element = document.createElement('span');
this.element.innerHTML = this.character;
applyProperties(this.element, this.initialStyles);
this.update();
document.body.appendChild(this.element);
};
this.update = function() {
this.position.x += this.velocity.x;
this.position.y += this.velocity.y;
this.lifeSpan--;
this.element.style.transform = "translate3d(" + this.position.x + "px," + this.position.y + "px,0) scale(" + (this.lifeSpan / 120) + ")";
}
this.die = function() {
this.element.parentNode.removeChild(this.element);
}
}
/**
* Utils
*/
// Applies css `properties` to an element.
function applyProperties(target, properties) {
for (var key in properties) {
target.style[key] = properties[key];
}
}
init();
})();

The most typical and correct way to execute JavaScript code is to put it into *.js file, then register this file as a part of your theme and then call it in any template you need. Technically:
Create a custom-cursor.js file in your theme and put your code there. (The js folder is usually a nice place to keep javascript files, so we assume your realtive path is js/custom-cursor.js).
Open your functions.php and register your script:
// This function will register your frontend scripts
// to let WordPress handle their loading
function rhonda_frontend_scripts() {
// We shouldn't call frontend scripts on admin side, so...
if ( ! is_admin() ) {
// Ok, let's register a script
wp_register_script(
// Set a script name (unique handler) to call it later
'custom-cursor',
// Specify a path to your javascipt file
get_template_directory_uri() . '/js/theme.min.js',
// Leave dependencies array blank (your code doesn't require any other scripts)
array(),
// Don't specify a version (you can read about this later)
false,
// Yes, call this script in the footer
// after the main content (for performance reasons)
true
);
// After the registration, you are able to load the script
// from any template using it's handler
// And since your cursor should be probably loaded on every page,
// you can load it just once right here in functions.php instead.
wp_enqueue_style( 'custom-cursor' );
// Ok, now you can add one more script if you wish.
}
}
// But don't forget that we need to call our registration function right in time,
// before the template is displayed, so we should use a hook.
// There is a special hook for this purpose: 'wp_enqueue_scripts'
add_action( 'wp_enqueue_scripts', 'rhonda_frontend_scripts' );
// Done :)
(3. Optional) If you need to load the script only in a special template, you neead to call wp_enqueue_style( 'custom-cursor' ); right from its body. For example, you can open single.php and put this code there (before get_header(), this is important):
// This function will inject your scripts in the header/footer
// of any page, which uses this .php template file
function rhonda_single_template_scripts() {
wp_enqueue_script( 'custom-cursor' );
}
// Don't forget to call it via special hook 'wp_enqueue_scripts'.
add_action( 'wp_enqueue_scripts', 'rhonda_single_template_scripts' );
// ...
// get_header();
Summary:
The basics: "Using_Javascript" (Codex)
Reference: wp_register_script()
Reference: wp_enqueue_script()
Hope this helps.

Related

P5js: why is my bg framerate not refreshing properly?

I have 2 sets of code in here. One is the simple bounce off code. The other is a function. I've tried to make it a function but it doesn't seem to be working properly.
The bg framerate doesn't clear in the sense that a string of balls show rather than a ball bouncing and animating.
if(this.y_pos > 400) this condition doesn't seem to be working even tho it works when it is drawn in the draw function.
var sketch = function (p) {
with(p) {
p.setup = function() {
createCanvas(800, 600);
// x_pos = 799;
// y_pos = 100;
// spdx = -random(5,10);
// spdy = random(12,17);
};
p.draw = function() {
background(0);
// fill(255);
// ellipse(x_pos,y_pos,50);
// x_pos += spdx;
// y_pos += spdy;
// if(y_pos > 400)
// {
// spdy *= -1;
// }
// for( var i = 0; i < 10; i++)
avalance(799, 100, random(5,10), random(12,17));
};
function avalance(x, y, spdx, spdy)
{
this.x_pos = x;
this.y_pos = y;
this.spdx = spdx;
this.spdy = spdy;
this.x_pos = 799;
this.y_pos = 100;
this.spdx = 1;
this.spdy = 1;
this.movement = function()
{
this.x_pos += -spdx;
this.y_pos += spdy;
if(this.y_pos > 400)
{
this.spdy *= -1;
}
}
this.draw = function()
{
this.movement();
this.drawnRox();
}
this.drawnRox = function()
{
fill(255);
ellipse(this.x_pos,this.y_pos,50);
}
}
}
};
let node = document.createElement('div');
window.document.getElementById('p5-container').appendChild(node);
new p5(sketch, node);
body {
background-color:#efefef;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script>
<div id="p5-container"></div>
Let's address both issues:
The draw() function is called for each new frame and in it you call avalance which creates a new ball. To fix that you need to
Create a global variable let ball; out of setup() and draw() so that you can reuse that later;
In setup create a new ball and assign it to your ball variable: ball = new avalance(799, 100, random(5,10), random(12,17));
In draw() you want to update the ball and that's what its own draw() function does (I would advise renaming it update() for example, to avoid confusion with the p5 specific draw() function). So you just need to call ball.draw() in draw().
This creates a ball which moves but still don't respect your 400px limit.
The issue is that in movement() you add spdx and spdy to the position but when the ball crosses the limit you update this.spdy, so you need to update the function with this code:
this.x_pos += -this.spdx;
this.y_pos += this.spdy;
And you should be good! Here is a code pend with your code working as you intend.
Also as a bonus advise: You probably want to use some p5.Vector objects to store positions, speeds and accelerations it really makes your code easier to read and to use. You could also rename your function Avalance (capital A) to show that you actually use a class and that this function shouldn't be called without new.
As #statox suggested, do new avalance(799, 100, random(5,10), random(12,17)) in the setup() section and call draw of the ball in draw() section.
You can test the code below by clicking "Run code snippet".
var sketch = function (p) {
with(p) {
var ball;
p.setup = function() {
createCanvas(800, 600);
ball = new avalance(799, 100, random(5,10), random(12,17));
};
p.draw = function() {
background(0);
ball.draw();
};
function avalance(x, y, spdx, spdy)
{
function movement ()
{
x += -spdx;
y += spdy;
if (y > height || y < 0)
{
spdy *= -1;
}
if (x > width || x < 0)
{
spdx *= -1;
}
}
this.draw = function()
{
movement();
drawnRox();
}
function drawnRox ()
{
fill(255);
ellipse(x,y,50);
}
}
}
};
let node = document.createElement('div');
window.document.getElementById('p5-container').appendChild(node);
new p5(sketch, node);
body {
background-color:#efefef;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script>
<div id="p5-container"></div>

I want to be able to pause a block in place when I click on it

Here is the website with the game/source code and want to try and see if i can pause a block as it falls when i left click it with my mouse but not sure the proper function for it. ( https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/By_example/Raining_rectangles )
You must clearTimeout() to make it paused, I have implemented a toggle on click of box i.e play/pause.
(function() {
"use strict"
window.addEventListener("load", setupAnimation, false);
var gl,
timer,
rainingRect,
scoreDisplay,
missesDisplay,
status,
paused = false;
function setupAnimation(evt) {
window.removeEventListener(evt.type, setupAnimation, false);
if (!(gl = getRenderingContext()))
return;
gl.enable(gl.SCISSOR_TEST);
rainingRect = new Rectangle();
timer = setTimeout(drawAnimation, 17);
document.querySelector("canvas")
.addEventListener("click", playerClick, false);
var displays = document.querySelectorAll("strong");
scoreDisplay = displays[0];
missesDisplay = displays[1];
status = displays[2];
}
var score = 0,
misses = 0;
function drawAnimation() {
gl.scissor(rainingRect.position[0], rainingRect.position[1],
rainingRect.size[0], rainingRect.size[1]);
gl.clear(gl.COLOR_BUFFER_BIT);
rainingRect.position[1] -= rainingRect.velocity;
if (rainingRect.position[1] < 0) {
misses += 1;
missesDisplay.innerHTML = misses;
rainingRect = new Rectangle();
}
// We are using setTimeout for animation. So we reschedule
// the timeout to call drawAnimation again in 17ms.
// Otherwise we won't get any animation.
timer = setTimeout(drawAnimation, 17);
}
function playerClick(evt) {
// We need to transform the position of the click event from
// window coordinates to relative position inside the canvas.
// In addition we need to remember that vertical position in
// WebGL increases from bottom to top, unlike in the browser
// window.
var position = [
evt.pageX - evt.target.offsetLeft,
gl.drawingBufferHeight - (evt.pageY - evt.target.offsetTop),
];
// if the click falls inside the rectangle, we caught it.
// Increment score and create a new rectangle.
var diffPos = [position[0] - rainingRect.position[0],
position[1] - rainingRect.position[1]
];
if (diffPos[0] >= 0 && diffPos[0] < rainingRect.size[0] &&
diffPos[1] >= 0 && diffPos[1] < rainingRect.size[1]) {
score += 1;
scoreDisplay.innerHTML = score;
// rainingRect = new Rectangle();
if (!paused) {
clearTimeout(timer)
paused = true;
status.innerHTML = 'Paused';
} else {
timer = setTimeout(drawAnimation, 17);
paused = false;
status.innerHTML = 'Playing';
}
}
}
function Rectangle() {
// Keeping a reference to the new Rectangle object, rather
// than using the confusing this keyword.
var rect = this;
// We get three random numbers and use them for new rectangle
// size and position. For each we use a different number,
// because we want horizontal size, vertical size and
// position to be determined independently.
var randNums = getRandomVector();
rect.size = [
5 + 120 * randNums[0],
5 + 120 * randNums[1]
];
rect.position = [
randNums[2] * (gl.drawingBufferWidth - rect.size[0]),
gl.drawingBufferHeight
];
rect.velocity = 1.0 + 6.0 * Math.random();
rect.color = getRandomVector();
gl.clearColor(rect.color[0], rect.color[1], rect.color[2], 1.0);
function getRandomVector() {
return [Math.random(), Math.random(), Math.random()];
}
}
function getRenderingContext() {
var canvas = document.querySelector("canvas");
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
var gl = canvas.getContext("webgl") ||
canvas.getContext("experimental-webgl");
if (!gl) {
var paragraph = document.querySelector("p");
paragraph.innerHTML = "Failed to get WebGL context." +
"Your browser or device may not support WebGL.";
return null;
}
gl.viewport(0, 0,
gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
return gl;
}
})();
<style>
body {
text-align: center;
}
canvas {
display: block;
width: 280px;
height: 210px;
margin: auto;
padding: 0;
border: none;
background-color: black;
}
button {
display: block;
font-size: inherit;
margin: auto;
padding: 0.6em;
}
</style>
<p>You caught
<strong>0</strong>.
You missed
<strong>0</strong>
Status
<strong>Playing</strong>.</p>
<canvas>Your browser does not seem to support
HTML5 canvas.</canvas>
As you can see in the code the function drawAnimation() is calling itself every 17ms using setTimeout() JavaScript function (and this is what creates steady animation).
function drawAnimation() {
.
.
.
timer = setTimeout(drawAnimation, 17);
}
In order to pause/stop the animation you would need to use JavaScript function clearTimeout(timer). Since you want to stop/pause the animation on click event you could just reuse the function playerClick (evt) { ... } from the code you already have and put the function clearTimeout(timer) there.
function playerClick (evt) {
.
.
.
clearTimeout(timer);
}
If you want to be able to continue with animation after you have paused it you'll need to implement some switch-logic (pause if it is already playing, play if it is already paused) inside your function playerClick (evt) or to use timers to continue the animation after some time, for example.

2 images animated using Javascript

From doing research and asking for help I have so far built an animation moving from left to right using the code in the JSFiddle below. This loops every 20 seconds.
http://jsfiddle.net/a9HdW/3/
However now I need a second image that moves from right to left and for this to automatically trigger straight after the first image has completed its animation.
If you could do this that would be amazing.
Thanks In Advance
<canvas id="canvas" width="1600" height="300"></canvas>
<script>
window.requestAnimFrame = (function(){
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
var canvas = document.getElementById("canvas"),
cx = canvas.getContext("2d");
function Card(x,y){
this.x = x || -300;
this.y = y || 0;
this.width = 0;
this.height = 0;
this.img=new Image();
this.init=function(){
// this makes myCard available in the img.onload function
// otherwise "this" inside img.onload refers to the img
var self=this;
this.img.onload = function()
{
// getting width and height of the image
self.width = this.width;
self.height = this.height;
self.draw();
loop();
}
this.img.src = "f15ourbase.png";
}
this.draw = function(){
cx.drawImage(this.img, this.x, this.y);
}
}
var myCard = new Card(50,50);
myCard.init();
function loop(){
if((myCard.x + myCard.width) < canvas.width){
requestAnimFrame(loop);
} else {
setTimeout(function() {
// resetting card back to old state
myCard.x = 50;
myCard.y = 50;
// call the loop again
loop();
}, 20000);
}
cx.clearRect(0, 0, canvas.width, canvas.height);
myCard.x = myCard.x + 15;
myCard.draw();
}
This is little bit complicated, however, it boils down to formatting your Card function to understand which direction it is going to. Therefore you will need direction parameter and more complicated draw function which updates your card movement to direction it is supposed to go, like this:
// Updating own position, based on direction
if(this.direction === 1) {
this.x = this.x + this.stepSize; // we move to right
// if I am over canvas at right, I am done animating
if(this.x >= canvas.width) {
this.finishedAnimation = true;
}
} else {
this.x = this.x - this.stepSize; // we move to left
// if I am over canvas at left, I am done animating
if(this.x <= -this.width) {
this.finishedAnimation = true;
}
}
Also, because timing is important - you will need to trigger other card movement only after the first one is completed. Like this:
// We won't draw next card before previous is finished
if(!cardToRight.finishedAnimation) {
cardToRight.draw();
} else {
// Card at right is finished, we move the left card now
cardToLeft.draw();
}
To summarize my points, I made a code which has code comments to explain it's contents, go it through carefully:
// Animation frame gist
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
// Variable declarations
var canvas = document.getElementById("canvas"),
cx = canvas.getContext("2d"),
// Handlers for both of the cards
cardToRight,
cardToLeft;
/**
* Card declaration
*
*/
var Card = function(x, y, direction) {
// how many pixels we move
this.stepSize = 5;
// x and y position on canvas
this.x = x;
this.y = y;
// Copy values are used when reset is done
this.xCopy = this.x;
this.yCopy = this.y;
// handle to specify if animation finished
this.finishedAnimation = false;
// 1 means move to right, 0 means move to left
this.direction = direction;
// Image of the card
this.img = undefined;
// Image url for this card
this.imgSrc = "http://placehold.it/350x150"; // for your image: "f15ourbase.png"
// Image width
this.width = 0;
};
/**
* Initialize
*
*/
Card.prototype.init = function(callback) {
// self refers to this card
var self = this;
this.img = new Image();
// onload success callback
this.img.onload = function() {
// Setting own width
self.width = this.width;
// triggering callback on successfull load
return callback();
};
// onload failure callback
this.img.onerror = function() {
// Returning error message for the caller of this method
return callback("Loading image " + this.imgSrc + " failed!");
};
// Triggering image loading
this.img.src = this.imgSrc;
};
/**
* Draw self
*
*/
Card.prototype.draw = function() {
// Updating own position, based on direction
if(this.direction === 1) {
this.x = this.x + this.stepSize; // we move to right
// if I am over canvas at right, I am done animating
if(this.x >= canvas.width) {
this.finishedAnimation = true;
}
} else {
this.x = this.x - this.stepSize; // we move to left
// if I am over canvas at left, I am done animating
if(this.x <= -this.width) {
this.finishedAnimation = true;
}
}
// cx (context) could be also passed, now it is global
cx.drawImage(this.img, this.x, this.y);
};
/**
* Reset self
*
*/
Card.prototype.reset = function() {
// Using our copy values
this.x = this.xCopy;
this.y = this.yCopy;
// handle to specify if animation finished
this.finishedAnimation = false;
};
/**
* Main loop
*
*/
function loop() {
// Clear canvas
cx.clearRect(0, 0, canvas.width, canvas.height);
// We won't draw next card before previous is finished
// Alternatively, you could make getter for finishedAnimation
if(!cardToRight.finishedAnimation) {
cardToRight.draw();
} else {
// Card at right is finished, we move the left card now
cardToLeft.draw();
}
// If cardToLeft not yet animated, we are still pending
if(!cardToLeft.finishedAnimation) {
// Schedule next loop
// "return" makes sure that we are not executing lines below this
return requestAnimFrame(loop);
}
// Animation finished, starting again after 5 seconds
setTimeout(function() {
// Resetting cards
cardToRight.reset();
cardToLeft.reset();
// Start the loop again
loop();
}, 5000); // 5000 milliseconds = 5 seconds
};
/**
* Main program below
*
*/
// Card to right (1 denotes it's direction to right)
cardToRight = new Card(50, 50, 1);
// Card to left (0 denotes it's direction to left)
cardToLeft = new Card(1000, 50, 0);
// Initialize cardToRight & cardToLeft
// Because using only two cards, we can do a bit nesting here
cardToRight.init(function(err) {
// If error with image loading
if(err) {
return alert(err);
}
// Trying to initialize cardToLeft
cardToLeft.init(function(err) {
// If error with image loading
if(err) {
alert(err);
}
// All ok, lets do the main program
loop();
});
});
As a special note: If you wan't your cards appear outside the canvas remember to change values of below accordingly:
// Card to right (1 denotes it's direction to right)
cardToRight = new Card(50, 50, 1); // 50 <- x position of right card
// Card to left (0 denotes it's direction to left)
cardToLeft = new Card(1000, 50, 0); // 1000 <- x position of left card
Finally, here is working JsFiddle example

Issue with scoping, when calling a function in a callback

when clicking inside the canvas it will generate a ball and move to the clicked location
when the ball get's to its location I want it to remove itself. But i think i have a problem
with the scope when calling the removeBall() function.
You can find a working example her: jsfiddle
/*
* Main app logic
*/
function Main() {
this.canvas = "canvas";
this.stage = null;
this.WIDTH = 0;
this.HEIGHT = 0;
this.init();
}
Main.prototype.init = function() {
console.clear();
this.stage = new createjs.Stage(this.canvas);
this.resize();
//start game loop
createjs.Ticker.setFPS(30);
createjs.Ticker.addEventListener("tick", this.gameLoop);
//click event handler
this.stage.on("stagemousedown", function(evt) {
main.fireBall(evt);
});
};
Main.prototype.fireBall = function(evt) {
var bal = new Bal(evt.stageX, evt.stageY);
};
Main.prototype.resize = function() {
//resize the canvas to take max width
this.WIDTH = window.innerWidth;
this.HEIGHT = Math.floor(window.innerWidth * 9 / 16);
this.stage.canvas.width = this.WIDTH;
this.stage.canvas.height = this.HEIGHT;
};
Main.prototype.gameLoop = function() {
//game loop
main.stage.update();
};
/*
* Ball logic
*/
function Bal(toX, toY) {
this.toX = toX ;
this.toY = toY;
this.widthPerc = 8;
this.init();
}
Bal.prototype.width = function() {
return Math.floor(main.stage.canvas.width / 100 * this.widthPerc);
};
Bal.prototype.init = function() {
//create a new ball
this.ball = new createjs.Shape();
this.ball.graphics.beginFill("green").drawCircle(0, 0, this.width());
this.ball.x = (main.stage.canvas.width / 2) - (this.width() / 2);
this.ball.y = main.stage.canvas.height - 20;
main.stage.addChild(this.ball);
this.move();
};
Bal.prototype.move = function() {
//create a tween to cliked coordinates
createjs.Tween.get(this.ball).to({
x: this.toX ,
y: this.toY ,
scaleX:0.4,scaleY:0.4,
rotation: 180
},
750, //speed
createjs.Ease.none
).call(this.removeBall); // <---- How can i pass the correct scope to the called function?
};
Bal.prototype.removeBall = function() {
//try to remove the ball
main.stage.removeChild(this.ball);
};
var main = new Main();
The solution above using bind works, however there is a much better solution. Bind is not available in all browsers (most notably Safari 5.1, which is a modern browser). http://kangax.github.io/es5-compat-table/#Function.prototype.bind
TweenJS has built-in support for scoping functions when using call(). Just pass the scope as the 3rd argument.
Ball.prototype.move = function() {
console.log(this.toX +","+this.toY);
createjs.Tween.get(this.ball).to({
x: this.toX ,
y: this.toY ,
scaleX:0.4,scaleY:0.4,
rotation: 180
},
750, //speed
createjs.Ease.none
).call(this.removeBall, null, this);
};
You can also pass an array of function arguments as the second parameter.
Tween.call(this.removeBall, [this.ball], this);
Ok found a solution! using the bind() functionality.
Bal.prototype.move = function() {
console.log(this.toX +","+this.toY);
createjs.Tween.get(this.ball).to({
x: this.toX ,
y: this.toY ,
scaleX:0.4,scaleY:0.4,
rotation: 180
},
750, //speed
createjs.Ease.none
).call(this.removeBall.bind(this));
};

How can I create a running animation on an HTML5 canvas?

I am building a simple 2D game as an attempt to learn canvas. The character can run around a virtual environment, and a variable called yOffset controls his offset from the top of the screen. I also have a global variable called running which sets itself to true or false based on whether or not the character is running (not shown here). My goal is to make the character bob up and down whilst he is running, and all the below code does is spawn lots of setInterval()s. Is this the right way to make my character run, or should I do it another way? If so, how?
$(document).keydown(function(e) {
if(e.which == 97) {
running = true;
run();
} else if(e.which == 100) {
running = true;
run();
} else if(e.which == 119) {
running = true;
run();
} else if(e.which == 115) {
running = true;
run();
}
});
(yes, if the character stops running, the running variable does go to false [not shown here] - I've already made sure the running variable works well)
runTimer = 0;
function run() {
if(runTimer == 0 && running) {
runTimer = 1;
yOffset = 80;
setTimeout(function() {
yOffset = 120;
}, 150);
setTimeout(function() { if (running) { runTimer = 0;run(); } }, 300);
}
}
If you need more information, the version that I am currently working on is available here.
I think you can simplify your code, and in fact you must in the quite probable case where you'd like to add some other characters.
To allow re-use of the animation, it's better to separate what is an animation (== the different steps that your character will go through), and an animation state (== in which step your character is now).
I wrote here some elements of an animation system.
So i define what is an animation step, a whole Animation (which is so far only an array of animation step), and an Animator (which holds the state, one might see it as a 'reader' of an animation).
Once you defined the animation and animators, and started the animators, you just have to call tick(time) to have the animation move on, and offset() to read the offset, which is way simpler than fighting with a bunch of setIntervals.
http://jsfiddle.net/xWwFf/
// --------------------
function AnimationStep(duration, offset) {
this.duration = duration;
this.offset = offset;
// you might add : image index, rotation, ....
}
// --------------------
function Animation(animationSteps) {
this.steps = animationSteps; // Array of AnimationStep
}
// define a read-only length property
Object.defineProperty(Animation.prototype, 'length', {
get: function () {
return this.steps.length
}
});
// --------------------
function Animator() {
this.currentAnimation = null;
this.step = -1;
this.running = false;
this.remainingTime = 0; // remaining time in current step;
}
Animator.prototype.startAnim = function (newAnim, firstStep) {
this.currentAnimation = newAnim;
this.step = firstStep || 0;
this.remainingTime = newAnim.steps[this.step].duration;
this.running = true;
}
Animator.prototype.tick = function (dt) {
// do nothing if no animation ongoing.
if (!this.running) return;
this.remainingTime -= dt;
// 'eat' as many frames as required to have a >0 remaining time
while (this.remainingTime <= 0) {
this.step++;
if (this.step == this.currentAnimation.length) this.step = 0;
this.remainingTime += this.currentAnimation.steps[this.step].duration;
}
};
Animator.prototype.offset = function () {
return this.currentAnimation.steps[this.step].offset;
}
// ______________________________
// example
var bounceAnim = [];
bounceAnim.push(new AnimationStep(200, 10));
bounceAnim.push(new AnimationStep(180, 20));
bounceAnim.push(new AnimationStep(150, 30));
bounceAnim.push(new AnimationStep(300, 40));
bounceAnim.push(new AnimationStep(320, 45));
bounceAnim.push(new AnimationStep(200, 40));
bounceAnim.push(new AnimationStep(120, 30));
bounceAnim.push(new AnimationStep(100, 20));
var anim1 = new Animation(bounceAnim);
var animator1 = new Animator();
var animator2 = new Animator();
animator1.startAnim(anim1);
animator2.startAnim(anim1, 3);
// in action :
var ctx = document.getElementById('cv').getContext('2d');
function drawScene() {
ctx.fillStyle = 'hsl(200,60%, 65%)';
ctx.fillRect(0, 0, 600, 200);
ctx.fillStyle = 'hsl(90,60%,75%)';
ctx.fillRect(0, 200, 600, 200);
ctx.fillStyle = 'hsl(10,60%,75%)';
ctx.fillRect(200, 200 + animator1.offset(), 22, 22);
ctx.fillStyle = 'hsl(40,60%,75%)';
ctx.fillRect(400, 200 + animator2.offset(), 22, 22);
animator1.tick(20);
animator2.tick(20);
}
setInterval(drawScene, 20);

Categories