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 {
    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: scale, opacity: opacity }}
            />
        )
    }
}

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

Leave a Reply