Jump to content

Displace image borders


Perdixo
 Share

Recommended Posts

Hey guys,

I'm using Pixi.js to create a blog page template that uses a displacement texture to distort the post images (on load, on hover and reverse it on click).

My issue is that i can't figure out how to affect the borders of my image, so when you hover it for example, the borders also move (now they are static). The image texture is displaced, but i'd like it to affect the containers borders.

I know this might sound easy, but for some reason i can't figure out what i'm missing here...

I've created a jsfiddle for live code.

here is my html

<div class="section">
  <div class="section__image" data-index="1" data-src="https://source.unsplash.com/random/">
  </div>
  <div class="section__content">
    <h2>Lorem ipsum dolor.</h2>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Debitis, rem. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium assumenda atque distinctio eos illo minima, mollitia officiis pariatur provident! Architecto cum facilis nostrum sit voluptate. Dignissimos dolores enim quidem totam?</p>
  </div>
</div>

<div class="section">
  <div class="section__image" data-index="2" data-src="https://source.unsplash.com/random">
  </div>
  <div class="section__content">
    <h2>Lorem ipsum dolor.</h2>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Debitis, rem. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Architecto asperiores dicta eius, est eum itaque magnam officia perspiciatis porro sequi. Autem ea ipsa sed ullam.</p>
  </div>
</div>

<div class="section">

  <div class="section__image"  data-index="3" data-src="https://source.unsplash.com/random">
  </div>
  <div class="section__content">
    <h2>Lorem ipsum dolor.</h2>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Debitis, rem. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad autem est incidunt iusto obcaecati provident quod sequi ut veritatis. At atque aut blanditiis consequatur distinctio dolor, et, ipsa iste nam numquam officia pariatur perferendis possimus provident quibusdam quod repellendus vitae!</p>
  </div>
</div>

<div class="section">
  <div class="section__image" data-index="4" data-src="https://source.unsplash.com/random">
  </div>
  <div class="section__content">
    <h2>Lorem ipsum dolor.</h2>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Debitis, rem. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Consectetur eius nemo perspiciatis quia sit temporibus.</p>
  </div>
</div>

Here is my css

* {
  box-sizing: border-box; 
}

html, body {
  height: 100%; 
}

body {
  margin: 0; 
  font-family: 'Helvetica', serif;
}

.section {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: 0 auto;
  padding: 50px 0;
  max-width: 960px; 
}

.section__image {
    position: relative;
    overflow: hidden;
    flex-shrink: 0;
    width: 100%;
    height: 100%;
}

.section__image > img {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      object-fit: cover;
}

.section__content {
    padding: 10px 0 0; 
}

And here is my javascript

// Note : Resize logic to fit the textures with canvas => https://github.com/pixijs/pixi.js/wiki/v4-Tips,-Tricks,-and-Pitfalls#resizing-renderer

class ImageLoader {
  constructor(element) {
    this.wrapper = element;
    this.width = element.getBoundingClientRect().width;
    this.height = element.getBoundingClientRect().height;
    this.src = element.dataset.src;
    this.mouseOn = false;
    this.animated = false;

    this.app = new PIXI.Application(this.width, this.height, {transparent: true});
    this.wrapper.append(this.app.view);
    this.container = new PIXI.Container();
    const parent = this.app.view.parentNode;
    this.app.renderer.resize(parent.clientWidth, parent.clientHeight);
    this.app.stage.addChild(this.container);

    this.load(this.startAnimation.bind(this));
  }

  load(afterLoad) {
    let tmpImg = new Image();
    tmpImg.src = this.src;
    tmpImg.addEventListener('load', () => {
      afterLoad();
    });
  }

  startAnimation() {
    const that = this;
    // create pixi image and add it to container
    this.bg = PIXI.Sprite.fromImage(this.src);
    //this.bg.width = this.app.screen.width;
    this.bg.width = this.width;
    // this.bg.height = this.height;
    this.bg.position.x = 0;
    this.bg.position.y = 0;
    this.container.addChild(this.bg);
    // create filter
    this.displacementSprite = PIXI.Sprite.fromImage('https://s3-us-west-2.amazonaws.com/s.cdpn.io/123024/disp5.jpg');
    this.displacementSprite.texture.baseTexture.wrapMode = PIXI.WRAP_MODES.REPEAT; // cover with filter all image
    this.displacementFilter = new PIXI.filters.DisplacementFilter(this.displacementSprite);
    // set filter scale
    this.displacementFilter.scale.set(1e4 + Math.random()*1000);
    this.displacementSprite.scale.set(0.4 + 0.6*Math.random());
    // add filter to container & stage (for waves effect on hover)
    this.app.stage.addChild(this.displacementSprite);
    this.container.filters = [this.displacementFilter];

    this.click();

    // animate displacementFilter on image
    let tl = new TimelineMax({onComplete:function() {that.animated = true;}});
    tl.to(that.displacementFilter.scale,1,{x:1,y:1});

    this.hover();
  }

  click() {
    let that = this;
    this.wrapper.addEventListener('click',() => {
        let tl = new TimelineMax(
          {onComplete:function() {that.animated = true;}}
          );
        tl.to(that.displacementFilter.scale,1,{x:1,y:1});
    });
  }

  hover() {
      let that = this;

    this.wrapper.addEventListener('mouseenter',function() {
      if(!that.mouseOn && that.animated) {
        that.mouseOn = true;
        TweenMax.ticker.addEventListener('tick',that.doWaves, that); // same as requestAnimationFrame
        let tl = new TimelineMax();
        tl.to(that.displacementFilter.scale,0.5,{x:20,y:10});
      }

    });

    this.wrapper.addEventListener('mouseleave',function() {
      if(that.mouseOn && that.animated) {
        that.mouseOn = false;
        TweenMax.ticker.removeEventListener('tick',that.doWaves, that);
        let tl = new TimelineMax();
        tl.to(that.displacementFilter.scale,0.5,{x:1,y:1});
      }
    });
  }

  doWaves() {
    this.displacementSprite.x += 1;
  }
}

const imageNodeList = document.querySelectorAll( '.section__image');
Array.prototype.forEach.call(imageNodeList, function (node) {
  const img = new ImageLoader(node);
});

 

Link to comment
Share on other sites

Hey @ivan.popelyshev,

Thanks for the answer! Sorry but i'm new to webgl and PIXI, so i might have some more questions..

I'm not sure to understand how to do this. For the canvas, i guess i might change its size in my constructor by changing this line 

this.app.renderer.resize(parent.clientWidth, parent.clientHeight);

But i couldn't find how to use the `containerfilterArea = app.screen` ..

For the last part i've read about it, i might have to create a plane that i'll reuse each time so only one animation car happer at a time right?

Thanks for your time ?

Link to comment
Share on other sites

Oh, i missed the dot. "container.filterArea = app.screen" for that container you place your filter on. You need it to take borders that you creaet by using renderer bigger than the image. Or you can add padding to filter: "displacementFilter.padding = 20" , something like that.

Yes, you have to create a plane and move it to be above image you want to show effects on. That's the price of using webgl.

I know that DisplacementFilter is our selling point for people who want image effects on html page.

Welcome to the forums!

Edited by ivan.popelyshev
Link to comment
Share on other sites

Thanks @ivan.popelyshev

So i've tried to apply this to my code but for some reason i can't figure out how to implement this.

I've started by adding this line in my constructor

this.container.filterArea = this.app.screen;

Resizing the renderer doesn't work, but i think i don't do it the right way.. i've tried many things, as modyfing the line 16 with

this.app.renderer.resize(parent.clientWidth + 100, parent.clientHeight + 100);

but it scales the entire thing and its borders, so it only makes my image bigger with the same issue. And when using 

this.displacementFilter.padding = 100;

i'm still having the same beginning issue. The only thing that worked for the borders is modifying the sprite width, but that messes up the layout..

this.bg.width = this.app.screen.width - 100;
this.bg.height = this.app.screen.height - 100;

I think i'm not changing the renderer size the right way?

 

Thanks for the welcome and for your time!

Edited by Perdixo
Link to comment
Share on other sites

but you didnt move the image itself. 

OK, basically, layout is your problem, pixi doesnt know what "layout" is. It knows position, scale, rotation. Changing width is actually changing scale.

Its your job to modify position that way Sprite is at the center of the canvas.

Please look through simple demos first: https://pixijs.io/examples/#/sprite/basic.js , https://pixijs.io/examples/#/demos-basic/container.js 

Tell me if you dont get it after that, we can wait for someone to have time to actually modify your demo the way it works :) Its probably 5 minutes, but i dont want to do all the job. In your case its probably just "container.position.set(50,50)"

Edited by ivan.popelyshev
Link to comment
Share on other sites

On 11/4/2019 at 1:07 AM, Perdixo said:

@ivan.popelyshev

Yess thanks, what i did is change the bg.width and set the container position. It works but the thing is that now the image is smaller than the original.

I wonder if there's a better way to preserve the layout. If someone could check the code that would be awesome!

 

OK, then you are on the right way. Unfortunately, I'm still way too busy to look at it. I usually recommend people to check if they understand what is "canvas size" , "css size", "sprite size" and what is "transform". If I just change your code, you will have the same problems next time when you use any other 2d renderer. As an example, you wouldn't have that problem if you used Adobe Flash before: it has the same structure of tree.

Edited by ivan.popelyshev
Link to comment
Share on other sites

Basically, if you put canvas instead of image - it cant affect page that is out. I know its possible to make text inside DIV appear outside - but in this case you cant just command rogue pixels to go out of canvas :)

I see that you use "image { width:100% } " in CSS,  that means you have outer container - and iof course if you try to add border in the pixi stage, without resizing canvas/renderer if will squeeze the image. Its not even automatical - you do it by assigning "bg.width = width - 100".

Suppose "WxH" is your original image size, try to make canvas of size (W+100) x (H+100) and make sure that CSS size is also bigger. If one is in percents, second is in pixels - well, that's a problem, in that case you have to take pen and paper, write it all down and try to solve it like a math equation, it usually helps me.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...