12

I have a pinned slider that goes horizontally when you get to it by updating the X value of the inner wrapper. This part works great.

However, for each individual slide, I would then like to have a parallax effect for the text (to go slower as you scroll through - relative to the current slide).

Here's a small (simplified) test-case I set up: https://codepen.io/michaelpumo/pen/EJgWgd

Unfortunately though, for some reason, the text seems to stagger and the animation is not smooth. This could be with me misunderstanding ScrollMagic's API (it's new to me).

I have 2 controllers because the only way I could get the "parallax" part was to set the controller to vertical: false for the second one. Perhaps it's something to do with this?

Help is much appreciated!

JavaScript

const ease = window.Power4.easeInOut
const el = document.querySelector('#el')
const wrapper = document.querySelector('#wrapper')
const slides = el.querySelectorAll('.El__slide')
const amount = slides.length
const controller = new window.ScrollMagic.Controller()
const horizontalMovement = new window.TimelineMax()

const controller2 = new window.ScrollMagic.Controller({
  vertical: false
})

horizontalMovement
  .add([
    window.TweenMax.to(wrapper, 1, {
      x: `-${(100 / amount) * (amount - 1)}%`
    })
  ])

new window.ScrollMagic.Scene({
    triggerElement: el,
    triggerHook: 'onLeave',
    duration: `${amount * 100}%`
  })
  .setPin(el)
  .setTween(horizontalMovement)
  .addTo(controller)

slides.forEach((item, index) => {
  const title = item.querySelector('h1')
  const subtitle = item.querySelector('h2')
  const tween = new window.TimelineMax()

  tween
    .fromTo(title, 1, { x: 0 }, { x: 500 }, 0)
    .fromTo(subtitle, 1, { x: 600 }, { x: 500 }, 0)

  new window.ScrollMagic.Scene({
      triggerElement: item,
      triggerHook: 1,
      duration: '100%'
    })
    .setTween(tween)
    .addTo(controller2)
})

SCSS

.El {
  width: 100%;
  max-width: 100%;
  overflow: hidden;
  &__wrapper {
    display: flex;
    width: 400vw;
    height: 100vh;
  }
  &__slide {
    display: flex;
    align-items: center;
    width: 100vw;
    padding: 50px;
    &:nth-child(1) {
      background-color: salmon;
    }
    &:nth-child(2) {
      background-color: blue;
    }
    &:nth-child(3) {
      background-color: orange;
    }
    &:nth-child(4) {
      background-color: tomato;
    }
  }
  &__content {
    width: 100%;
  }
  &__title,
  &__subtitle {
    position: relative;
  }
}

HTML

<div id="el" class="El">
  <div id="wrapper" class="El__wrapper">
    <div class="El__slide">
      <div class="El__content">
        <h1 class="El__title">Title A</h1>
        <h2 class="El__subtitle">Subtitle A</h2>
      </div>
    </div>
    <div class="El__slide">
      <div class="El__content">
        <h1 class="El__title">Title B</h1>
        <h2 class="El__subtitle">Subtitle B</h2>
      </div>
    </div>
    <div class="El__slide">
      <div class="El__content">
        <h1 class="El__title">Title C</h1>
        <h2 class="El__subtitle">Subtitle C</h2>
      </div>
    </div>
    <div class="El__slide">
      <div class="El__content">
        <h1 class="El__title">Title D</h1>
        <h2 class="El__subtitle">Subtitle D</h2>
      </div>
    </div>
  </div>
</div>
Michael Giovanni Pumo
  • 14,338
  • 18
  • 91
  • 140
  • Honestly there is a major problem with that library. It doesn't support checks for visibility on horizontal. That is why its all broken and jerky. If you did vertical scroll it will probably work fine. – Deckerz Apr 16 '19 at 10:04
  • Okay, thanks @Deckerz - can you recommend another library to get the same effect? Vertical does work fine as you say. I'm pretty sure my code is simple enough so I can't understand if the mistake is on me or not. – Michael Giovanni Pumo Apr 16 '19 at 10:07
  • I played around with it for an hour and a bit and found out that the tween is applied to all 4 scenes at the same time even if they are visible. It merely detects the scrollY axis to be the same. – Deckerz Apr 16 '19 at 10:21
  • Yep, I believe that's intentional. I wanted each tween to be relative to the section it sits within. So we have a loop over the sections that applies a scene for each. – Michael Giovanni Pumo Apr 16 '19 at 11:31

3 Answers3

6

To get the desired effect using ScrollMagic methods you have to set refreshInterval: 1 to the controller2.

Documentantion: http://scrollmagic.io/docs/ScrollMagic.Controller.html#constructor

Example: https://codepen.io/biomagic/pen/dLgzJO

const ease = window.Power4.easeInOut
const el = document.querySelector('#el')
const wrapper = document.querySelector('#wrapper')
const slides = el.querySelectorAll('.El__slide')
const amount = slides.length
const controller = new window.ScrollMagic.Controller()
const horizontalMovement = new window.TimelineMax()

const controller2 = new window.ScrollMagic.Controller({
  vertical: false,
  refreshInterval: 1
})

horizontalMovement
  .add([
    window.TweenMax.to(wrapper, 1, { x: `-${(100 / amount) * (amount - 1)}%` })
  ])

new window.ScrollMagic.Scene({
  triggerElement: el,
  triggerHook: 'onLeave',
  duration: `${amount * 100}%`
})
  .setPin(el)
  .setTween(horizontalMovement)
  .addTo(controller)

slides.forEach((item, index) => {
  const title = item.querySelector('h1')
  const subtitle = item.querySelector('h2')
  const tween = new window.TimelineMax()
  tween
    .fromTo(title, 1, { x: 0 }, { x: 500 }, 0)
    .fromTo(subtitle, 1, { x: 600 }, { x: 500 }, 0)
  new window.ScrollMagic.Scene({
    triggerElement: item,
    triggerHook: 1,
    duration: '100%'
  })
  .setTween(tween)
  .addTo(controller2)
})

Another approach. You can to add tweenChanges: true to your scene.

Documentation: http://scrollmagic.io/docs/animation.GSAP.html#newScrollMagic.Sceneoptions

Example: https://codepen.io/biomagic/pen/rbqpMb

const ease = window.Power4.easeInOut
const el = document.querySelector('#el')
const wrapper = document.querySelector('#wrapper')
const slides = el.querySelectorAll('.El__slide')
const amount = slides.length
const controller = new window.ScrollMagic.Controller()
const horizontalMovement = new window.TimelineMax()

const controller2 = new window.ScrollMagic.Controller({
  vertical: false
})

horizontalMovement
  .add([
    window.TweenMax.to(wrapper, 1, { x: `-${(100 / amount) * (amount - 1)}%` })
  ])

new window.ScrollMagic.Scene({
  triggerElement: el,
  triggerHook: 'onLeave',
  duration: `${amount * 100}%`
})
  .setPin(el)
  .setTween(horizontalMovement)
  .addTo(controller)

slides.forEach((item, index) => {
  const title = item.querySelector('h1')
  const subtitle = item.querySelector('h2')
  const tween = new window.TimelineMax()
  tween
    .fromTo(title, 1, { x: 0 }, { x: 500 }, 0)
    .fromTo(subtitle, 1, { x: 600 }, { x: 500 }, 0)
  new window.ScrollMagic.Scene({
    triggerElement: item,
    triggerHook: 1,
    duration: '100%',
    tweenChanges: true
  })
  .setTween(tween)
  .addTo(controller2)
})
Yura Kosyak
  • 401
  • 3
  • 16
  • Thanks for these options. Of them all, refreshInterval settings seem to work best. Your last example though does not work and actually breaks. A CSS transition is not an option because that transitions a change in styles...so it won't stick exactly to the user scrolling. I've had a lot of answers like this and it's not a solution IMO, more of a hack. I will try these out. – Michael Giovanni Pumo Apr 24 '19 at 09:10
  • Thank you for checking. Updated last example. – Yura Kosyak Apr 24 '19 at 09:39
  • I have swapped 1st and 2nd examples such as from reviews 2nd was is the most correct option. Also removed 3rd and 4th variants based on CSS transition option like incorrect. I am glad that these options came up. – Yura Kosyak Apr 30 '19 at 10:53
  • 1
    Of both of your options, I have gone with the first one using refreshInterval. Whilst it's not perfect, that one appeared to have the most smooth effect on the movement of the title. Thank you for your research! – Michael Giovanni Pumo May 10 '19 at 09:45
0

I'm not sure that I understood the essence of your question. Because my native language is different. But, I want to help you. For a smooth text movement, this will help you:

.El__title, .El__subtitle {
    position: relative;
    transition-property: all;
    transition-duration: 900ms;
    transition-timing-function: ease;
    transition-delay: 0s;
}

If I misunderstood you, then show me an example of what you need in the end. Then I will try to help.

-1

I've tested your code and looks everything nice. I can recommend you if you want a smooth behavior to add a transition to your CSS. On this way you are going to have a smooth transition.

Just add this lines:

&__title,
&__subtitle {
  position: relative;
}

/* Add the next lines */
&__title {
  transition: all .1s linear;
}

you can change the effects and time. I send you a link to the docs: Transition DOCS

AlbertSabate
  • 122
  • 6