Animation » Example Animations » 16. Scroll: Refresh

16. Scroll: Refresh

The useTransform() hook is used to transform the vertical scroll distance to the small circle’s scale and opacity.

Code component

const items = [0, 1, 2, 3, 4]
const height = 70
const padding = 10
const size = 150

export default function CC_16_Scroll_Refresh(props) {
    const scrollY = useMotionValue(0)
    const scale = useTransform(scrollY, [0, 100], [0, 1])
    const opacity = useTransform(scrollY, [0, 100], [0, 1])

    return (
        <div>
            <motion.div
                style={{
                    width: 40,
                    height: 40,
                    borderRadius: "50%",
                    backgroundColor: "#fff",
                    position: "absolute",
                    top: 120,
                    left: "50%",
                    marginLeft: -20,
                    scale: scale,
                    opacity: opacity,
                }}
            />
            <motion.div
                style={{
                    width: size,
                    height: size,
                    borderRadius: 30,
                    overflow: "hidden",
                    position: "relative",
                    transform: "translateZ(0)",
                    cursor: "grab",
                }}
                whileTap={{ cursor: "grabbing" }}
            >
                <motion.div
                    style={{
                        width: size,
                        height: getHeight(items),
                        y: scrollY,
                    }}
                    drag="y"
                    dragConstraints={{
                        top: -getHeight(items) + size,
                        bottom: 0,
                    }}
                >
                    {items.map((index) => {
                        return (
                            <div
                                style={{
                                    width: size,
                                    height: height,
                                    borderRadius: 20,
                                    backgroundColor: "#fff",
                                    marginBottom:
                                        index !== items.length - 1 ? 10 : 0,
                                }}
                            />
                        )
                    })}
                </motion.div>
            </motion.div>
        </div>
    )
}

function getHeight(items) {
    const totalHeight = items.length * height
    const totalPadding = (items.length - 1) * padding
    const totalScroll = totalHeight + totalPadding
    return totalScroll
}

Code overrides

Pro tip: Don’t use a ‘native’ scroll with overrides, or things will not work.

You can only use useMotionValue() inside an override function (or code component). That’s not convenient when you need to share the value between overrides, so here I used the non-hook version: motionValue().

const scrollY = motionValue(0)

export function Circle(Component): ComponentType<any> {
    return (props) => {
        const { style, ...rest } = props

        const scale = useTransform(scrollY, [0, 100], [0, 1])
        const opacity = useTransform(scrollY, [0, 100], [0, 1])

        return <Component {...rest} style={{ ...style, scale, opacity }} />
    }
}

export function Scroll(Component): ComponentType {
    return (props) => {
        return <Component {...props} contentOffsetY={scrollY} />
    }
}

2 comments on “16. Scroll: Refresh”

  • danbillingham says:

    Hej – I am having some trouble with the scroll refresh example.

    Property ‘style’ does not exist on type ‘{ children?: ReactNode;}’

    Any thoughts.

    • Tes Mat says:

      That’s just a TypeScript error. It’ll probably still work.

      To get rid of the error: Give your override a type of <any> (like I did in the example project):

      export function Circle(Component): ComponentType<any> {
          …

      Or put a // @ts-ignore before the line that gives the error

      export function Circle(Component): ComponentType {
          return (props) => {
              // @ts-ignore
              const { style, ...rest } = props
      
              …

Leave a Reply