0

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 );
     }
 });
Agent Zebra
  • 4,410
  • 6
  • 33
  • 66
  • 1
    hello again @AgentZebra! can you post a fiddle please? `bg.position` should be a [`Point`](http://pixijs.github.io/docs/PIXI.Sprite.html#position) object. Also, `tl.progress` takes [`Number`](http://greensock.com/docs/#/HTML5/GSAP/TimelineMax/progress/) as its first parameter. Post a fiddle and I think it should be an easy fix to apply. – Tahir Ahmed May 31 '15 at 13:57
  • Hello @TahirAhmed thanks so much, [here's my fiddle](http://jsfiddle.net/SquareGiraffe/n0ergr71/) with a test tween (line 65) so you can see it working. (Lines 70-77 currently commented out are what I am working on). I probably need the trackpad.value to move a PIXI.Point for a reference for tweening the background - I'm trying to put a start and stop y value on each tween so I can tween a sequence of sprites, and to put some easing on there too. [Here's a codepen example similar to your example, for doing this with div elements](http://codepen.io/adrianparr/pen/mbrqt). – Agent Zebra May 31 '15 at 23:20
  • jsFiddle doesn't include `Trackpad`?? – Tahir Ahmed May 31 '15 at 23:29
  • Yes it's in the html, I'm pulling it in from here: https://dl.dropboxusercontent.com/s/sjp9wkn1pite18e/Trackpad.js Should I put the code in the js box instead? – Agent Zebra Jun 01 '15 at 00:25
  • [Here's a fork of the above fiddle](http://jsfiddle.net/SquareGiraffe/u6y72g9y/) with the `Trackpad` in the js box – Agent Zebra Jun 01 '15 at 01:14
  • Yeah I think it makes sense to load extra javascript & css files using the *External Resources* options instead of embedding in HTML as far as fiddles are concerned. – Tahir Ahmed Jun 01 '15 at 04:08
  • Anyway, I noticed it (and other files in your HTML) and have downloaded them all on local machine. For me `Trackpad` wasn't generating any values. So your code required further debugging (or digging I should say). – Tahir Ahmed Jun 01 '15 at 04:12
  • Give me a few hours. – Tahir Ahmed Jun 01 '15 at 04:13

2 Answers2

2

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:

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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

Tahir Ahmed
  • 5,687
  • 2
  • 17
  • 28
  • Thanks so much Tahir for looking at this - locally I am getting an error with the above, at line 10 `Uncaught TypeError: undefined is not a function(anonymous function) @ app2.js:10` - I'll try tomorrow to replicate fiddle locally. I just noticed the fiddle, yes! that's it exactly, with just a little more of a easedInOut slowdown at the beginning and end of each mouse scroll / drag or whatever input. What I need is to set it up to use greensock (with a start and finish position value for each tween) so I can add more greensock tweens to it also. Thank you so much Tahir. Very, very helpful. – Agent Zebra Jun 03 '15 at 05:37
  • 1
    @AgentZebra, added **Notes** above. *Ease* is calculated in `Trackpad.js` itself. The `value` it spits takes certain `easing` into account itself already. Read about my previous answer on the topic of [using TimelineMax in accordance with scroll](http://stackoverflow.com/questions/30315401/how-to-adjust-a-greensock-js-tween-based-on-browser-scroll-y-position-rather-tha/30358273#30358273), if I have understood you clearly. – Tahir Ahmed Jun 03 '15 at 22:33
  • Thank you so much Tahir, I think I've got it now. I very much appreciate you taking the time to explain everything so clearly. I'm still not exactly sure how to implement greensock with this, and your previous example in pixi as that was browser based rather than canvas based. – Agent Zebra Jun 06 '15 at 05:14
  • @AgentZebra No problem. You are right about the previous answer being focused on the 'scroll' event of the browser but correct me if I am wrong, you want to listen to `mousemove`, `mousewheel`, `keyboard` & `touch` events trying to either drag or scroll your canvas & based on that, you want to *position* a GSAP `TimelineMax` respective to its previous *position*? – Tahir Ahmed Jun 06 '15 at 05:20
  • I want to have a start and end position var for each timelineMax tween and have them driven by the `mousemove, mousewheel, keyboard & touch` events. [Here's a browser version of what I want to do in pixi] (http://codepen.io/adrianparr/pen/mbrqt) See my `**edit to the initial question above (at the bottom of the question)` , you can see one idea of how to do this in the browser (but not in pixi) - I want to replicate this in pixi. – Agent Zebra Jun 06 '15 at 05:55
  • For instance, I may have one of the background elements scale, and there will be various elements on top of the background moving up/down, left/right and fading in/out, but the `progress` of ALL the tweens will be driven by the `mousemove, mousewheel, keyboard & touch` events rather than by the usual way of working with GSAP with a time duration etc. – Agent Zebra Jun 06 '15 at 05:57
  • Got it. Give me a few hours ;) – Tahir Ahmed Jun 06 '15 at 06:16
  • hey @AgentZebra, I have attempted the problem. New answer. Lets see if it fits the bill. – Tahir Ahmed Jun 08 '15 at 16:39
1

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

Tahir Ahmed
  • 5,687
  • 2
  • 17
  • 28
  • Thanks Tahir, I'm finally getting back to this. This is fantastic, really fabulous work. And I'm super grateful you put so much time into answering my question. I was a little overwhelmed by it tbh. It's highly complex, difficult for a novice to follow. Feels like overkill for what I'm trying to do. I'm really trying to create a very simple pixi implimentation of the following comment: (a duplicate of my `** edit` at the bottom of the above question), super simple so I can understand it :D but also grabbing `value` from trackpad.js. Super awesome other work on your codepen btw, first class! – Agent Zebra Jun 18 '15 at 00:47
  • `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 ); } });` – Agent Zebra Jun 18 '15 at 00:47
  • Thanks for the nice words @AgentZebra. Appreciate it. And yes, I know this is surely an overkill for the use-case we are talking about here. But I always wanted to get into design patterns and this seemed like a great opportunity for me to try them out as well. Two birds one stone you know. So, although I agree that for your case, it is definitely a bit too much but I highly recommend you to take away what you can from it. There are loads of useful links I have placed in code right next to their implementation in all the JS files mentioned above. So do dig them for learning purposes. – Tahir Ahmed Jun 18 '15 at 05:49
  • And I will definitely look into your new *edit* that you have put in the question when I find some free time. Thanks again. – Tahir Ahmed Jun 18 '15 at 05:50