Changing background Image depending on level in game javascript? - javascript
So I'm building a water pipe base game which can be seen here http://www.mckenziedave.co.uk/client_files/gabi_pipes/
I thought id post it rather than explain it.
Im using HTML5 and JS script but I have hit a little problem, I wish to change the background depending on what level the user is on. Would this be best done by CSS or could I implement it into the java script? Instead of posting all the scripts I have just posted the level selector and level creator (the game board works on a basis of 0/1).
$(document).ready(function(){
PipeGame.configure({
cols: 4,
rows: 6,
startX:0,
StartY:0,
lastX:3,
lastY:5,
godMode: true,
autoStart: null
});
var board =[[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]],
[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]],
[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]],
[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]];
PipeGame.setGameBoard(board);
var slider = new Slider($(".options"))
});
var Slider = function(el){
this.el = el;
this.dragging = false;
this.startx = this.el.offset().left;
this.el.on("touchstart",this.startDrag.bind(this));
this.el.on("touchmove",this.drag.bind(this));
this.el.on("touchend",this.stopDrag.bind(this));
}
Slider.prototype.startDrag = function(e){
this.startx = e.originalEvent.touches[0].pageX;
this.dragging = true;
}
Slider.prototype.stopDrag = function(e){
this.dragging = false;
}
Slider.prototype.drag = function(e){
var pos = Math.round($(".plumbing-creator").position().left - (this.startx - e.originalEvent.touches[0].pageX) );
if(this.dragging){
this.el.css({left: pos +"px"})
}
}
var levels = {
level1: [[["0","1","0","1"],["0","0","0","0"],["0","0","0","0"],["0","0","0","0"],["0","0","0","0"],["0","0","0","0"]],[["0","0","1","1"],["0","1","1","0"],["0","0","0","0"],["0","0","0","0"],["0","0","0","0"],["0","0","0","0"]],[["0","0","0","0"],["0","0","1","1"],["1","1","0","0"],["0","0","0","0"],["0","0","0","0"],["0","0","0","0"]],[["0","0","0","0"],["0","0","0","0"],["0","0","1","1"],["1","0","1","0"],["1","0","1","0"],["1","1","0","0"]]],
level2: [[["0","1","1","0"],["0","1","1","0"],["0","0","0","0"],["0","0","0","0"],["0","0","0","0"],["0","0","0","0"]],[["0","0","0","0"],["0","1","1","0"],["0","1","1","0"],["0","0","0","0"],["0","0","0","0"],["0","0","0","0"]],[["0","0","0","0"],["0","0","0","0"],["0","1","1","0"],["0","1","1","0"],["0","0","0","0"],["0","0","0","0"]],[["0","0","0","0"],["0","0","0","0"],["0","0","0","0"],["0","1","1","0"],["0","1","0","1"],["0","1","1","0"]]],
level3: [[["0","1","0","1"],["0","1","1","0"],["0","0","1","1"],["1","1","0","0"],["0","1","1","0"],["1","1","0","0"]],[["0","1","0","1"],["0","1","0","1"],["0","1","0","1"],["0","1","0","1"],["0","1","0","1"],["0","1","0","1"]],[["0","1","0","1"],["0","1","0","1"],["0","1","0","1"],["0","1","0","1"],["0","1","0","1"],["0","1","0","1"]],[["0","0","1","1"],["1","0","0","1"],["0","0","1","1"],["0","1","0","1"],["1","0","0","1"],["0","1","0","1"]]],
}
Anyone know of an easy way to change the background depending on the level?? Thank you :)
You should use CSS.
For each level, you can create a different CSS class.
Example:
.level1
{
background-color: red;
}
.level2
{
background-color: blue;
}
I can think of a couple ways off the top of my head depending on the rest of your code.
1) When a level changes, set a class on the body.
JS
document.getElementsByTagName('body')[0].className = 'level-whatever';
CSS
body.level-whatever {
background: do-what-you-want
}
2) Just change the background with JS.
var body = document.getElementsByTagName('body')[0];
body.style.backgroundImage = 'url("whatever-url")';
I notice that your main.js, which has the following configuration:
PipeGame.configure({
cols: 4,
rows: 6,
startX:0,
StartY:0,
lastX:3,
lastY:5,
godMode: true,
autoStart: null
});
Is called everytime you visit a different arcade.html#* page. You could potentially add another configuration setting:
background: level*,
And change something based on that. To be honest, this is a pretty broad question with a lot of different ways to accomplish the task at hand. Applying a CSS rules based on your level (like so):
var currentLevel = parseInt(window.location.hash.substring(1)) || 1;
if(currentLevel == 1){
$("body").addClass("last-level");
$("body").style(...);
} // Use as a Case Statement or have an array of level backgrounds, etc
Seems to be the consensus here.
Related
Disable Touch on Materialize Carousel
It looks like no one has asked this question before since I've pretty much scoured the internet looking for a very simple answer. How would one go about disabling the ability to swipe left/right on the materialize carousel?
in Materialize.js add/edit: var allowCarouselDrag = true; value: function _handleCarouselDrag(e) { if(allowCarouselDrag){ .... } } You can set the allowCarouselDrag variable per application.
I solved it like this // Create carouser $('#carousel').carousel({ fullWidth: true, indicators: false, duration: 100, }); // Get instance of carousel carouselInstance = M.Carousel.getInstance(sliderDOM); // Remove event listeners added by Materialize for corousel document.getElementById("carousel").removeEventListener('mousedown', carouselInstance._handleCarouselTapBound); document.getElementById("carousel").removeEventListener('mousemove', carouselInstance._handleCarouselDragBound); document.getElementById("carousel").removeEventListener('mouseup', carouselInstance._handleCarouselReleaseBound); document.getElementById("carousel").removeEventListener('mouseleave', carouselInstance._handleCarouselReleaseBound); document.getElementById("carousel").removeEventListener('click', carouselInstance._handleCarouselClickBound); After that drag/swipe is disabled and you can still change page/item via carouselInstance.set(0); and carouselInstance.next(0);
This is far from a perfect solution, and it might disable too much of the functionality in your case, I'm not sure. An option to turn this on/off would be much appreciated. But for my needs, turning off the events on the carousel did the job: var carousel = $('.carousel.carousel-slider').carousel(); // Disable all swipping on carousel if (typeof window.ontouchstart !== 'undefined') { carousel.off('touchstart.carousel'); } carousel.off('mousedown.carousel');
function tap(e) { pressed = true; dragged = false; vertical_dragged = true; reference = xpos(e); referenceY = ypos(e); velocity = amplitude = 0; frame = offset; timestamp = Date.now(); clearInterval(ticker); ticker = setInterval(track, 100); }
I have attempted to solve this problem for the past ~three days and have come to the conclusion that there is no clean solution other than directly editing the materialize.js file as in Lester's answer. Unfortunately, this is not an ideal solution as it causes issues when updating Materialize etc. The simplest solution that I have come up with after this time is the following piece of javascript: window.onload = function() { window.mouseDownNow = false; // The selector below must be more specific than .carousel.carousel-slider in // order for the event to be cancelled properly. $('.class-added-to-block-swiping-functionality') .mousedown(function() { window.mouseDownNow = true; }) .mousemove(function(event) { if(window.mouseDownNow) { event.stopPropagation(); } }) .mouseup(function() { window.mouseDownNow = false; }); }; This will simply stop the event from bubbling to the Materialize swiping functionality. Note: I am not sure how specific the selector must be, mine were classes that were specific to text areas.
Most performant way to do document wide PNG animation
I've got a GIF animation that I use thoughout my site as a saving/loading icon: Because of edges on different background colors, I'd like to change it to a PNG animation. When I want to show a loader at this moment I only have to make sure the following span is visible: <span class="loader"></span> There are several ways how this span be inserted in the document: through knockout visible binding, through JS, only by stylesheets etc. Problem I don't want my animation code to be aware of how this span ended up visible on the document, I just want him to animate it. Of course scanning the whole document every frame (16 fps) for potential new spans with the 'loader' class, just to know which position properties need to be animated is not quite performant. So what would be a good performant way to do a document wide png animation? Note that I do need to support IE8 :(
Using a sprite in combination with CSS background-position comes immediately to mind: (yes I know it's a pretty shitty one, but it'll do the job). You mentioned Knockout, and as its aim is to separate logic from presentation, I'll use that. However, there's no way to completely separate it that I know of that doesn't come with a performance cost (cf your comment). Typically in Knockout bindingHandlers are used to do DOM manip independently of your viewModel. Haven't tested, but should normally work on IE8. Run the snippet below for a demo ko.bindingHandlers.loadIndicator = { update: function(element, valueAccessor) { var val = ko.unwrap(valueAccessor()); if (val == true) { var intv = setInterval(function() { var bgX = parseInt(element.style.backgroundPosition.split(' ')[0].replace('px','')); if (bgX > -48) // 4 frames of 14px element.style.backgroundPosition = (bgX - 14) + 'px'; else element.style.backgroundPosition = '0px'; }, 150); element.style.display = 'block'; } else { element.style.display = 'none'; clearInterval(intv); } } }; var app = { loading: ko.observable(true) }; ko.applyBindings(app); // simulate 'loaded' after 3 secs setTimeout(function() { app.loading(false); }, 3000); .loader { display: block; width: 14px; height: 14px; background-image: url(http://i.imgur.com/1OZACw8.png); } <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script> <span class="loader" data-bind="loadIndicator: loading"></span> If you think a custom binding is not worth it, you can use the visible binding in combination with a transparent animated GIF (yes, it is possible). See for example: http://blog.ciuly.com/general/internet/making-animated-gif-transparent-with-gimp/. Any way you choose, you'll have to keep track of whether something has finished loading/ saving, for example in a KO observable property. Prefer not using Knockout? You could do the same in vanilla JS/ jQuery. However, because you have to track the status from somewhere, you have to either interval-check DOM attributes (which achieves the same as Knockout does automatically on observables) or choose a variant of the approach below where you call an init/ stop function to hide/display the loader. function loader(container) { var elem = document.createElement('span'); elem.className = 'loader'; container.appendChild(elem); var intv = setInterval(function() { var bgX = parseInt(elem.style.backgroundPosition.split(' ')[0].replace('px','')); if (bgX > -48) // 4 frames of 14px elem.style.backgroundPosition = (bgX - 14) + 'px'; else elem.style.backgroundPosition = '0px'; }, 150); this.stop = function() { clearInterval(intv); container.removeChild(elem); }; } var x = new loader(document.body); setTimeout(function() { x.stop() }, 10000); Performance-wise I believe the animated transparent GIF with visible binding and the vanilla init/stop method are the 2 best candidates.
onmouseout and onmouseover
I am working on homework that involves working with javascript. Part of my homework assignment is to use the event handlers onmouseout and onmouseouver. What is supposed to happen when the user hovers over a specific div element, the font size grows by 25%, and when the user mouses out of the div element, the font size goes back to normal. My question is, is it possible to incorporate both an onmouseover function and an onmouseout function into one function? Somehow that is what my teacher wants us to do. I have this started so far. function FontSize(x) { x.style.fonstSize = large; } I'm also thinking this isnt the correct code to make the font 25% larger, but I'm not sure how to really incorporate an onmouseout in this function.
As a teacher myself, I am 99% sure that by "one function" the instructor means one general-purpose function to change the font size, not one function which uses conditional statements to work backwards and figure out whether it should be doing onmouseout or onmouseover. Your script should contain: function resize(elem, percent) { elem.style.fontSize = percent; } Your HTML should contain: <div onmouseover="resize(this, '125%')" onmouseout="resize(this, '100%')" Text within div.. </div> Note: Situations such as here, are exactly why JavaScript has the keyword "this"--to save us from needing to use complicated document.getElementById() statements.
You can use "%" property for controlling font-size as described here with the following code. document.getElementById("div1").onmouseover = function() { document.getElementById("div1").style.fontSize = "125%" }; document.getElementById("div1").onmouseout = function() { document.getElementById("div1").style.fontSize = "100%"; }; Here is the working jsfiddle : http://jsfiddle.net/LxhdU/
Yes you can. Call the same function on both events, and pass a parameter to indicate whether the fontsize should increase or decrease. ChangeFontSize = function(element, shouldIncreaseFontsize) { var small=14; var large = small * 1.25; if(shouldIncreaseFontsize) { element.style.fontSize = large + "px"; } else { element.style.fontSize = small + "px"; } } http://jsfiddle.net/TMHbW/1/
I'd do something simple like the following. The large and small values can be whatever you need them to be for the font size to work or they can be variables you've defined in prior code. Demo: http://jsfiddle.net/lucuma/EAbYn/ function doHover(e) { if (e.type=='mouseover') { this.style.fontSize = "large"; } else { this.style.fontSize = "small"; } } var el = document.getElementById('myelement') el.onmouseout =doHover; el.onmouseover=doHover;
It is possible you do not need to call both the events on the element explicitly instead extension you create will do that.Extend the Element's prototype. Jquery also does similar to this. Ref Prototype See Fiddle:- http://jsfiddle.net/4fs7V/ Element.prototype.hover= function( fnOver, fnOut ) { this.onmouseover=fnOver; this.onmouseout=fnOut || fnOver; return this; }; document.getElementById('test').hover(function(){ //do your mouseover stuff }, function(){ //do your mouseout stuff }); Update Same can be achieved with just one function too:- Hover me .largeFont { font-size:125%; } Element.prototype.hover = function (fnOver, fnOut) { this.onmouseover = fnOver; this.onmouseout = fnOut || fnOver; return this; }; document.getElementById('test').hover(changeMe); function changeMe() { if(this.hasAttribute('class')) { this.removeAttribute('class'); } else { this.setAttribute('class', 'largeFont'); } }
How to pass an array to an user defined object or convert non-OO code to OO? javascript
I really have trouble with OO coding in js. I have written a piece of code which rotates through 3 divs, and pauses on hover of any div. This code is just regular js using an array/json as the input. the code is a bit long so sorry about that. I just need some guidance on how I can convert this primitive code to a better form, as in OO and encap. When I tried myself I could not pass the slides array/json to my defined object. Is there a trick or guideline i can follow on how to rewrite this to a better form? Edit - What is a good guideline to follow so I can rewrite this with objects instead of global variables and loose functions var slideIndex = 0; var prevIndex = 0; var t; function initPromo(){ sortSlides(); nextPromo(); addListeners(); } function addListeners(){ for(var i=0; i<slides.length; i++) $(slides[i].el).hover(function(){ stopPromo(); }, function(){ resumePromo(); }); } function resumePromo(){ startTimer(); } function stopPromo(){ clearTimeout(t); } function nextPromo(){ if(slideIndex > 0 || prevIndex > 0) $(slides[prevIndex].el).css("display","none"); $(slides[slideIndex].el).css("display","block"); prevIndex = slideIndex; slideIndex = (slideIndex<slides.length-1) ? slideIndex+1 : 0; startTimer(); } function startTimer(){ t = setTimeout("nextPromo()", 3000); } function SortByWeight(a,b) { return b.weight - a.weight; } function SortByWeightFr(a,b) { return b.frWeight - a.frWeight; } function sortSlides(){ ($("body.en").length > 0) ? slides.sort(SortByWeight) : slides.sort(SortByWeightFr); } var slides = [ { el:'#ps1', weight:1, frWeight:3 }, { el:'#ps2', weight:0.5, frWeight:6 }, { el:'#ps3', weight:4, frWeight:9 } ]; window.onload = function () { initPromo(); }; HTML <body class="en"> <div id="homepageSlides"> <div id="promoSlides"> <div id="ps1">ps1</div><div id="ps2">ps2</div><div id="ps3">ps3</div> </div> </div> </body> Edit: Early days in OO coding, not asked in the right way
Well your "plain javascript" code is already taking you part way there. The first function you have defined identies the domain object: Promo. var Promo = function () { }; You have actions on an instance of promo, init, start, stop, resume, etc. These can be defined on the prototype of Promo. Promo.prototype.init = function() { // ... }; It could get a little annoying typing prototype each time, so we could bundle the prototype into a pointer that allows us a lot easier access... var Promo = function () { }; (function(obj) { obj.init = function() { // ... }; })(Promo.prototype); So we've got some structure but we need to now separate concerns. Throughout your plain javascript you've got config type data strewn through the code. It's generally a good idea to isolate these bits of data to a single entry point for your object. obj.init = function(_el) { // where _el is the base element of this widget }; I see you're also using jQuery which is good because it gives you a lot of power. One convention I like to use is instead of passing a huge amount of config data into a given widget, I like to give my objects minimal config and let them inspect the HTML to determine additional configuration data. This has the added advantage of if you wanted to add slides in the future or otherwise make changes to the slide content you need'nt worry about changing the JS. Let's say we were to alter the slide HTML to look like... <div id="promoSlides"> <div data-type="slide" data-slide-id="1">ps1</div> <div data-type="slide" data-slide-id="2">ps2</div> <div data-type="slide" data-slide-id="3">ps3</div> </div> Using jQuery we could identify how many slides are present. obj.init = function(_el) { this.baseElement = $(_el); this.slides = this.baseElement.find('*[data-type="slide"]'); }; Now we're passing in minimal config, we've separated out the identification of the slides to the HTML, and we've got a nice pattern for a self-sufficient object. The rest would be to fill in the details (totally untested, but something like this)... var Promo = function () { }; (function (obj) { obj.init = function(_el, _delay) { // Initialize markup this.baseElement = $(_el); this.slides = this.baseElement.find('*[data-type="slide"]'); this.slideDelay = _delay; // Sort slides // (not sure what's going on here) // Bind events this.baseElement .on('mouseenter', this.stop.bind(this)) .on('mouseleave', this.start.bind(this)); }; obj.start = function() { this.timer = setInterval(this.advance.bind(this), this.slideDelay); }; obj.stop = function() { clearInterval(this.timer); }; obj.advance = function() { // Slide the visible slide off screen // (note: the parent tag will need overflow:hidden) var visible = this.baseElement.find('*[data-type="slide"]:visible'); visible.animate({ left: '-' + (visible.width()) + 'px' }, 1000); // Slide the next slide in var next = visible.next(); next.css('left', this.baseElement.width() + 1).animate({ left: '0' }, 1000); }; })(Promo.prototype); Note that I made use of bind which isn't supported yet in older versions of IE.
Its not the converting to object oriented style what is needed for that code there. Here are issues i see there: pollution of global scope mixing fixed CSS rules with Javascript use of .length attribute within a loop no event delegation misplacement of <script> tag, resulting in use of window.onload creating new jQuery object when it is not needed use of CSS3 selectors in jQuery calls no clue how to use setTimeout() tight coupling to HTML ( id on each slide )
How to improve image cross-fade performance?
I want to be able to do a cross fade transition on large images whose width is set to 100% of the screen. I have a working example of what I want to accomplish. However, when I test it out on various browsers and various computers I don't get a buttery-smooth transition everywhere. See demo on jsFiddle: http://jsfiddle.net/vrD2C/ See on Amazon S3: http://imagefader.s3.amazonaws.com/index.htm I want to know how to improve the performance. Here's the function that actually does the image swap: function swapImage(oldImg, newImg) { newImg.css({ "display": "block", "z-index": 2, "opacity": 0 }) .removeClass("shadow") .animate({ "opacity": 1 }, 500, function () { if (oldImg) { oldImg.hide(); } newImg.addClass("shadow").css("z-index", 1); }); } Is using jQuery animate() to change the opacity a bad way to go?
You might want to look into CSS3 Transitions, as the browser might be able to optimize that better than Javascript directly setting the attributes in a loop. This seems to be a pretty good start for it: http://robertnyman.com/2010/04/27/using-css3-transitions-to-create-rich-effects/
I'm not sure if this will help optimize your performance as I am currently using IE9 on an amped up machine and even if I put the browser into IE7 or 8 document mode, the JavaScript doesn't falter with your current code. However, you might consider making the following optimizations to the code. Unclutter the contents of the main photo stage by placing all your photos in a hidden container you could give an id of "queue" or something similar, making the DOM do the work of storing and ordering the images you are not currently displaying for you. This will also leave the browser only working with two visible images at any given time, giving it less to consider as far as stacking context, positioning, and so on. Rewrite the code to use an event trigger and bind the fade-in handling to the event, calling the first image in the queue's event once the current transition is complete. I find this method is more well-behaved for cycling animation than some timeout-managed scripts. An example of how to do this follows: // Bind a custom event to each image called "transition" $("#queue img").bind("transition", function() { $(this) // Hide the image .hide() // Move it to the visible stage .appendTo("#photos") // Delay the upcoming animation by the desired value .delay(2500) // Slowly fade the image in .fadeIn("slow", function() { // Animation callback $(this) // Add a shadow class to this image .addClass("shadow") // Select the replaced image .siblings("img") // Remove its shadow class .removeClass("shadow") // Move it to the back of the image queue container .appendTo("#queue"); // Trigger the transition event on the next image in the queue $("#queue img:first").trigger("transition"); }); }).first().addClass("shadow").trigger("transition"); // Fire the initial event Try this working demo in your problem browsers and let me know if the performance is still poor.
I had the same problem too. I just preloaded my images and the transitions became smooth again.
The point is that IE is not W3C compliant, but +1 with ctcherry as using css is the most efficient way for smooth transitions. Then there are the javascript coded solutions, either using js straight (but need some efforts are needed to comply with W3C Vs browsers), or using libs like JQuery or Mootools. Here is a good javascript coded example (See demo online) compliant to your needs : var Fondu = function(classe_img){ this.classe_img = classe_img; this.courant = 0; this.coeff = 100; this.collection = this.getImages(); this.collection[0].style.zIndex = 100; this.total = this.collection.length - 1; this.encours = false; } Fondu.prototype.getImages = function(){ var tmp = []; if(document.getElementsByClassName){ tmp = document.getElementsByClassName(this.classe_img); } else{ var i=0; while(document.getElementsByTagName('*')[i]){ if(document.getElementsByTagName('*')[i].className.indexOf(this.classe_img) > -1){ tmp.push(document.getElementsByTagName('*')[i]); } i++; } } var j=tmp.length; while(j--){ if(tmp[j].filters){ tmp[j].style.width = tmp[j].style.width || tmp[j].offsetWidth+'px'; tmp[j].style.filter = 'alpha(opacity=100)'; tmp[j].opaque = tmp[j].filters[0]; this.coeff = 1; } else{ tmp[j].opaque = tmp[j].style; } } return tmp; } Fondu.prototype.change = function(sens){ if(this.encours){ return false; } var prevObj = this.collection[this.courant]; this.encours = true; if(sens){ this.courant++; if(this.courant>this.total){ this.courant = 0; } } else{ this.courant--; if(this.courant<0){ this.courant = this.total; } } var nextObj = this.collection[this.courant]; nextObj.style.zIndex = 50; var tmpOp = 100; var that = this; var timer = setInterval(function(){ if(tmpOp<0){ clearInterval(timer); timer = null; prevObj.opaque.opacity = 0; nextObj.style.zIndex = 100; prevObj.style.zIndex = 0; prevObj.opaque.opacity = 100 / that.coeff; that.encours = false; } else{ prevObj.opaque.opacity = tmpOp / that.coeff; tmpOp -= 5; } }, 25); }