Errors with greensock tween activated by mouse in pixi.js - javascript

I'm trying to scroll a greensock tween in pixi. I'm getting errors trying to hook the code that gets the mouse/arrow input (trackpad.value) with my tween.
Here's my working greensock test tween, to make sure I have greensock working in pixi: (have to tween the position element in pixi):
var t1 = new TimelineMax({onUpdate:animate, onUpdateScope:stage});
t1.to(bg.position, 3, {y:100});
Here's my code where I'm trying to hook trackpad.value into the greensock code (I'm getting the following error: Uncaught TypeError: bg.position is not a function):
trackpad = new Trackpad(document);
var t1 = new TimelineMax({paused:true, onUpdate:animate, onUpdateScope:stage});
t1.progress(bg.position( Math.abs( trackpad.value ) / 3240));
I then tried the following - it didn't work (but I didn't get an error):
var moveIt = trackpad.value / 3240;
t1.progress(bg.position, moveIt, {});
Here's the code where the trackpad value is defined:
/*
* param: the html element that will be scrolled
*/
Trackpad = function(target)
{
this.target = target;
this.value = 0;
this.easingValue = 00;
this.dragOffset = 0;
this.dragging;
this.speed= 0;
this.prevPosition = 0;
$(this.target).mousedown($.proxy(this.onMouseDown, this));
this.target.onmousewheel = $.proxy(this.onMouseWheel, this);
// not forgetting touchs!
this.target.ontouchstart = $.proxy(this.onTouchStart, this);
// stop dragging!
$(document).keydown( $.proxy(this.onArrow, this))//function(e){
//this.target.ondragstart = function(){return false;}
}
// set constructor
Trackpad.constructor = Trackpad;
// create the functions
Trackpad.prototype.unlock = function()
{
this.locked = false;
this.speed = 0;
this.easingValue = this.value;
}
Trackpad.prototype.lock = function()
{
this.locked = true;
}
Trackpad.prototype.update = function()
{
if(this.easingValue > 0)this.easingValue = 0;
if(this.easingValue < -10700)this.easingValue = -10700;
this.value = this.easingValue;
if(this.dragging)
{
var newSpeed = this.easingValue - this.prevPosition;
newSpeed *= 0.7;
this.speed += (newSpeed - this.speed) *0.5;//+= (newSpeed - this.speed) * 0.5;
this.prevPosition = this.easingValue;
}
else
{
this.speed *= 0.9;
this.easingValue += this.speed;
if(Math.abs(this.speed) < 1)this.speed = 0;
}
}
Trackpad.prototype.onArrow = function(event)
{
if (event.keyCode == 38) {
// UP
this.speed = 4;
return false;
}
else if (event.keyCode == 40) {
// UP
this.speed -= 4
return false;
}
}
Trackpad.prototype.onMouseWheel = function(event)
{
event.preventDefault();
this.speed = event.wheelDelta * 0.1;
}
Trackpad.prototype.startDrag = function(newPosition)
{
if(this.locked)return;
this.dragging = true;
this.dragOffset = newPosition - this.value;
}
Trackpad.prototype.endDrag = function(newPosition)
{
if(this.locked)return;
this.dragging = false;
}
Trackpad.prototype.updateDrag = function(newPosition)
{
if(this.locked)return;
this.easingValue = (newPosition - this.dragOffset);
}
/*
* MOUSE
*/
Trackpad.prototype.onMouseDown = function(event)
{
if(event)event.preventDefault();
event.returnValue = false;
$(document).mousemove($.proxy(this.onMouseMove, this));
$(document).mouseup($.proxy(this.onMouseUp, this));
this.startDrag(event.pageY);
}
Trackpad.prototype.onMouseMove = function(event)
{
if(event)event.preventDefault();
this.updateDrag(event.pageY);
}
Trackpad.prototype.onMouseUp = function(event)
{
//$(this.target).mousemove(null);
$(document).unbind('mousemove');
$(document).unbind('mouseup');
//this.target.onmousemove = null;
this.endDrag();// = false;
}
/*
* TOUCH!
*/
Trackpad.prototype.onTouchStart = function(event)
{
//event.preventDefault();
this.target.ontouchmove = $.proxy(this.onTouchMove, this);
this.target.ontouchend = $.proxy(this.onTouchEnd, this);
this.startDrag(event.touches[0].clientY);
}
Trackpad.prototype.onTouchMove = function(event)
{
event.preventDefault();
this.updateDrag(event.touches[0].clientY);
}
Trackpad.prototype.onTouchEnd = function(event)
{
this.target.ontouchmove = null;
this.target.ontouchend = null;
this.endDrag();
}
** edit
tl = new TimelineLite( { paused: true } );
// respond to scroll event - in this case using jquery
$(window).scroll();
//apply whatever math makes the most sense to progress the timeline progress from 0 to 1 within those parameters. Something like,
$(window).scroll( function() {
var st = $(this).scrollTop();
if ( st < someArbitraryValue ) { // someArbitraryValue, where to start
// Here, "someOtherArbitaryValue" would be the
// "height" of the scroll to react to
tl.progress( Math.abs( st ) / someOtherArbitaryValue );
}
});

Is this the kind of effect you were after?
JavaScript:
window.requestAnimFrame=(function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||function(callback){window.setTimeout(callback,1000/60);};})(); //http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
var stageWidth=$(window).innerWidth();
var stageHeight=$(window).innerHeight();
var renderer=PIXI.autoDetectRenderer(stageWidth,stageHeight);
var bg,cat,moon,blue,trackpad,texture1,texture2,texture3;
document.body.appendChild(renderer.view);
texture1=PIXI.Texture.fromImage('https://dl.dropboxusercontent.com/u/45891870/Experiments/StackOverflow/1.5/cat.jpg');
texture2=PIXI.Texture.fromImage('https://dl.dropboxusercontent.com/u/45891870/Experiments/StackOverflow/1.5/moon.jpg');
texture3=PIXI.Texture.fromImage('https://dl.dropboxusercontent.com/u/45891870/Experiments/StackOverflow/1.5/blue.jpg');
bg=new PIXI.Container();
cat=new PIXI.Sprite(texture1);
moon=new PIXI.Sprite(texture2);
blue=new PIXI.Sprite(texture3);
cat.anchor.x=cat.anchor.y=moon.anchor.x=moon.anchor.y=blue.anchor.x=blue.anchor.y=0;
cat.position.x=cat.position.y=moon.position.x=blue.position.x=bg.position.x=bg.position.y=0;
cat.width=moon.width=blue.width=stageWidth;
moon.position.y=1080;
blue.position.y=2160;
bg.addChild(cat);
bg.addChild(blue);
bg.addChild(moon);
bg.vy=bg.vx=0;//what are those?
trackpad=new Trackpad(document);
requestAnimFrame(animate);
function animate(){
requestAnimFrame(animate);
bg.position.y=trackpad.value;
trackpad.update();
renderer.render(bg);
}
Let me know if this is exactly the thing you were looking for & I'll then break it down for you in terms of what has changed in comparison to your code.
Notes:
First & foremost, I have used the latest version (v3.0.6) of Pixi.JS in my example above. This v3 update brought a few major changes. Couple of them prominent to your problem are:
No need for Stage object anymore for rendering purposes. Any Container type object can be used directly to be rendered on canvas.
Shortening of the name DisplayObjectContainer to simply Container. This is probably the reason why you are getting the error when trying to implement my code in your environment that you mentioned in comments because I presume you are using one of the old verions.
Read all about this update here, here & here.
I always prefer to use the latest & greatest of GSAP (v1.17.0‏). Even the dot releases of this framework brings major updates which is why I like to keep it up to date. Read an important note on this update here. Having said that, the current implementation doesn't really use TweenMax at all.
TweenMax bundles EasePack, CSSPlugin & a few other things. No need to load them in separately. Update your HTML accordingly. Use this handy GSAP CheatSheet by Peter Tichy to get such information and more about this tool.
Changes in Trackpad.js:
Inside the update method, there was a maximum scroll limit defined the page can scroll up to. That value previously was -10700. I changed it to -2160. You may want to set it to -3240 I think, based on what I have been able to understand so far as to what you are trying to achieve.
Formatting changes.
Changes in main.js (whatever name you gave to your main script file):
Added a requestAnimationFrame polyfill thanks to Paul Irish.
Removed the var stage= new PIXI.Stage(0xff00ff); line. Read #1 above for details.
Renamed DisplayObjectContainer to Container which was assigned to bg. Read #1 above for details.
Added bg.position.y=trackpad.value; in the animate loop. You were missing this. You will need to use trackpad.value in order to position your bg.
Added trackpad.update(); in the same animate loop. This is the big one and IMHO, this is the one you were failing to understand the purpose of. In summary, Trackpad.js needs to update its value on a timely basis & the only loop you have got running is the animate loop thanks to requestAnimFrame. Hence, the update(); method is called.
Rendering bg instead of stage. Read #1 above for details.
Formatting changes.
Let me know if anything is unclear.
T

I thought of editing the old answer but decided against it because I think it answers your original question.
Take a look at this Codepen demo for a new approach to the same problem. I am really hoping to listen to community on the approach I have taken here in terms of listening to events and using them to adjust a GSAP timeline.
There are 4 JS files used in my example: app.js, constants.js, timeline.js & listeners.js. Links to which can be found in the settings gear icon of the JavaScript editor of the demo. All of these files are heavily annotated with links to solutions I found over the internet to specific problems.
Among these files, code of app.js is as follows:
JavaScript:
function Application(){}
Application.prototype.init=function(){
this.constants=Constants.getInstance();
this.BASE_URL=this.constants.BASE_URL;
this.IMAGE_JS_URL=this.constants.IMAGE_JS_URL;
this.IMAGE_PIXI_URL=this.constants.IMAGE_PIXI_URL;
this.IMAGE_GSAP_URL=this.constants.IMAGE_GSAP_URL;
this.createPolyfillForBind();
this.setupRenderer();
this.loadImages();
};
Application.prototype.setupRenderer=function(){
this.stageWidth=window.innerWidth;
this.stageHeight=window.innerHeight;
//this.renderer=PIXI.autoDetectRenderer(this.stageWidth,this.stageHeight);
this.renderer=new PIXI.CanvasRenderer(this.stageWidth,this.stageHeight);
document.body.appendChild(this.renderer.view);
};
Application.prototype.loadImages=function(){
var self=this;
this.loader=new PIXI.loaders.Loader(this.BASE_URL,1,{crossOrigin:''}); // PIXI Loader class [http://pixijs.github.io/docs/PIXI.loaders.Loader.html]
this.loader.add(this.IMAGE_JS_URL); // Loader extends ResourceLoader [http://adireddy.github.io/docs/haxe-pixi/v3/types/pixi/plugins/resourceloader/ResourceLoader.html]
this.loader.add(this.IMAGE_PIXI_URL);
this.loader.add(this.IMAGE_GSAP_URL);
//this.loader.once('complete',function(){self.onImagesLoaded.apply(self);}); // Vanilla JS alternative to jQuery's proxy() method [http://stackoverflow.com/a/4986536]
this.loader.once('complete',this.onImagesLoaded.bind(this)); // bind() polyfill [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Polyfill]
this.loader.load();
};
Application.prototype.onImagesLoaded=function(){
this.setupSprites();
this.initTimeline();
this.initListeners();
this.startTicker();
};
Application.prototype.setupSprites=function(){
this.containerBg=new PIXI.Container();
this.spriteJS=new PIXI.Sprite(PIXI.utils.TextureCache[this.BASE_URL+this.IMAGE_JS_URL]); // TextureCache in action [http://www.html5gamedevs.com/topic/7674-load-textures-synchronously/?p=45836]
this.spritePIXI=new PIXI.Sprite(PIXI.utils.TextureCache[this.BASE_URL+this.IMAGE_PIXI_URL]); // PIXI.TextureCache became PIXI.utils.TextureCache in v3 [http://www.html5gamedevs.com/topic/14144-v3-utilstexturecache-utils-is-not-defined/?p=80524]
this.spriteGSAP=new PIXI.Sprite(PIXI.utils.TextureCache[this.BASE_URL+this.IMAGE_GSAP_URL]);
this.containerBg.addChild(this.spriteJS);
this.containerBg.addChild(this.spritePIXI);
this.containerBg.addChild(this.spriteGSAP);
this.spriteJS.anchor.x=this.spriteJS.anchor.y=this.spritePIXI.anchor.x=this.spritePIXI.anchor.y=this.spriteGSAP.anchor.x=this.spriteGSAP.anchor.y=0;
this.spriteJS.position.x=this.spriteJS.position.y=this.spritePIXI.position.x=this.spriteGSAP.position.x=this.containerBg.position.x=this.containerBg.position.y=0;
this.scaleImage(this.spriteJS);
this.scaleImage(this.spritePIXI);
this.scaleImage(this.spriteGSAP);
this.spritePIXI.alpha=this.spriteGSAP.alpha=0;
this.spriteJS.position.y=this.constants.GUTTER;
this.spritePIXI.position.y=this.spriteJS.height*2+this.constants.GUTTER;
this.spriteGSAP.position.y=this.spriteJS.height+this.spritePIXI.height*2+this.constants.GUTTER;
};
Application.prototype.scaleImage=function(sprite){
//var scale=Math.min(this.stageWidth/sprite.width,this.stageHeight/sprite.height); // resize with aspect ratio [http://community.createjs.com/discussions/createjs/547-resizing-canvas-and-its-content-proportionally-cross-platform#comment_27266530] and [https://opensourcehacker.com/2011/12/01/calculate-aspect-ratio-conserving-resize-for-images-in-javascript/]
var scale=this.stageWidth/sprite.width;
sprite.scale.x=sprite.scale.y=scale;
};
Application.prototype.initTimeline=function(){
this.timeline=new Timeline();
this.timeline.init(this.containerBg,this.spriteJS,this.spritePIXI,this.spriteGSAP,this.stageWidth,this.stageHeight);
};
Application.prototype.initListeners=function(){
var self=this;
//this.listeners=new Listeners();
//this.constants.setListenersObject(this.listeners);
//this.listeners.init();
this.listeners=Listeners.getInstance();
this.listeners.addListeners();
document.addEventListener(this.constants.SCROLLED,this.onScroll.bind(this),false);
document.addEventListener(this.constants.STARTED_DRAG,this.onStartDrag.bind(this),false);
document.addEventListener(this.constants.DRAGGED,this.onDrag.bind(this),false);
document.addEventListener(this.constants.END_DRAG,this.onEndDrag.bind(this),false);
};
Application.prototype.onScroll=function(e){ this.timeline.onScroll(e); };
Application.prototype.onStartDrag=function(e){ this.timeline.onStartDrag(e); };
Application.prototype.onDrag=function(e){ this.timeline.onDrag(e); };
Application.prototype.onEndDrag=function(e){ this.timeline.onEndDrag(e); };
Application.prototype.startTicker=function(){
var self=this;
//TweenLite.ticker.addEventListener('tick',function(){self.render.apply(self);},false); // Vanilla JS alternative to jQuery's proxy() method [http://stackoverflow.com/a/4986536]
TweenLite.ticker.addEventListener('tick',this.render.bind(this),false);
};
Application.prototype.render=function(){this.renderer.render(this.containerBg);};
Application.prototype.createPolyfillForBind=function(){ // [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Polyfill]
if(!Function.prototype.bind){
Function.prototype.bind=function(oThis){
if(typeof this!=='function'){
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var aArgs=Array.prototype.slice.call(arguments,1),
fToBind=this,
fNOP=function(){},
fBound=function(){
return fToBind.apply(this instanceof fNOP
?this
:oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype=this.prototype;
fBound.prototype=new fNOP();
return fBound;
};
}
};
//
var app=new Application();
app.init();
P.S. I have also heavily experimented with design patterns in this same example, mainly Prototype and Singleton patterns. I am also looking forward to comments on them as well from the community.
T

Related

How to convert javascript code for Angular

I'm trying to implement something like the following into an Angular project: https://codepen.io/vincentorback/pen/NGXjda
The code compiles just fine in VS code, but when I try and preview in the browser, I get the following two errors:
Uncaught (in promise): TypeError undefined is not an object (evaluating 'this.context.addEventListener')
TypeError undefined is not an object (evaluating 'this.getScrollPos')
Stackblitz: https://stackblitz.com/edit/ionic-rv4ju7
home.page.ts
export class HomePage implements OnInit {
context = document.getElementsByClassName('loop')[0];
startElement = document.getElementsByClassName('is-start')[0];
clones = document.getElementsByClassName('is-clone');
disableScroll = false;
scrollWidth;
scrollPos;
clonesWidth;
i;
constructor() {
window.requestAnimationFrame(this.reCalc);
this.context.addEventListener('scroll', function () {
window.requestAnimationFrame(this.scrollUpdate);
}, false);
window.addEventListener('resize', function () {
window.requestAnimationFrame(this.reCalc);
}, false);
}
getScrollPos() {
return (this.context.pageXOffset || this.context.scrollLeft) - (this.context.clientLeft || 0);
}
setScrollPos(pos) {
this.context.scrollLeft = pos;
}
getClonesWidth() {
this.clonesWidth = 0;
this.i = 0;
for (this.i; this.i < this.clones.length; this.i += 1) {
this.clonesWidth = this.clonesWidth + this.clones[this.i].clientWidth;
}
return this.clonesWidth;
}
reCalc() {
this.scrollPos = this.getScrollPos();
this.scrollWidth = this.context.scrollWidth;
this.clonesWidth = this.getClonesWidth();
if (this.scrollPos <= 0) {
this.setScrollPos(1);
}
}
scrollUpdate() {
if (this.disableScroll === false) {
this.scrollPos = this.getScrollPos();
if (this.clonesWidth + this.scrollPos >= this.scrollWidth) {
// Scroll to the left when you’ve reached the far right
this.setScrollPos(1); // Scroll 1 pixel to allow scrolling backwards.
this.disableScroll = true;
} else if (this.scrollPos <= 0) {
// Scroll to the right when you reach the far left.
this.setScrollPos(this.scrollWidth - this.clonesWidth);
this.disableScroll = true;
}
if (this.disableScroll) {
// Disable scroll-jumping for a short time to avoid flickering.
window.setTimeout(function () {
this.disableScroll = false;
}, 40);
}
}
}
}
You need to move your code from the constructor to AfterViewInit, where DOM elements are available. As a further recommendation, I would recommend that keep what you can out of the constructor. Constructor is mainly used for initializing variables, not doing any logic.
Furthermore, you have issues with this, this doesn't point to what you think it does. Take a read: How to access the correct `this` inside a callback? Very useful reading! So I would recommend to use arrow functions instead of function to keep the context of this.
So change things like:
this.context.addEventListener('scroll', function () {
to:
this.context.addEventListener('scroll', () => {
Here's a fork of your StackBlitz
PS: where you can, make use of Angular tools instead of accessing the DOM like .getElementById. Just as a future hint. Many times angular has own set of tools, and accessing and manipulating the DOM from the component should be a last resort.

Cannot call a function in Javascript

Hello I'm experimenting with Box2dWeb, and working with top-down car game.
My problem arises when I try to control the car, so it will move, at first only forwards. For simplicity I don't want to use wheels, and just apply the force to the car (a box).
For the controls I made a function for but for a reason it's not getting called... That's where I need a pointer or advice. (Creation and placement of objects works just fine)
Here's part of the code:
var GlobalVar={ }
var KEY = {
UP: 87,//W
DOWN: 83,//s
LEFT: 65,//A
RIGHT: 68//D
}
GlobalVar.pressedKeys = [];//an array to remember which key is pressed or not
$(function(){
$(document).keydown(function(e){
GlobalVar.pressedKeys[e.keyCode] = true;
});
$(document).keyup(function(e){
GlobalVar.pressedKeys[e.keyCode] = false;
});
Rendering();
PlaceStuff(GlobalVar.currentLevel);//placing stuff, like car and boundaries/walls
moveCar();
});
function moveCar(){
if (GlobalVar.pressedKeys[KEY.UP]){
var force = new b2Vec2(0, -10000000);
GlobalVar.car.ApplyForce(force, GlobalVar.car.GetWorldCenter());
}
}
It doesn't look like the moveCar function is being called more than once.
You should do the following:
function moveCar(){
if (GlobalVar.pressedKeys[KEY.UP]){
var force = new b2Vec2(0, -10000000);
GlobalVar.car.ApplyForce(force, GlobalVar.car.GetWorldCenter());
}
requestAnimationFrame(moveCar);
}
You may also want to add a modifier to modify the amount of force added depending on the frame rate:
then = Date.now();
function moveCar(){
var now = Date.now();
var modifier = now - then; // Make modifier the time in milliseconds it took since moveCar was last executed.
then = now;
if (GlobalVar.pressedKeys[KEY.UP]){
var force = new b2Vec2(0, -10000000);
GlobalVar.car.ApplyForce(force * modifier, GlobalVar.car.GetWorldCenter());
}
requestAnimationFrame(moveCar);
}
This will ensure the car doesn't move slower on slower systems.
If you also want the Rendering() function to be executed more than once, you may also want to create another function which gets called as often as possible and calls the other two functions.
then = Date.now();
function moveCar(modifier){
if (GlobalVar.pressedKeys[KEY.UP]){
var force = new b2Vec2(0, -10000000);
GlobalVar.car.ApplyForce(force * modifier, GlobalVar.car.GetWorldCenter());
}
}
function update() {
var now = Date.now();
var modifier = now - then; // Make modifier the time in milliseconds it took since moveCar was last executed.
then = now;
moveCar(modifier);
Rendering();
requestAnimationFrame(update);
}
As pointed out in the comments, you only call moveCall once, but you probably want to do it after each key press:
$(document).on('keydown keyup', function(e){
GlobalVar.pressedKeys[e.keyCode] = true;
moveCar();
});

Is $(window).scrollTop() the correct statement?

I'm trying to build a function like this one:
var t =$('#top');
var q1=$('#fe1');
var q2=$('#fe2');
var q3=$('#fe3');
var q4=$('#fe4');
var q5=$('#fe5');
var win = $(window);
var doc=$(document);
var wins = win.scrollTop();
var docs = doc.scrollTop();
function next (){
if (wins == docs) {
q1.ScrollTo();
}
else if (wins == q1.scrollTop()) {
q2.ScrollTo();
}
else if (wins == q2.scrollTop()) {
q3.ScrollTo();
}
else if (wins == q3.scrollTop()) {
q4.ScrollTo();
}
else if (wins == q4.scrollTop()) {
q5.ScrollTo();
}
}
I want to go scrolling to the next section. To do so, the code checks in which section I am so it knows which section to scroll to. But I think $(window).scrollTop() is not what I am looking for.
I want a statement that returns the distance between the top of the page and the top of what I am displaying. Maybe i have to do a more complex operation. Do you know how can i get this?
Thanks.
This problem is quite simple to solve even without jQuery or similar in plain JavaScript. Here it is for your example:
var next = (function (sections) {
function getTop(node) {
return node ? node.offsetTop + getTop(node.offsetParent) : 0;
}
return function () {
var i, nodeTop, top = window.pageYOffset;
for (i = 0; i < sections.length; i += 1) {
nodeTop = getTop(document.getElementById(sections[i]));
if (nodeTop > top) {
window.scrollTo(window.pageXOffset, nodeTop);
return;
}
}
};
}(['top', 'fe1', 'fe2', 'fe3', 'fe4', 'fe5']));
The code is quite general, so you can pass any section ids you want (they just need to appear in the correct order).
We use two standard DOM properties/functions here window.pageYOffset and window.scrollTo() to get and set vertical offset of the window (window.pageXOffset is used to keep horizontal offset the same). To get the vertical offset of the section start I defined getTop function using simple recursion (jQuery uses similar code IMHO).
Resulting function next() is defined in a self-invoking closure to hide the implementation and helper function. To use it after this code is run, you simply call
next();
I tested this code, so I am quite confident it works :).

Incorporate window.onresize into OO JS class

I'm just trying to structure my Javascript better and wondering how to incorporate window.onresize into the returned object, like so:
var baseline = function(){
var tall, newHeight, target, imgl, cur, images = [];
return {
init: function(selector, target){
this.images = document.querySelectorAll(selector);
this.target = target;
this.setbase(this.images);
window.onresize = this.setbase(this.images);
},
setbase: function(imgs){
this.imgl = imgs.length;
if(this.imgl !== 0){
while(this.imgl--){
this.cur = imgs[this.imgl];
this.cur.removeAttribute("style");
this.tall = this.cur.offsetHeight;
this.newHeight = Math.floor(this.tall / this.target) * this.target;
this.cur.style.maxHeight = this.newHeight + 'px';
}
} else {
return false;
}
}
}
}();
Is this the way that people would do it, is this going to work? Thanks
EDIT:
Invoked like so:
window.onload = function(){
baseline.init('img', '24');
};
I would like it so that when the window is resized, baseline.init is called with the same params as the initial init function call...
Here's the main error
init: function(selector, target){
this.images = document.querySelectorAll(selector);
this.target = target;
this.setbase(this.images);
// This line says call setbase now and assign the result of that
// as the onresize handler
window.onresize = this.setbase(this.images);
},
Your this.images does not point to the var images = [] you've created. This is for when you're using protoype style objects. You should just use images in your functions.
Some of your variables look like they're only used in setBase, they should be local
Looking at your object, it's very hard to tell what it's supposed to do, sounds like you're wrapping code in an object just for the sake of wrapping it into an object. What does baseline mean?
Here's a better version of your code, you should read and understand http://www.joezimjs.com/javascript/javascript-closures-and-the-module-pattern/ and http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html so you can decide what pattern you want to use and how they actually work. You are mixing both patterns, even though you didn't intend to. The trick is that with the way you're writing it (module pattern) there's no need to use this in the code, they're actually local variables held be the module
var baseline = function(){
// Don't use "this.tall", just "tall" gets you the variable
// Class variables, are you sure you need them throughout the class
var tall, newHeight, target, imgl, cur, images = [];
// Different name for the parameter so it doesn't get confused with
// the class variables
function init(selector, pTarget) {
images = document.querySelectorAll(selector);
target = pTarget;
setBase();
// Since we're not using this, you
// can just reference the function itself
window.onresize = setBase
}
// Most JS developers name methods using camelCase
function setBase() {
imgl = imgs.length;
if(imgl !== 0){
while(imgl--){
cur = imgs[imgl];
cur.removeAttribute("style");
tall = cur.offsetHeight;
newHeight = Math.floor(tall / target) * target;
cur.style.maxHeight = newHeight + 'px';
}
// should you return true here? what does returning
// something even mean here?
} else {
return false;
}
}
// Return just the public interface
return {
init: init
setBase: setBase
};
}();

Showing elements one after another

I often see questions like this one and there are multiple solutions. I'm trying to come up with something short that can be reusable. My question is, given the following code, do I need to clearTimeout() and where to do it? And also, anything you would improve? How good or bad is this piece of code for performance?
http://jsfiddle.net/elclanrs/fQX8M/15/
var fade1by1 = function ($elms, props) {
props = props || {};
props.delay = props.delay || 1; // s
props.speed = props.speed || 400; // ms
props.ease = props.ease || 'linear';
for (var i=0, d=0, l=$elms.length; i<l; i++, d+=props.delay*1000) {
(function (i, d) {
// Using `delay()` instead of `setTimeout()`
// as Alexander suggested
$elms.eq(i).delay(d).fadeIn(props.speed, props.ease);
})(i, d);
}
};
I don't think you need to window.clearTimeout since it does not seem like you want to stop the animation. If you are still undecided then what about using .delay, it clearly uses window.setTimeout also.
var fade1by1 = function ($elms, speed) {
speed = speed || 1; // Seconds
for (var i=0, s=0, l=$elms.length; i<l; i++, s+=speed*1000) {
$elms.eq(i).delay(s).fadeIn('slow');
}
};
See it in action here.
Seems to me you should do this with the fadeIn function's callback. Something like the following should accomplish all of your goals (substitute in your new params):
var customFade = function(parent, speed){
$(':hidden:first', parent).fadeIn(speed, function(){
customFade(parent, speed)
});
}
$('button').click(function(){ customFade($('ul'), 1000); });
Instead of setting all the actions at once with increasingly long wait periods, another approach is to bind the show behavior directly to each selected element as a custom event, and include in that bound function a fixed waiting period plus a call to trigger the custom event of the "next" element, if present. To get it started, you just light the fuse of the first element.
So, something like this:
var fadeCascade = function fadeCascade(your_selector, props) {
props = props || {};
props.delay = props.delay || 1; // s
props.speed = props.speed || 2000; // ms
props.ease = props.ease || 'linear';
$(your_selector)
.addClass('showme') // Being a little lazy here, but it works
// You could work out an inspection by attached event
.bind('showme', function() { // custom event
$(this)
.delay( props.delay * 1000 )
.fadeIn(props.speed, props.ease, function() {
$(this).nextAll('.showme:first').trigger('showme'); // jqueryish recursion
});
}).hide() // or just hide in initial css
.first().trigger('showme'); // set the dominoes falling
};
And to trigger the reveal:
fadeCascade('div.bar');​
If you're concerned about hygiene, you could unbind events and remove classes as you go.
Example: http://jsfiddle.net/redler/EKx6s/1/
Update: Added delay, thanks #Alexander.

Categories