Related
I'm working on a slide feature and it works great on all browsers except IE of course.
I firstly thought it's because of Math.sign but I've added a polyfill for it and it solve the error, but the slider still didn't work. Then I investigated a bit more and I think it's because of this syntax
_C.style.setProperty('--i', num);
which is used to set a value and use it in CSS like this:
transform: translate(calc(var(--i, 0)/var(--n)*var(--n)*-100%));
function move(e) {
document.getElementsByClassName('checked')[0].classList.remove('checked')
if(x0 || x0 === 0) {
if (!Math.sign) {
Math.sign = function(x) {
return ((x > 0) - (x < 0)) || +x;
}
}
let dx = unify(e).clientX - x0, s = Math.sign(dx);
if((i > 0 || s < 0) && (i < N - 1 || s > 0))
_C.style.setProperty('--i', i -= s);
let circles = document.getElementsByClassName('testimonial-circle')
let circle = circles[i]
circle.classList.add('checked')
x0 = null
}
};
The idea is to click on a bubble and slide to the specific "testimonial"
I am creating one page site and then trying to get offsetTop on window.scroll, by which i want to traverse the DOM according to the slide.
a lot of tries.. feeling dumb now..
if anyone can help, would be highly appreciable.
thanks
here is the code and fiddle URL:
$(window).scroll(function () {
var y = $(window).scrollTop(),
a = $('#first').offset().top - 200,
b = $('#second').offset().top - 200,
c = $('#third').offset().top - 200,
d = $('#fourth').offset().top - 200;
if (y > a) {
$('h1').html('This is First Slide');
}
if (y > b) {
$('h1').html('This is Second Slide');
}
if (y > c) {
$('h1').html('This is Third Slide');
}
if (y > d) {
$('h1').html('This is Third Slide');
}
else{
$('h1').html('No heading');
}
});
http://jsfiddle.net/A8Hmr/9/
Your logic is correct it's just a miss with the ifs.
I will show the code and explain:
var a = $('#first').offset().top - 200,
b = $('#second').offset().top - 200,
c = $('#third').offset().top - 200,
d = $('#fourth').offset().top - 200;
$(window).scroll(function () {
var y = $(window).scrollTop();
if (y > a && y < b) {
$('h1').text('This is First Slide');
}
else if (y > b && y < c) {
$('h1').text('This is Second Slide');
}
else if (y > c && y < d) {
$('h1').text('This is Third Slide');
}
else if (y > d) {
$('h1').text('This is Third Slide');
}
else{
$('h1').text('No heading');
}
});
Demo
1) You don't need to take the offset of the slides on every scroll, since they don't change, you can put them outside of the scroll event, that way it will improve the performance.
2) The problem in the code was the if. Since they were all ifs (and not if/else if) statements, all of theme were checked if they were true. Meaning that if the first one was true the next one will not be true and it will enter in the else statement automaticaly overwriting the if that was true.
So you have to make them if/else if and since once y > a become true it will always be true (untill it goes to y < a) you must have an additional condition if y < b meaning if y is less then the next slide. Ofcourse once again you can use only if/else but what is the point in checking 5 things if only one is correct ? Performance should be a main thing in every js code. ;)
Version 2:
(function(){
var a = $('#first').offset().top - 200,
b = $('#second').offset().top - 200,
c = $('#third').offset().top - 200,
d = $('#fourth').offset().top - 200,
h1 = $('h1'),
textChange = ['No heading','This is First Slide','This is Second Slide','This is Third Slide', 'This is Third Slide']
$(window).scroll(function () {
var y = $(window).scrollTop();
if (y > a && y < b && h1.text() != textChange[1]) {
h1.text(textChange[1]);
}
else if (y > b && y < c && h1.text() != textChange[2]) {
h1.text(textChange[2]);
}
else if (y > c && y < d && h1.text() != textChange[3]) {
h1.text(textChange[3]);
}
else if (y > d && h1.text() != textChange[4]) {
h1.text(textChange[4]);
}
else if(y <= a && h1.text() != textChange[0]){
h1.text(textChange[0]);
}
});
})();
Demo
What change here?
1) I wrapped the whole thing in self invoking anonymous function (since it's not a good practice to have global variables).
2) We made a variable outside the scroll event that will hold the h1 so we don't have to go in the dom on every scroll event.
3) We made an array that will hold the text that will change. (and updated the values in the text scroll)
4) We changed the if condition in the if statement to check if the text is already the same so we don't have to change it again. So now it will fire only once instead of firing every time we scroll.
5) We changed the else to else if since it would enter once the text is the same an jump to the else.
Pretty much that should increase the performance a lot.
I'm developing a HTML5 Canvas game using EaselJS and I've written a function that allows me to create "blocks" just by setting one or more images, size and position.
and by "blocks", what I mean is:
I'm doing this using two methods:
First method:
With this method the blocks are created in the available space inside the location I've set, using the images randomly.
Second method:
The blocks are created inside the location I've set using specific images for the top left corner, top side, top right corner, left side, center, right side, bottom left corner, bottom side and bottom right corner, and there can be more than a single image for each one of those parts (so the system uses a random one to avoid repeating the same image multiple times).
Ok, but what's the problem?
This function uses a zillion 77 lines (131 lines counting with the collision-detection-related part)! I know there's a better way of doing this, that will take about a half or less lines than it's taking now, but I don't know how to do it and when someone show me, I'll use the "right way" for the rest of my life. Can you help me?
What I want:
A possible way to use less lines is to use a single "method" that allows me to create blocks that are compound by blocks that are compound by the 9-or-more images (I just don't know how to do it, and I know it's difficult to understand. Try to imagine the third image being used 9 times). // This part of the question makes it on-topic!
Note that this question isn't subjective, since the goal here is to use less lines, and I'm not using the EaselJS tag because the question isn't EaselJS-specific, anyone with JavaScript knowledge can answer me.
Here's my incredibly big JavaScript function:
var Graphic = function (src, blockWidth, blockHeight) {
return {
createBlockAt: function (x, y, blockGroupWidth, blockGroupHeight, clsdir, alpha) {
for (var blockY = 0; blockY < blockGroupHeight / blockHeight; blockY++) {
for (var blockX = 0; blockX < blockGroupWidth / blockWidth; blockX++) {
var obj = new createjs.Bitmap(src[Math.floor(Math.random() * src.length)]);
obj.width = blockWidth;
obj.height = blockHeight;
if (typeof alpha !== 'undefined') {
obj.alpha = alpha; // While debugging this can be used to check if a block was made over another block.
}
obj.x = Math.round(x + (blockWidth * blockX));
obj.y = Math.round(y + (blockHeight * blockY));
stage.addChild(obj);
}
}
}
}
}
var complexBlock = function (topLeft, topCenter, topRight, middleLeft, middleCenter, middleRight, bottomLeft, bottomCenter, bottomRight, blockWidth, blockHeight) {
return {
createBlockAt: function (x, y, blockGroupWidth, blockGroupHeight, clsdir, alpha) {
for (var blockY = 0; blockY < blockGroupHeight / blockHeight; blockY++) {
for (var blockX = 0; blockX < blockGroupWidth / blockWidth; blockX++) {
if (blockY == 0 && blockX == 0) {
var obj = new createjs.Bitmap(topLeft[Math.floor(Math.random() * topLeft.length)]);
}
if (blockY == 0 && blockX != 0 && blockX != (blockGroupWidth / blockWidth - 1)) {
var obj = new createjs.Bitmap(topCenter[Math.floor(Math.random() * topCenter.length)]);
}
if (blockY == 0 && blockX == (blockGroupWidth / blockWidth - 1)) {
var obj = new createjs.Bitmap(topRight[Math.floor(Math.random() * topRight.length)]);
}
if (blockY != 0 && blockY != (blockGroupHeight / blockHeight - 1) && blockX == 0) {
var obj = new createjs.Bitmap(middleLeft[Math.floor(Math.random() * middleLeft.length)]);
}
if (blockY != 0 && blockY != (blockGroupHeight / blockHeight - 1) && blockX != 0 && blockX != (blockGroupWidth / blockWidth - 1)) {
var obj = new createjs.Bitmap(middleCenter[Math.floor(Math.random() * middleCenter.length)]);
}
if (blockY != 0 && blockY != (blockGroupHeight / blockHeight - 1) && blockX == (blockGroupWidth / blockWidth - 1)) {
var obj = new createjs.Bitmap(middleRight[Math.floor(Math.random() * middleRight.length)]);
}
if (blockY == (blockGroupHeight / blockHeight - 1) && blockX == 0) {
var obj = new createjs.Bitmap(bottomLeft[Math.floor(Math.random() * bottomLeft.length)]);
}
if (blockY == (blockGroupHeight / blockHeight - 1) && blockX != 0 && blockX != (blockGroupWidth / blockWidth - 1)) {
var obj = new createjs.Bitmap(bottomCenter[Math.floor(Math.random() * bottomCenter.length)]);
}
if (blockY == (blockGroupHeight / blockHeight - 1) && blockX == (blockGroupWidth / blockWidth - 1)) {
var obj = new createjs.Bitmap(bottomRight[Math.floor(Math.random() * bottomRight.length)]);
}
obj.width = blockWidth;
obj.height = blockHeight;
if (typeof alpha !== 'undefined') {
obj.alpha = alpha; // While debugging this can be used to check if a block was made over another block.
}
obj.x = Math.round(x + (blockWidth * blockX));
obj.y = Math.round(y + (blockHeight * blockY));
stage.addChild(obj);
}
}
}
}
}
var bigDirt = complexBlock(["http://i.imgur.com/DLwZMwJ.png"], ["http://i.imgur.com/UJn3Mtb.png"], ["http://i.imgur.com/AC2GFM2.png"], ["http://i.imgur.com/iH6wFj0.png"], ["http://i.imgur.com/wDSNzyc.png", "http://i.imgur.com/NUPhXaa.png"], ["http://i.imgur.com/b9vCjrO.png"], ["http://i.imgur.com/hNumqPG.png"], ["http://i.imgur.com/zXvJECc.png"], ["http://i.imgur.com/Whp7EuL.png"], 40, 40);
bigDirt.createBlockAt(0, 0, 40*3, 40*3);
Okay... Lots of code here, how do I test?
Here we go: JSFiddle
I don't see an easy way to reduce the number of lines given the nine possible branches, but you can substantially reduce the repetition in your code:
function randomImage(arr) {
return new createjs.Bitmap(arr[Math.floor(Math.random() * arr.length)]);
}
if (blockY == 0 && blockX == 0) {
var obj = randomImage(topLeft);
} // etc
Re: the nine possible branches, you should note that they are mutually exclusive, so should be using else if instead of just if, and that they are also naturally grouped in threes, suggesting that they should be nested.
EDIT in fact, there is a way to reduce the function size a lot. Note that for X and Y you have three options each (nine in total). It is possible to encode which image array you want based on a two-dimensional lookup table:
var blocksHigh = blockGroupHeight / blockHeight;
var blocksWide = blockGroupWidth / blockWidth;
var blockSelector = [
[topLeft, topCenter, topRight],
[middleLeft, middleCenter, middleRight],
[bottomLeft, bottomCenter, bottomRight]
];
for (var blockY = 0; blockY < blocksHigh; blockY++) {
var blockSY = (blockY == 0) ? 0 : blockY < (blocksHigh - 1) ? 1 : 2;
for (var blockX = 0; blockX < blocksWide; blockX++) {
var blockSX = (blockY == 0) ? 0 : blockY < (blocksWide - 1) ? 1 : 2;
var array = blockSelector[blockSY][blockSX];
var obj = randomImage(array);
...
}
}
Note the definitions of blocksHigh and blocksWide outside of the loop to reduce expensive repeated division operations.
See http://jsfiddle.net/alnitak/Kpj3E/
Ok, it's almost a year later now and I decided to come back here to improve the existing answers. Alnitak's suggestion on creating a "2-dimensional lookup table" was genius, but there's a even better way of doing what I was asking for.
Sprite Sheets
The problem core is the need for picking lots of separated images and merge them in order to create a bigger mosaic. To solve this, I've merged all images into a sprite sheet. Then, with EaselJS, I've separated each part of the platform (topLeft, topCenter, etc) in multiple animations, and alternative images of the same platform part that would be used randomly are inserted within it's default part animation, as an array (so topLeft can be five images that are used randomly).
This was achieved by making a class that creates an EaselJS container object, puts the sprite sheet inside this container, moves the sprite sheet to the correct position, caches the frame and updates the container cache using the "source-overlay" compositeOperation — which puts the current cache over the last one — then it does this again until the platform is finished.
My collision detection system is then applied to the container.
Here's the resulting JavaScript code:
createMosaic = function (oArgs) { // Required arguments: source: String, width: Int, height: Int, frameLabels: Object
oArgs.repeatX = oArgs.repeatX || 1;
oArgs.repeatY = oArgs.repeatY || 1;
this.self = new createjs.Container();
this.self.set({
x: oArgs.x || 0,
y: oArgs.y || 0,
width: ((oArgs.columnWidth || oArgs.width) * oArgs.repeatX) + oArgs.margin[1] + oArgs.margin[2],
height: ((oArgs.lineHeight || oArgs.height) * oArgs.repeatY) + oArgs.margin[0] + oArgs.margin[3],
weight: (oArgs.weight || 20) * (oArgs.repeatX * oArgs.repeatY)
}).set(oArgs.customProperties || {});
this.self.cache(
0, 0,
this.self.width, this.self.height
);
var _bmp = new createjs.Bitmap(oArgs.source);
_bmp.filters = oArgs.filters || [];
_bmp.cache(0, 0, _bmp.image.width, _bmp.image.height);
var spriteSheet = new createjs.SpriteSheet({
images: [_bmp.cacheCanvas],
frames: {width: oArgs.width, height: oArgs.height},
animations: oArgs.frameLabels
});
var sprite = new createjs.Sprite(spriteSheet);
this.self.addChild(sprite);
for (var hl = 0; hl < oArgs.repeatY; hl++) {
for (var vl = 0; vl < oArgs.repeatX; vl++) {
var _yid = (hl < 1) ? "top" : (hl < oArgs.repeatY - 1) ? "middle" : "bottom";
var _xid = (vl < 1) ? "Left" : (vl < oArgs.repeatX - 1) ? "Center" : "Right";
if(typeof oArgs.frameLabels[_yid + _xid] === "undefined"){
oArgs.frameLabels[_yid + _xid] = oArgs.frameLabels["topLeft"];
} // Case the expected frameLabel animation is missing, it will default to "topLeft"
sprite.gotoAndStop(_yid + _xid);
if (utils.getRandomArbitrary(0, 1) <= (oArgs.alternativeTileProbability || 0) && oArgs.frameLabels[_yid + _xid].length > 1) { // If there are multiple frames in the current frameLabels animation, this code choses a random one based on probability
var _randomPieceFrame = oArgs.frameLabels[_yid + _xid][utils.getRandomInt(1, oArgs.frameLabels[_yid + _xid].length - 1)];
sprite.gotoAndStop(_randomPieceFrame);
}
sprite.set({x: vl * (oArgs.columnWidth || oArgs.width), y: hl * (oArgs.lineHeight || oArgs.height)});
this.self.updateCache("source-overlay");
}
}
this.self.removeChild(sprite);
awake.container.addChild(this.self);
};
Usage:
createMosaic({
source: "path/to/spritesheet.png",
width: 20,
height: 20,
frameLabels: {
topLeft: 0, topCenter: 1, topRight: 3,
middleLeft: 4, middleCenter: [5, 6, 9, 10], middleRight: 7,
bottomLeft: 12, bottomCenter: 13, bottomRight: 15
},
x: 100,
y: 100,
repeatX: 30,
repeatY: 15,
alternativeTileProbability: 75 / 100
});
I would recommend using the "createMosaic" as a function returned by a constructor that passes the required arguments to it, so you'll not need to write the source image path, width, height and frameLabels every time you want to create a dirt platform, for example.
Also, this answer may have more LoC than the others that came before, but it's made this way in order to have more structure.
If you take a look at this website, you'll see that as you scroll and hit certain areas, a fade in animation plays, and brings the content to view. I've tried looking through the source to try to understand how they do this, but I haven't found any luck yet.
I'm guessing they use Javascript/jQuery to add a class when the DIV appears like so:
$('#element').addClass('animation');
But the question remains of how do they know when the DIV appears to call such Javascript?
It's in http://hockeyapp.net/javascripts/jquery.features.js
Here it is slightly prettier:
function f_scrollTop() {
return f_filterResults(
window.pageYOffset ? window.pageYOffset : 0,
document.documentElement ? document.documentElement.scrollTop : 0,
document.body ? document.body.scrollTop : 0
)
}
function f_filterResults(e, t, n) {
var r = e ? e : 0;
return t && (!r || r > t) && (r = t), n && (!r || r > n) ? n : r
}
$(document).ready(function() {
var e = navigator.userAgent.match(/(iPad|iPhone|iPod)/i) ? !0 : !1;
e ? ($("#crashes").css("opacity", 1),
$("#feedback").css("opacity", 1),
$("#distribution").css("opacity", 1),
$("#analytics").css("opacity", 1),
$("#customers").css("opacity", 1))
: ($(window).scroll(function() {
var e = $("body").height(),
t = f_scrollTop(),
n = 0;
t > 250 && (n = 1), $("#crashes").css("opacity", n)
}), $(window).scroll(function() {
var e = $("body").height(),
t = f_scrollTop(),
n = 0;
t > 2250 && (n = 1), $("#feedback").css("opacity", n)
}), $(window).scroll(function() {
var e = $("body").height(),
t = f_scrollTop(),
n = 0;
t > 3100 && (n = 1), $("#distribution").css("opacity", n)
}), $(window).scroll(function() {
var e = $("body").height(),
t = f_scrollTop(),
n = 0;
t > 4400 && (n = 1), $("#analytics").css("opacity", n)
}), $(window).scroll(function() {
var e = $("body").height(),
t = f_scrollTop(),
n = 0;
t > 3200 && (n = 1), $("#customers").css("opacity", n)
})), $(".switch-monthly").live("click", function(e) {
$(this).addClass("switch-yearly"), $(this).removeClass("switch-monthly"), $(".price.monthly").fadeOut(), $(".price.yearly").fadeIn(), $(".save").slideDown(), e.preventDefault()
}), $(".switch-yearly").live("click", function(e) {
$(this).removeClass("switch-yearly"), $(this).addClass("switch-monthly"), $(".price.monthly").fadeIn(), $(".price.yearly").fadeOut(), $(".save").slideUp(), e.preventDefault()
}), $(".fancybox").fancybox({openEffect: "elastic",closeEffect: "elastic"})
});
f_scrollTop and f_filterResults form a cross-browser way to find how far the page has been scrolled.
On document.ready, they bind five functions to $(window).scroll. Every time you scroll, it gets the distance using t = t_scrollTop(), and sets n to 1 or 0 depending on the value of t. Then sets the opacity of each of the divs (#crashes, #feedback, #distribution, #analytics, #customers) to n. (Better explanation below)
So, they don't know when the div appears - each time you scroll it checks whether or not it has and sets the opacity accordingly. Also, they don't use animate, but instead have a CSS transition set for four of the divs in http://hockeyapp.net/stylesheets/public.css (don't try reading that):
#distribution,
#crashes,
#feedback,
#analytics {
opacity: 0;
-webkit-transition:opacity .5s ease-in-out 0s
}
On lines like this:
t > 250 && (n = 1), $("#crashes").css("opacity", n)
The comma operator says "evaluate each expression and the value of the whole expression is the value of the last." Here it's probably just used for brevity, since the source has been minified.
Since n is already 0 and && short circuits, if t > 250 then it will evaluate (n = 1), otherwise it will leave n as 0. Then it sets the opacity to n.
Not sure how they are doing it specifically but you may want to take a look at jQuery waypoints which would accomplish what you're trying to do: http://imakewebthings.com/jquery-waypoints/
Having a look at their source I think they are using something call anchorScroll which is here. http://www.binpress.com/app/anchorscroll/228
You can track to see how far down the page the user has scrolled with a little bit of jQuery like this:
$(window).scroll(function(e){
if($(this).scrollTop() > 150) //the 150 here is the height in pixels
{
$('#element').addClass('animation');
}
});
In this code, the height in pixels is where you would specify how far down the page you would like for the animation to occur. You may have to play with the heights a bit to get it exactly how you want it.
You can use the scroll api:
$(window).scroll(function (event) {
var top = $(window).scrollTop();
if (top > 200){ // assuming the position of your div.
$('#element').addClass('animation');
}
});
for (m = 0; m < troopsCount.length; m++) {
//FM_log(7,"i="+i+" m="+m);
//FM_log(7,"tipoTropaPrioritaria[m] = "+tipoTropaPrioritaria[m]);
//FM_log(7,"troopsCount[m] = "+troopsCount[m]);
//FM_log(7,"availableTroops[m] = "+availableTroops[m]);
if ((tipoTropaPrioritaria[m] == null || tipoTropaPrioritaria[m] == "undefined")
|| (troopsCount[m] == null || troopsCount[m] == "undefined") ||
(availableTroops[m] == null || availableTroops[m] == "undefined"))
return "alternaTropas(): ERRO - tipoTropaPrioritaria[m] || troopsCount[m] || availableTroops[m] null ou undefined";
if ((parseInt(tipoTropaPrioritaria[m]) != 0) && (parseInt(troopsCount[m]) != 0)) {
naoServe = true;
break;
}
else {
if ((parseInt(availableTroops[m])) < (parseInt(troopsCount[m]))) {
naoServe = true;
break;
}
else if (m < troopsCount.length) {
naoServe = true;
}
else { //means m >= troopsCount.length
naoServe = false;
}
}
}
my question is: the last statement
else { //means m >= troopsCount.length
naoServe = false;
}
will it ever be evaluated since
for (m = 0; m < troopsCount.length; m++)
???
No, it won't be executed, assuming that m and troopsCount aren't modified in the loop itself (which in this example they don't seem to be).
As I believe you're pointing out, the loop's conditional would prevent the loop from running again if m were greater than or equal to troopsCount.length at the start of the loop.
Nope. It should never happen.
The loop stops immediately once m < troopsCount.length is false. As such, m >= troopsCount.length will never be true inside the loop, unless you change its value inside the loop itself (which you don't, in this sample).
No. The loop is only evaluated as long as m < troopsCount.length. So m will never be >= troopsCount.length as long as you don't modify m or troopsCount.length inside the loop.
let´s assume troopsCount.length = 10
when m = 9 it will execute all the code in the loop right, but when m = 10 it won´t execute anything.
so if I change it this way:
else {
if ((parseInt(availableTroops[m])) < (parseInt(troopsCount[m]))) {
naoServe = true;
break;
}
else if (m < (troopsCount.length - 1)) { // troopsCount.length - 1 = 9, m < 9 = m from 0 to 8
naoServe = true;
}
else { // troopsCount.length = 9
naoServe = false;
}
}
}
it would work, right?