framer-motion#useViewportScroll TypeScript Examples

The following examples show how to use framer-motion#useViewportScroll. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: useInViewScroll.ts    From framer-motion-hooks with MIT License 6 votes vote down vote up
useInViewScroll = (
  el: RefObject<HTMLElement>,
  options: IOptions = {}
): MotionValue<number> => {
  const progress = useMotionValue(0)
  const { scrollY } = useViewportScroll()

  useEffect(() => {
    const handleScrollProgress = () => {
      const node = el.current
      if (!node) return

      const threshold = options.threshold || 0

      const elPosY = node.getBoundingClientRect().top + scrollY.get()
      const elHeight = node.scrollHeight

      const viewIntersect = Math.max(elPosY - window.innerHeight, 0)
      const current = scrollY.get() - viewIntersect - threshold
      const total = Math.min(window.innerHeight, elPosY) + elHeight - threshold

      const quotient = current / total

      if (quotient > 0 && quotient < 1) {
        progress.set(quotient)
      }
    }

    handleScrollProgress()
    const unsubscribeFromScroll = scrollY.onChange(handleScrollProgress)

    return () => unsubscribeFromScroll()
  }, [el, options])

  return progress
}
Example #2
Source File: parallax.tsx    From samuelkraft-next with MIT License 6 votes vote down vote up
Parallax = ({ children, offset = 50, clampInitial, clampFinal }: ParallaxProps): JSX.Element => {
  const prefersReducedMotion = useReducedMotion()
  const [elementTop, setElementTop] = useState(0)
  const [clientHeight, setClientHeight] = useState(0)
  const ref = useRef(null)

  const { scrollY } = useViewportScroll()

  const initial = elementTop - clientHeight
  const final = elementTop + offset

  const yRange = useTransform(scrollY, [initial, final], [clampInitial ? 0 : offset, clampFinal ? 0 : -offset])
  const y = useSpring(yRange, { stiffness: 400, damping: 90 })

  useLayoutEffect(() => {
    const element = ref.current
    const onResize = () => {
      setElementTop(element.getBoundingClientRect().top + window.scrollY || window.pageYOffset)
      setClientHeight(window.innerHeight)
    }
    onResize()
    window.addEventListener('resize', onResize)
    return () => window.removeEventListener('resize', onResize)
  }, [ref])

  // Don't parallax if the user has "reduced motion" enabled
  if (prefersReducedMotion) {
    return <>{children}</>
  }

  return (
    <motion.div ref={ref} style={{ y }}>
      {children}
    </motion.div>
  )
}
Example #3
Source File: Parallax.tsx    From vignette-web with MIT License 5 votes vote down vote up
Parallax = ({
  children,
  id,
  offset = 30,
  className,
  fadeIn,
}: ParallaxProps): JSX.Element => {
  const prefersReducedMotion = useReducedMotion()
  const [elementTop, setElementTop] = useState(0)
  const [clientHeight, setClientHeight] = useState(0)
  const ref = useRef<HTMLDivElement>(null)

  const { scrollY } = useViewportScroll()

  const initial = elementTop - clientHeight
  const final = elementTop + offset

  const router = useRouter()

  const yRange = useTransform(scrollY, [initial, final], [offset, -offset], {
    clamp: true,
  })
  const y = useSpring(yRange, { stiffness: 400, damping: 90 })

  useEffect(() => {
    const element = ref.current
    const onResize = () => {
      if (element) {
        setElementTop(
          element.getBoundingClientRect().top + window.scrollY ||
            window.pageYOffset,
        )
      }

      setClientHeight(window.innerHeight)
    }
    onResize()
    window.addEventListener(`resize`, onResize)
    return () => window.removeEventListener(`resize`, onResize)
  }, [ref, router.pathname])

  // Don't parallax if the user has "reduced motion" enabled
  if (prefersReducedMotion) {
    return <>{children}</>
  }

  return (
    <motion.div
      id={id}
      className={className}
      ref={ref}
      style={{ y }}
      transition={fadeIn ? { delay: 0.15, duration: 0.3 } : {}}
      initial={fadeIn && { opacity: 0 }}
      whileInView={fadeIn ? { opacity: 1 } : {}}
      viewport={{ once: true }}
    >
      {children}
    </motion.div>
  )
}