Animation » Example Animations » 14. Drag: 3D transform

14. Drag: 3D transform

This example also uses useTransform() to transform the drag distance to other values (like the one on the previous page). Here, the x and y positions are converted into rotateX and rotateY values.

This box is made draggable in all directions but has drag constraints that make it bounce back when you release it. In addition, its drag elastic is set a bit higher than the default so you can drag it further.

Here, we’re tracking both its x and y and changing them to 3D rotation values.

  • When you drag the box left or right (changing its x), you change its rotateY (the axis that runs from top to bottom).
  • And when you drag it up or down (y), you change its rotateX (the horizontal axis).

Code component

export default function CC_14_Drag_3D_transform(props) {
    const x = useMotionValue(0)
    const y = useMotionValue(0)
    const rotateX = useTransform(y, [-100, 100], [60, -60])
    const rotateY = useTransform(x, [-100, 100], [-60, 60])

    return (
        <div>
            <div
                style={{
                    width: 100,
                    height: 100,
                    borderRadius: "50%",
                    background: `radial-gradient(rgba(255,255,255,0),
                        rgba(255,255,255,0.3))`
                    perspective: 800,
                }}
            >
                <motion.div
                    style={{
                        width: 150,
                        height: 150,
                        borderRadius: 30,
                        backgroundColor: "#fff",
                        left: -25,
                        top: -25,
                        position: "relative",
                        x,
                        y,
                        rotateX,
                        rotateY,
                        cursor: "grab",
                    }}
                    drag
                    dragConstraints={{ top: 0, right: 0, bottom: 0, left: 0 }}
                    dragElastic={0.6}
                    whileTap={{ cursor: "grabbing" }}
                />
            </div>
        </div>
    )
}

Code overrides

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

        const x = useMotionValue(0)
        const y = useMotionValue(0)
        const rotateX = useTransform(y, [-100, 100], [60, -60])
        const rotateY = useTransform(x, [-100, 100], [-60, 60])

        return (
            <Component
                {...rest}
                drag
                dragConstraints={{ top: 0, right: 0, bottom: 0, left: 0 }}
                dragElastic={0.6}
                style={{ ...style, x, y, rotateX, rotateY }}
            />
        )
    }
}

Without a CSS perspective value, you wouldn’t see any 3D effect, so the box’s parent layer (that unassuming small circle in the back) has this code override.

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

        return <Component {...rest} style={{ ...style, perspective: 800 }} />
    }
}


Join the Framer book mailing list    ( ± 6 emails/year )

GDPR

We use Mailchimp as our marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing per their Privacy Policy and Terms.



Leave a Reply