pixi.js 中鼠标激活的 greensock tween 错误

Errors with greensock tween activated by mouse in pixi.js

我正在尝试在 pixi 中滚动 greensock tween。我在尝试将获取 mouse/arrow 输入 (trackpad.value) 的代码与我的 tween 挂钩时遇到错误。

这是我的 greensock 测试补间,以确保我的 greensock 在 pixi 中工作:(必须补间 pixi 中的位置元素):

var t1 = new TimelineMax({onUpdate:animate, onUpdateScope:stage});
 t1.to(bg.position, 3, {y:100});

这是我的代码,我试图将 trackpad.value 挂接到 greensock 代码中(我收到以下错误: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));

然后我尝试了以下 - 它没有用(但我没有收到错误):

var moveIt = trackpad.value / 3240;
  t1.progress(bg.position, moveIt, {});

这是定义触控板值的代码:

/*
 *  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();
}

** 编辑

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 );
     }
 });

this是您想要的那种效果吗?

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/Whosebug/1.5/cat.jpg');
texture2=PIXI.Texture.fromImage('https://dl.dropboxusercontent.com/u/45891870/Experiments/Whosebug/1.5/moon.jpg');
texture3=PIXI.Texture.fromImage('https://dl.dropboxusercontent.com/u/45891870/Experiments/Whosebug/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);
}

如果这正是您正在寻找的东西,请告诉我,然后我会根据与您的代码相比发生的变化为您分解。

备注:

  1. 首先,我在上面的示例中使用了 Pixi.JS 的最新版本 (v3.0.6)。此 v3 更新带来了一些重大变化。其中对您的问题很突出的是:
    • 不再需要 Stage 对象进行渲染。任何 Container 类型的对象都可以直接用于在 canvas.
    • 上渲染
    • 将名称 DisplayObjectContainer 缩短为 Container。这可能是您在评论中提到的环境中尝试实现我的代码时出现错误的原因,因为我认为您使用的是旧版本之一。
    • 阅读有关此更新的全部内容 here, here & here
  2. 我总是喜欢使用最新的和最好的 GSAPv1.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。话虽如此,当前的实现并没有真正使用 TweenMax完全没有。
  3. TweenMax 捆绑了 EasePackCSSPlugin 和其他一些东西。无需单独加载它们。相应地更新您的 HTML。使用这个方便的 GSAP CheatSheet by Peter Tichy 获取此类信息以及有关此工具的更多信息。
  4. Trackpad.js的变化:
    • update 方法中,定义了页面可以向上滚动的最大滚动限制。该值以前是 -10700。我把它改成了-2160。您可能希望将其设置为 -3240 我认为,根据我目前能够理解的您想要实现的目标。
    • 格式更改。
  5. main.js 中的更改(无论您为主脚本文件指定什么名称):
    • 添加 requestAnimationFrame polyfill 感谢 Paul Irish
    • 删除了 var stage= new PIXI.Stage(0xff00ff); 行。阅读上面的 #1 了解详细信息。
    • 已将 DisplayObjectContainer 重命名为 Container,后者已分配给 bg。阅读上面的 #1 了解详细信息。
    • animate 循环中添加了 bg.position.y=trackpad.value;。你错过了这个。您将需要使用 trackpad.value 来定位您的 bg.
    • 在同一个 animate 循环中添加了 trackpad.update();。这是大问题,恕我直言,这是您未能理解其目的的问题。总之,Trackpad.js 需要及时更新其 value 并且由于 requestAnimFrame,您获得的唯一循环 运行 是 animate 循环。因此,update(); 方法被调用。
    • 渲染 bg 而不是 stage。阅读上面的 #1 了解详细信息。
    • 格式更改。

如果有任何不清楚的地方,请告诉我。

T

我想过编辑旧答案,但最终决定放弃,因为我认为它回答了您原来的问题。

查看此 Codepen demo 以了解解决同一问题的新方法。我真的很希望听取社区关于我在这里采用的方法来听取事件并使用它们来调整 GSAP 时间线的意见。

我的示例中使用了 4 个 JS 文件:app.jsconstants.jstimeline.jslisteners.js。可以在演示的 JavaScript 编辑器的 设置齿轮图标 中找到链接。所有这些文件都带有我在 Internet 上找到的针对特定问题的解决方案的链接进行了大量注释。

这些文件中,app.js的代码如下:

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 [
    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 [
    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。在同一个示例中,我还大量尝试了 设计模式 ,主要是 PrototypeSingleton 模式。我也期待社区对它们的评论。

T