I'm trying to create multiple instances of a slider on a page. Each slider should know which slide it’s currently viewing. It seems that when I update the slide property, I change it for the class, and not the instance. This suggests to me that I'm not instantiating properly in my public init() function. Where am I going wrong?
var MySlider = (function() {
'use strict';
var animating = 0,
slides = 0, // total slides
slider = null,
slide = 0, // current slide
left = 0;
function slideNext(e) {
if ((slide === slides - 1) || animating) return;
var slider = e.target.parentNode.children[0],
x = parseFloat(slider.style.left);
animate(slider, "left", "px", x, x - 960, 800);
slide++;
}
return {
init: function() {
var sliders = document.querySelectorAll('.my-slider'),
l = sliders.length;
for (var i = 0; i < l; i++) {
sliders[i] = MySlider; // I don't think this is correct.
slider = sliders[i];
buildSlider(slider);
}
}
}
})();
Based on your comment, I think this is more like what you want:
MySlider = (function () {
Slider = function (e) {
this.e = e;
// other per element/per slider specific stuff
}
...
var sliders; // define this out here so we know its local to the module not init
return {
init: function () {
sliders = document.querySelectorAll('.my-slider');
var l = sliders.length;
for (var i = 0; i < l; i++) {
sliders[i] = new Slider(sliders[i]); //except I'd use a different array
slider = sliders[i];
buildSlider(slider);
}
}
})();
This way, you are associating each element with it's own element specific data but you have a containing module within which you can operate on your collection of modules.
It seems that when I update the slide property, I change it for the class, and not the instance.
You are correct. That code is run only when the MySlider class is defined. If you want an instance variable, you need to declare it inside the returned object, ie, part of your return block:
var MySlider = (function(param) {
return {
slider: param,
init: function() {
var sliders = document.querySelectorAll('.my-slider'),
l = sliders.length;
for (var i = 0; i < l; i++) {
sliders[i] = MySlider; // I don't think this is correct.
slider = sliders[i];
buildSlider(slider);
}
}
});
Related
I have 3 clickable objects. When one is clicked, this becomes the 'selected unit'.
I am trying to create some generic actions for the units such as moving to a point.
In my create function I initialize the units, when a unit is clicked on - this is supposed to become the 'selected unit' so that my movement and direction function applies to the this unit. However, the script is not able to recognize which unit intend for example I get this error:
Uncaught TypeError: Cannot read property 'velocity' of undefined.
Is there a way to use a variable to indicate selected users and pass that to the functions?
window.onload = function() {
var block_count = 0;
var block = '';
var selected_unit = '';
var unit_clicked = 0;
var tank1 = null;
var game = new Phaser.Game(800, 600, Phaser.AUTO, '', { preload: preload, create: create, update: update, render: render});
function preload () {
game.load.image('block', 'block.png');
game.load.image('tank1', 'tank.png');
game.load.image('baddie', 'tank.png');
game.load.image('mouse_btn', 'block.png');
game.input.mouse.capture = true;
}
function create () {
game.physics.startSystem(Phaser.Physics.ARCADE);
mouse_btn = game.add.sprite(30,30, 'mouse_btn');
mouse_btn.anchor.setTo(0.5, 0.5);
//T1
tank1 = game.add.sprite(30,30, 'tank1');
initialise_player(tank1);
game.physics.enable(tank1, Phaser.Physics.ARCADE);
//T2
tank2 = game.add.sprite(30,60, 'tank1');
initialise_player(tank2);
game.physics.enable(tank2, Phaser.Physics.ARCADE);
game.world.setBounds(0, 0, 2000, 2000);
game.camera.follow(tank1);
}
function update () {
if(selected_unit == '') {
mouse_btn.x = game.input.mousePointer.worldX
mouse_btn.y = game.input.mousePointer.worldY
}
if(game.input.activePointer.leftButton.isDown && block_count == 0 && unit_clicked == 0) {
game.input.activePointer.leftButton.stop(event);
block_count =1;
block = game.add.sprite(game.input.mousePointer.worldX, game.input.mousePointer.worldY, 'block');
game.physics.enable(block, Phaser.Physics.ARCADE);
block.anchor.setTo(0.5, 0.5)
lookAtObject(selected_unit, block, 0.005);
}
if(block.alive){
game.physics.arcade.moveToObject(selected_unit, block, 260, 0)
} else {
console.log(selected_unit)
selected_unit.body.velocity.x = 0;
selected_unit.body.velocity.y = 0;
}
if(game.physics.arcade.collide(selected_unit, block)) {
block_count--;
block.kill();
}
}
function render(){
//console.log(game.physics.arcade.collide(tank1, block))
}
function lookAtObject(obj, target, rotspeed){
var angle = Math.atan2(block.y - tank1.y, block.x - tank1.body.x);
tank1.rotation = angle + game.math.degToRad(90);
}
function initialise_player(tank1){
tank1.anchor.setTo(0.5, 0.5);
tank1.inputEnabled = true;
tank1.input.useHandCursor = true;
tank1.events.onInputDown.add(t1_clicked,this);
tank1.events.onInputOver.add(t1_over, this)
tank1.events.onInputOut.add(t1_out, this)
}
function t1_clicked() {
selected_unit = tank1;
}
function t1_over() {
unit_clicked = 1
}
function t1_out () {
unit_clicked = 0
}
};
The error you're getting on initial load is because in update you're making an assumption that selected_unit exists and has a body.
Update your third if to make sure selected_unit is defined.
if (selected_unit !== '') {
selected_unit.body.velocity.x = 0;
selected_unit.body.velocity.y = 0;
}
However, a better option would be to put this a few lines down, where you kill the block instead.
if(game.physics.arcade.collide(selected_unit, block)) {
selected_unit.body.velocity.x = 0;
selected_unit.body.velocity.y = 0;
block_count--;
block.kill();
}
if (block.alive); moveToObject is also expecting selected_unit to exist and have a body, which may not be the case; wrap it with a check.
if (selected_unit !== '') {
game.physics.arcade.moveToObject(selected_unit, block, 260, 0)
}
That now allows tank1 to rotate to look at the item you just placed, but it doesn't move it until it or tank2 have been clicked on.
This also points out that there are a number of tweaks you'll want to make to your code in general, since you're ignoring arguments that are being passed in. For example, t1_clicked isn't using the sprite that's been clicked on, but is instead just hard-coding tank1. lookAtObject isn't using obj or target, but again has values hard-coded in.
One other thing you may want to change is the following:
if(selected_unit == '') {
mouse_btn.x = game.input.mousePointer.worldX
mouse_btn.y = game.input.mousePointer.worldY
}
If you make that the following, you won't end up with an extra sprite hanging about on the screen.
if (block_count === 0) {
mouse_btn.x = game.input.mousePointer.worldX;
mouse_btn.y = game.input.mousePointer.worldY;
}
I'm trying to create a fire effect for a game in Matter.js, and I need to blur a circle to make it look more realistic. However, I need to make it so it only blurs the fire, not the whole canvas. How can I do this?
This is the code I have so far:
function setOnFire(object) {
var fireX = object.position.x;
var fireY = object.position.y;
var fire = Bodies.circle(fireX, fireY, vw*1, {
isStatic: true,
render: {
fillStyle: "rgba(255,130,0,1)"
}
});
World.add(world, fire);
}
This is not exactly what I had in mind, but it is as close as you can get.
Start by going to matter.js and go to this section:
for (k = body.parts.length > 1 ? 1 : 0; k < body.parts.length; k++) {
part = body.parts[k];
if (!part.render.visible)
continue;
Add this code after the continue;:
if (body.bloom) {
c.shadowColor = body.render.fillStyle;
c.shadowOffsetX = 0;
c.shadowOffsetY = 0;
c.shadowBlur = body.bloom;
}
Then, go to the very end of the loop and add this:
if (body.bloom) {
c.shadowColor = "transparent";
c.shadowOffsetX = 0;
c.shadowOffsetY = 0;
c.shadowBlur = 0;
}
Then, just add the bloom while making your body. For instance:
let fireParticle = Bodies.circle(0, 0, {
bloom: 25
});
1) I'm working on a dat.GUI application on which I have 2 sliders. I want to reset one when the other is changed. For example :
var FizzyText = function() {
this.slider1 = 0;
this.slider2 = 0;
};
var gui = new dat.GUI();
var text = new FizzyText();
var slider1 = gui.add(text, 'slider1', 0, 5);
var slider2 = gui.add(text, 'slider2', 0, 5);
slider1.onChange(function(value){
console.log(value);
text.slider2 = 0; // this doesn't work
});
slider2.onChange(function(value){
console.log(value);
text.slider1 = 0; // this doesn't work
});
This is just an example but it is very important that the slider is reseted or set to its default value (in FizzyText).
The example above comes from https://workshop.chromeexperiments.com/examples/gui/#9--Updating-the-Display-Automatically where I can't automatically update the slider
2) I want to add a reset button in which all sliders will be reseted. But with the previous answer I'd be able to reset all values
I found the answer :
gui.__controllers is and array of controllers. So I just added something like that :
var FizzyText = function () {
this.slider1 = 0;
this.slider2 = 0;
};
var gui = new dat.GUI();
var text = new FizzyText();
var slider1 = gui.add(text, 'slider1', 0, 5);
var slider2 = gui.add(text, 'slider2', 0, 5);
/* Here is the update */
var resetSliders = function (name) {
for (var i = 0; i < gui.__controllers.length; i++) {
if (!gui.__controllers.property == name)
gui.__controllers[i].setValue(0);
}
};
slider1.onChange(function (value) {
console.log(value);
resetSliders('slider1');
});
slider2.onChange(function (value) {
console.log(value);
resetSliders('slider2');
});
It's best to reset dat.GUI's values by using the controller's .initialValue instead of hard coding it, so the following would be preferable: gui.__controllers[i].setValue(gui.__controllers[i]);
You can reset all of a GUI's controllers by using gui.__controllers.forEach(controller => controller.setValue(controller.initialValue));
I'm dabbling with canvas. And I'm a little lost on something.
I have this function:
function preloadimages(arr) {
var newimages = []
var arr = (typeof arr != "object") ? [arr] : arr
for (var i = 0; i < arr.length; i++) {
newimages[i] = new Image()
newimages[i].src = arr[i]
}
}
And I call it like so:
preloadimages(['images/background.png', 'images/hero.png', 'images/monster.png']);
The only problem is, I don't know how to then draw them again later.
If I was preloading one image inside my js I would say:
var bgOk = false;
var bg = new Image();
bg.onload = function () {
bgOk = true;
};
bg.src = "images/background.png";
and then further down when I wanted it drawn I would say:
if (bgOk) {
context.drawImage(bg, 0, 0);
}
And that would be that. The problem is I have made a preloader class, I don't really know how now to call in just the image I want to draw now, or even how to implement the bgOk idea so that if it loaded ok, I can draw it, and if not, leave it alone.
Could someone advise me on this? I'm basically just trying to go more class based rather than the dirty great mess I normally have with a huge javascript file that is ugly and not as maintainable.
This seems to be a complicated problem, but in reality isn't as bad as it looks. If you want to use pre-existing code, or just want to look at something for ideas you can have a look at: http://thinkpixellab.com/pxloader/ This library was used in the HTML5 version of Cut The Rope.
A simple custom implementation could be something like the following:
function loadImages(arr, callback) {
this.images = {};
var loadedImageCount = 0;
// Make sure arr is actually an array and any other error checking
for (var i = 0; i < arr.length; i++){
var img = new Image();
img.onload = imageLoaded;
img.src = arr[i];
this.images[arr[i] = img;
}
function imageLoaded(e) {
loadedImageCount++;
if (loadedImageCount >= arr.length) {
callback();
}
}
}
And then you can call it like this:
var loader = loadImages(['path/to/img1', 'path/to/img2', 'path/to/img3'], function() {
ctx.drawImage(loader.images['path/to/img1']); // This would draw image 1 after all the images have been loaded
// Draw all of the loaded images
for (var i = 0; i < loader.images.length; i++) {
ctx.drawImage(loader.images[i]);
}
});
If you want more details on asset loading you can have a look at the asset loading section of Udacity's HTML5 Game Development course https://www.udacity.com/course/cs255
A function I use:
function ImageLoader(sources, callback)
{
var images = {};
var loadedImages = 0;
var numImages = 0;
// get num of sources
for (var src in sources) {
numImages++;
}
for (var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if (++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
You call it like so:
var sources = {
bg: path/to/img.png,
title: path/to/img/png
};
var _images = {};
isReady = ImageLoader(sources, function(images) {
_images = images;
});
And then to access your images
_images.bg;
Example: drawImage(_images.bg, 0, 0);
How can I make the snow clear after a certain time. I've tried using variables and the calling a timeout which switches on to false and stops the makesnow() function but that doesn't seem to clear the page at all.
<script language="javascript">
ns6 = document.getElementById;
ns = document.layers;
ie = document.all;
/*******************[AccessCSS]***********************************/
function accessCSS(layerID) { //
if(ns6){ return document.getElementById(layerID).style;} //
else if(ie){ return document.all[layerID].style; } //
else if(ns){ return document.layers[layerID]; } //
}/***********************************************************/
/**************************[move Layer]*************************************/
function move(layer,x,y) { accessCSS(layer).left=x; accessCSS(layer).top = y; }
function browserBredde() {
if (window.innerWidth) return window.innerWidth;
else if (document.body.clientWidth) return document.body.clientWidth;
else return 1024;
}
function browserHoyde() {
if (window.innerHeight) return window.innerHeight;
else if (document.body.clientHeight) return document.body.clientHeight;
else return 800;
}
function makeDiv(objName,parentDiv,w,h,content,x,y,overfl,positionType)
{
// positionType could be 'absolute' or 'relative'
if (parentDiv==null) parentDiv='body';
var oDiv = document.createElement ("DIV");
oDiv.id = objName;
if (w) oDiv.style.width = w;
if (h) oDiv.style.height= h;
if (content) oDiv.innerHTML=content;
if (positionType==null) positionType="absolute";
oDiv.style.position = positionType;
if (x) oDiv.style.left=x; else oDiv.style.left=-2000;
if (y) oDiv.style.top=y; else oDiv.style.top=-2000;
if (overfl) oDiv.style.overflow=overfl; else oDiv.style.overflow="hidden";
eval(' document.'+parentDiv+'.appendChild (oDiv); ');
delete oDiv;
}
var snowC=0;
var x = new Array();
var y = new Array();
var speed = new Array();
var t=0;
var cC = new Array();
var ra = new Array();
function makeSnow() {
x[snowC] = Math.round(Math.random()*(browserBredde()-60));
y[snowC] = 10;
makeDiv("snow"+snowC,"body",32,32,'<img src="http://i693.photobucket.com/albums/vv296/KIBBLESGRAMMY/CAT/Orange-tabby-cat-icon.gif">');
speed[snowC] = Math.round(Math.random()*8)+1;
cC[snowC]=Math.random()*10;
ra[snowC] = Math.random()*7;
snowC++;
}
function moveSnow() {
var r = Math.round(Math.random()*100);
if (r>70 && snowC<20) makeSnow();
for (t=0;t<snowC;t++) {
y[t]+=speed[t];move("snow"+t,x[t],y[t]);
if (y[t]>browserHoyde()-50) {y[t] = 10;x[t] = Math.round(Math.random()*(browserBredde()-60));}
cC[t]+=0.01;
x[t]+=Math.cos(cC[t]*ra[t]);
}
setTimeout('moveSnow()',20);
}
moveSnow();
</script>
makeSnow just adds the snowflakes. Stopping that, as you say, does not clear anything. moveSnow handles the animation, and calls itself at a timeout. If instead of setting a timeout for the next moveSnow each time, you set it up to run in an interval just once, you would have an easier time stopping it.
window.snowAnimation = window.setInterval(moveSnow, 20);
If you add a css class to your snow flakes, it would be easier to target them for deletion.
oDiv.className = 'snowflake';
Then your clear function could look something like:
function clearSnow() {
window.clearTimeout(window.snowAnimation);
var flakes = document.getElementsByTagName('snowflake');
for(var i = 0, l = flakes.length; i < l; i++) {
document.body.removeChild(flakes[i]);
}
}
Timeout doesnt help, it helps you only to stop creating new snowdivs, however if you see makeDiv is the one which creates new divs on to the body, if you clear / display:none the divs which got created on makeDiv i hope it will clear all the divs on the screen.
You need to remove the divs that were created. It might be easier if you give them all some sort of class, like ".snowflake" as you create them (in makeDiv), then start removing them from the dom.
You will have to clear the elements created after the time you wanna stop snow falling.
Following code snippet will help you to clear the elements
if(count < 500){
setTimeout('moveSnow()',20);
}else{
var i = 0;
var elem = document.getElementById('snow'+i);
do{
elem.parentNode.removeChild(elem);
elem = document.getElementById('snow'+ ++i);
}while(elem != null)
}
count++;
you have to create a global variable count.