Pie
Pie

Reputation: 21

Pixi.js images jump when dragged

I am having trouble with pixi.js I am creating a page like http://www.wolverineunleashed.com/#muscles I have created a big stage which the user can use their drag their way around, it all works fine apart from when the user is dragging, the images shake on the screen, I am thinking that it might be a rendering issue? But at the moment I am pulling my hair out so any help would be most grateful. The code I have is:

var w = window.innerWidth;
var h = window.innerHeight;
var images = [];
var stage = new PIXI.Container();
var renderer = new PIXI.autoDetectRenderer(w, h,{transparent:true},true);
document.body.appendChild(renderer.view);

var background = new PIXI.Container();
background.parent = background;
background.interactive = true;

background.on('mousedown', onDragStart)
        .on('touchstart', onDragStart)
        .on('mouseup', onDragEnd)
        .on('mouseupoutside', onDragEnd)
        .on('touchend', onDragEnd)
        .on('touchendoutside', onDragEnd)
        .on('mousemove', onDragMove)
        .on('touchmove', onDragMove);

loadImages();

requestAnimationFrame(animate);

function animate() {
    requestAnimationFrame(animate);
    renderer.render(background);
}

function onDragStart(event)
{
    this.data = event.data;
    this.mousePressPoint = [];
    this.dragging = true;
    this.mousePressPoint[0] = this.data.getLocalPosition(this.parent).x - this.position.x;
    this.mousePressPoint[1] = this.data.getLocalPosition(this.parent).y - this.position.y;
}

function onDragEnd()
{
    this.dragging = false;
    this.data = null;
}

function onDragMove()
{
    if (this.dragging)
    {
        var position = this.data.getLocalPosition(this.parent);
        this.position.x = parseInt(position.x - this.mousePressPoint[0]);
        this.position.y = parseInt(position.y - this.mousePressPoint[1]);
    }
}

Upvotes: 2

Views: 2621

Answers (2)

Chris Rune
Chris Rune

Reputation: 111

This is a very old question, and the PIXI library has changed since the OP, but I was having problems with this myself, and I don't feel the question yet has a good answer...

Some of the PIXI API has changed since the OP, but see the PIXI documentation for the dragging bunnies example.

The jumpiness of the sprite when it first starts to drag is because the sprite is always positioned relative to the anchor/pivot point. Because of this, the position of the anchor point follows the position of the mouse when dragging (at least in the code above). If you click in the center of the bunnies, you don't notice, but clicking on the very edge, produces a very noticeable jump. When using significantly larger sprites (as in the example linked in the OP), this jump can become quite glaring.

Here is how I fixed it:

Very little needs to be changed from the PIXI demo. onDragEnd and onDragMove remain identical:

function onDragEnd(){
    this.dragging=false;
    this.data = null;
}


function onDragMove(){
    if (this.dragging){
        let newPosition = this.data.getLocalPosition(this.parent);
            this.x = newPosition.x;
            this.y = newPosition.y;
        }
}

However, we need to update the pivot point to the location of the click event within the onDragStart function like so:

function onDragStart(event){
    this.data = event.data;

    //store this variable for convenience           
    let position = this.data.getLocalPosition(this);

    // Set the pivot point to the new position
    this.pivot.set(position.x, position.y)

    // update the new position of the sprite to the position obtained through 
    // the global data. This ensures the position lines up with the location of 
    // the mouse on the screen. I'm not certain why, but this is necessary. 
    this.position.set(this.data.global.x, this.data.global.y)
    this.dragging = true;
}   

With this set up, dragging sprites will be smooth, regardless of size. Excellent for creating a click-and-drag-to-explore kind of environment, as linked in the OP.

Hope this helps someone else two years in the future.

Upvotes: 10

Goose Ninja
Goose Ninja

Reputation: 720

I do hope that you have already figured out how to fix this issue, but since there is no answer on here, I'm going to guide you in the right direction.

The code you give us is missing the loadImages() function, but I believe that we should be able to solve it anyway.

The problem seems to lie in this code snippet:

var background = new PIXI.Container();
background.parent = background;
background.interactive = true;

background.on('mousedown', onDragStart)
    .on('touchstart', onDragStart)
    .on('mouseup', onDragEnd)
    .on('mouseupoutside', onDragEnd)
    .on('touchend', onDragEnd)
    .on('touchendoutside', onDragEnd)
    .on('mousemove', onDragMove)
    .on('touchmove', onDragMove);

Here you are making the background container interactive and giving it all the event handlers.

What you should be doing instead is making each individual sprite/image interactive and giving them the eventhandlers that move it around.

I added the following code to your code: // Create a sprite from some image var sprite = new PIXI.Sprite.fromImage('some_image.png');

// Make the sprite interactive. should be done to each individual sprite
sprite.interactive = true;

// Set the anchor in the center of our sprite 
sprite.anchor.x = 0.5;
sprite.anchor.y = 0.5;

// Position our sprite in the center of the renderer
sprite.position.x = renderer.width / 2;
sprite.position.y = renderer.height / 2;


sprite.on('mousedown', onDragStart)
        .on('touchstart', onDragStart)
        .on('mouseup', onDragEnd)
        .on('mouseupoutside', onDragEnd)
        .on('touchend', onDragEnd)
        .on('touchendoutside', onDragEnd)
        .on('mousemove', onDragMove)
        .on('touchmove', onDragMove);


background.addChild(sprite);

And you should put this inside your loadImages function. And then make that function iterate through each image, giving them the event handlers and options they need for this to work.

Here is the code, based on yours, that works.

var w = window.innerWidth;
var h = window.innerHeight;
var images = [];
var stage = new PIXI.Container();
var renderer = new PIXI.autoDetectRenderer(w, h,{transparent:true},true);
document.body.appendChild(renderer.view);

var background = new PIXI.Container();

// Create a sprite from some image
var sprite = new PIXI.Sprite.fromImage('some_image.png');

// Make the sprite interactive. should be done to each individual sprite
sprite.interactive = true;

// Set the anchor in the center of our sprite 
sprite.anchor.x = 0.5;
sprite.anchor.y = 0.5;

// Position our sprite in the center of the renderer
sprite.position.x = renderer.width / 2;
sprite.position.y = renderer.height / 2;


sprite.on('mousedown', onDragStart)
        .on('touchstart', onDragStart)
        .on('mouseup', onDragEnd)
        .on('mouseupoutside', onDragEnd)
        .on('touchend', onDragEnd)
        .on('touchendoutside', onDragEnd)
        .on('mousemove', onDragMove)
        .on('touchmove', onDragMove);


background.addChild(sprite);

requestAnimationFrame(animate);

function animate() {
    requestAnimationFrame(animate);
    renderer.render(background);
}

function onDragStart(event)
{
    this.data = event.data;
    this.mousePressPoint = [];
    this.dragging = true;
}

function onDragEnd()
{
    this.dragging = false;
    this.data = null;
}

function onDragMove()
{
    if (this.dragging)
    {
        var position = this.data.getLocalPosition(this.parent);
        this.position.x = position.x;
        this.position.y = position.y;
    }
}

Upvotes: 0

Related Questions