I got a JS question, I can't seem to be able to call a method. I'm using this dragger script: http://skidding.github.io/dragdealer/
To use it I gotta define it like this:
new Dragdealer('just-a-slider', {
animationCallback: function(x, y) {
$('#just-a-slider .value').text(Math.round(x * 100));
}
});
The documentation says:
setValue(x, y, snap=false) Set the value of a Dragdealer instance
programatically. The 3rd parameter allows to snap the handle directly
to the desired value, without any sliding transition.
I tried:
var dragdealer_instance = new Dragdealer('just-a-slider', {
animationCallback: function(x, y) {
$('#just-a-slider .value').text(Math.round(x * 100));
}
});
dragdealer_instance.setValue(10, 1);
but it says that the dragdealer_instance is undefined.. how can I call that function?
Related
I have been trying to make a JavaScript animation of moving circle in HTML Canvas without using global variables. I am using requestAnimationFrame function. Since JavaScript does not support passing variable by reference, I tried creating a Circle class:
class Circle{
constructor(x, y, dx, dy) //set initial position and velocity of circle
{
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
}
}
function moveCircle(circle, other variables)
{
//clear canvas
//calculate new position and velocity using circle.x, etc.
//save new values to the object
//draw new circle into the canvas
requestAnimationFrame(moveCircle);
}
function button()//function called after click on button
{
//initial settings of canvas, intial condition
circle = new Circle(x, y, dx, dy);
moveCircle(circle, other vars);
}
This makes one frame and then throws error "Cannot read property 'x' of undefined". What am I doing wrong? Is there any other way of doing this, while avoiding global variables?
First of all, you don't need to create a class you can just pass the coordinates in an object or as separate arguments.
Secondly you should use Function#bind on requestAnimationFrame to pass the same arguments to next call.
Example using object:
function moveCircle(circle) {
console.log(circle.x);
if (circle.x) {
circle.x -= circle.dx;
requestAnimationFrame(moveCircle.bind(null, circle));
}
}
function button() {
moveCircle({
x: 500,
y: 0,
dx: 20,
dy: 0
});
}
button();
Example without object:
function moveCircle(x, dx) {
console.log(x);
if (x) {
requestAnimationFrame(moveCircle.bind(null, x - dx, dx));
}
}
function button() {
moveCircle(500, 20);
}
button();
To keep things simple you could use a closure to create a handler that internally knows what your circle looks like and how it should move. A closure is simply a function that defines its own variables locally, then returns a function that can access those.
We want to return a function that only accepts one argument: time, since that is the argument passed into every handler in the AnimationFrame by the browser. Then we want the closure function to draw into the globally defined canvas. Have a look here:
const canvas = document.body.appendChild( document.createElement( 'canvas' ) );
function makeMovingCircle( canvas, x, y, radius, distance, duration ){
// Lets get the context to draw here so we only need to fetch it once and store it, saving some computing time in favour of storing into memory.
const ctx = canvas.getContext( '2d' );
// We need to return a named function here so it can interally call itself again in requestAnimationFrame
return function AnimationHandler( time ){
// Lets just calculate an offset here based on the distance and duration we passed in above.
const progress = (time % duration / duration) * distance;
ctx.clearRect( 0, 0, canvas.width, canvas.height );
ctx.beginPath();
ctx.arc( x + progress, y, radius, 0, Math.PI*2, true );
ctx.stroke();
// Now call the named handler for the next animationFrame
window.requestAnimationFrame( AnimationHandler );
}
}
// Now lets make an animation handler and add it to the animationFrame. If you ever want to cancel it, you might want to store it in a global variable though so you can call cancelAnimationFrame on it.
window.requestAnimationFrame(
makeMovingCircle( canvas, 15, 15, 10, 100, 2000 )
);
I did a quick example from my memory:
const circle = $('#circle');
class Main {
constructor() {
}
start() {
requestAnimationFrame(this.loop.bind(this))
}
loop() {
const pos = circle.position();
// speed is a constant 1,1. But you could replace it by a variable
circle.css({top: pos.top+1, left: pos.left+1, position:'absolute'});
requestAnimationFrame(this.loop.bind(this))
}
}
const main = new Main();
main.start();
<div>
<img id="circle" src="https://playcode.io/static/img/logo.png"
alt="PlayCode logo">
<h1 id="msg"></h1>
</div>
#circle {
position: absolute;
top: 0;
left: 0;
}
In js, object are passed by reference, and primitive types by value.
I think you should avoid having other variables in moveCircle function. That kind of function is usually called "loop" or "gameLoop" or "update". When your button is clicked, add a speed to the circle, without creating a new circle, something like myCircle.speed = {x:2, y:2}. In the gameloop, add the speed to the position each frame.
Think also about delta time, as requestAnimationFrame will be faster depending on the PC/Mobile that run it.
You can wrap your application into on Main class if you like. (like I did above)
Finnaly, if you insist passing parameters to moveCircle, you can use bind.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind
function c(a,b,c) {
console.log(a); console.log(b); console.log(c);
}
c.bind(window, 4,3)
// function c() // bind will return a function ! In a class usually the context that you want to pass is `this`. If you are playing directly in the console, the context is window by default (not that it matter in this example)
c.bind(window, 4,3)()
// 4
// 3
// undefined
The first call to moveCircle (from what I understand, it is in button function), should also be a requestAnimationFrame like requestAnimationFrame(moveCircle.bind(window, other vars)), you could re-use that in moveCircle as well, to avoid any global variable. Still, I recommend making a class to wrap your application, so that you can use local variables to your class to save the current state of your game instead of having that same state living inside the function arguments only.
I am trying to create a simple animation that makes a car image most across the screen (learning exercise). I am trying to make it happen with setInterval, and this doesn't seem to be working. The image is stationary. See code below:
var Car = function(x, y) {
this.x = x;
this.y = y;
};
Car.prototype.draw = function() {
var carHtml = '<img src="http://nostarch.com/images/car.png">';
Car.prototype.moveRight = function(distance) {
this.x;
this.carElement.css({
left: distance,
top: this.y
});
};
var tesla = new Car(20, 20);
tesla.draw();
function runOnInterval(fn, arg) {
setInterval(function () {
fn(arg);
}, 1000);
}
runOnInterval(tesla.moveRight(), 10);
As I said, this results in the image being stationary, and an error message in the console saying "fn is not a function". Can anyone tell me what I am missing here? Thank you.
Arguments are evaluated eagerly. If you have foo(bar()) then bar is called first and its return value is passed to foo.
That's what happens in
runOnInterval(tesla.moveRight(), 10);
It calls tesla.moveRight() and passes the return value (undefined) to runOnInterval which will then error because undefined is not a function.
You have to pass tesla.moveRight itself and if you have to bind its this value to the correct object:
runOnInterval(tesla.moveRight.bind(tesla), 10);
// or
runOnInterval(function(x) { tesla.moveRight(x); }, 10);
See also:
Calling functions with setTimeout()
How to access the correct `this` inside a callback?
http://codepen.io/Lewitje/pen/GjqbbA
Looking at this and trying to figure out what is happening is discouraging me. I've been doing a javascript deep dive lately and I understand objects, constructors, prototypes etc. and on css end I know animation with keyframes, canvas, coordinates etc yet looking at this I know maybe 40-50% of the javascript that is written there.
1) I know that a class and constructor is declared (this is relatively new to javascript?)
2) jQuery .each method is used to attach labels A1, A2 etc. with setupLabels()
3) A variety of different functions to display the number punched in on the dial, move the arm, move the can etc.
Now this is where it gets confusing for me:
setPosition(x, y, callback){
$('.hand').on('transitionend', ()=>{
$('.hand').off('transitionend');
setTimeout(function(){
callback();
}, 500);
});
this.calculateVelocity(x, y, (velX, velY)=>{
$('.arm').css({
'top': (y + 35) + 'px',
'transition-duration': velY + 's'
});
$('.hand').css({
'left': (x + 5) + 'px',
'transition-duration': velX + 's',
'transition-delay': velY + 's'
});
});
}
calculateVelocity(x, y, callback){
var currentX = $('.hand')[0].offsetLeft;
var currentY = $('.arm')[0].offsetTop;
var velX = Math.ceil((Math.max(currentX, x) - Math.min(currentX, x)) / 70);
var velY = Math.ceil((Math.max(currentY, y) - Math.min(currentY, y)) / 70);
callback(velX, velY);
}
}
I'm guessing the this.calculateVelocity is infact, calling the function and the what comes after => is defining the callback function?
If that is the case how does the callback in setPosition work as it hasn't been defined?
JavaScript starts by compiling a list of all the function names before it runs, so calculateVelocity has been defined. Additionally, you are correct, (velX, velY) => {} is the callback, and the function is being called. It's in the new Arrow function format.
Both of the following are identical:
this.calculateVelocity(x, y, (velX, velY) => {
...
});
this.calculateVelocity(x, y, function(velX, velY) {
...
});
Inside setPosition, this refers to the global object. In this case, calculateVelocity is a function on the global object, and so it can be called via this.calculateVelocity.
I should also note that the source JavaScript in the linked Codepen is actually TypeScript/ES7.
So i'm starting to learn ab it of JS. This is probably kinda basic since I started with it yesterday. So I have come so far that I actually got a error that I have been trying to figure out this past 2-3 hours without any results.
I'm trying to take the value from height and width where I create a own function (area) that should make a math value from height * width / 2 and then return it back to area(). and then get the value in console.log. Pretty simple but im getting a error on the last console.log for some strange reason which I cant figure out why.
function x(a, b) {
return {
a: a, b: b
}
}
function Area(a, b) {
this.a = a;
this.b = b;
this.y = this.a* this.b / 2;
return y;
}
var Snorlax = x(12, 14);
console.log(Snorlax.a);
console.log(Snorlax.b);
console.log(Snorlax.Area()); <---- Issue here
What could the issue be?
EDIT: So the issue is that im getting a issue on console.log(Snorlax.Area()); <---- Issue here where it says Snorlax.Area is not a function so basically I just wanna fix it to make this program to work. By having the console.log(Snorlax.Area()); it supposed to make the function Area to work and give me a results of a*b/2. Thats pretty much it.
EDIT2:
function x(a, b) {
return {
a: a, b: b
}
}
function Area(a, b) {
return a * b / 2;
}
var Snorlax = x(12, 14);
console.log(Snorlax.a);
console.log(Snorlax.b);
console.log(Area(Snorlax.a, Snorlax b));
In that case that would be right?
Your not passing in a,b to the Area() function
Try: console.log(Area(Snorlax.a,Snorlax.b));
Return this.y from the Area function
The reason you function is not defined is because you were calling an Area function on your x object(which does not exist). You need to call the Area function straight like above.
I am developing a game using the framework atomJS and library libCanvas. Here is the code where the error occurs:
var Planet=atom.Class({
//other code
clearLayer : function (layer) {
layer.ctx.clearRect(this.x, this.y, this.size, this.size);
},
colonize : function (layer, angle, color,ms) {
**this.clearLayer(layer);**
drawArc({
context: layer.ctx,
x: Math.round(this.x + this.size / 2),
y: Math.round(this.y + this.size / 2),
radius: this.radius + 5,
width: 4,
color: color,
opacity: 0.6,
angleFinish: angle
});
if (this.colonizing) {
//if (this.cursorOnPlanet()) this.context.fillText(COLONIZING, (this.x + this.size / 2) - 30, this.y + this.size - 2);
this.colonizingTimer = setTimeout(this.colonize, ms,layer, angle + 5, color,ms);
if (angle > 360) {
this.colonizing = false;
this.state = 1;
}
} else {
clearTimeout(this.colonizingTimer);
this.clearLayer(layer);
}
},
});
On this line, this.clearLayer(layer); the script terminates with an error Object [object DOMWindow] has no method 'clearLayer'.Tell me please what's the problem?
Thanks!
It's important to see how whateverObject.colonize() is actually getting called. Anyway, it's clear that the original object's method is being bound to a different object before getting called. This is fairly common in event handlers, for example, where this usually (but not always) ends up being the event target, not the method's original object.
It's common for developers to use a closure to ensure that they have a safe reference for the original this. For example, you might define colonize in a constructor that says var self=this;, which would guarantee the name self points to the original this even if this itself gets rebound.
Another approach is to use Function.prototype.bind (which you'd have to polyfill for old JS engines), which creates a new function with a this object guaranteed to be whatever you specify.
It sounds like the function is being called from the DOM window and not the local class. When the this object is a window, you'll inevitably have scoping issues.
Your problem is with the setTimeout function. When the timeout is called, it's telling the DOMWindow, not the local class, to call the function. To fix this, wrap the call into a function.
function(){<code>}
Edit: I'm not really sure of the purpose of the extra fields in the setTimeout, so I omitted my solution. If you wrap whatever you're doing in a function, it should work though.
change
this.colonizingTimer = setTimeout(this.colonize, ms,layer, angle + 5, color,ms);
to
var self = this;
this.colonizingTimer = setTimeout(function(){self.colonize.call(self);}, ms,layer, angle + 5, color,ms);
The thing is that because of the timeout, the this object is removed from your object scope and at execution time refers to the global object(window) which has no method named clearLayer.
Here's a simplified demo to see the difference.
& the most correct way is to use "delay":
this.colonizingTimer = this.colonize.delay(ms, this, [layer, angle + 5, color, ms]);
But, if i understand right you want to animate angle from zero to 360 degrees? Why dont you use "Animatable" & ".animate" ?
With every question about LibCanvas you can send me an email to shocksilien#gmail.com