Jthorpe
Jthorpe

Reputation: 10204

How do you animate an SVG Transform string with framer-motion

I'm having trouble animating the SVG Transform string using framer-motion. I tried animating it in the usual (manual) way

import { type FC, useRef, useEffect } from "react"
import { animate } from "motion" // formerly framer-motion

const MyComponent:FC<{initialTransform:string, finalTransform:string}> = 
    ({initialTransform, finalTransform}) => {
   const ref = useRef()
   useEffect(() => {
       animate(ref,
          {transform:finalTransform},
          // for visibility into what might be going wrong...
          {update: (transform) => console.log({transform})})   
   },[])
   return <g ref={ref} transform={initialTransform} >
    {... lots of SVG things...}
   </g>
}

but (1) the g element's transform attribute is never updated and (2) the update callback is only called with the initial and final transform and isn't tweened the way numbers and css values (like "20px") would be.

Upvotes: -1

Views: 42

Answers (1)

Jthorpe
Jthorpe

Reputation: 10204

In the end, I used an SVG transform parser library (that I wrote many years ago) to convert the transform strings to objects that could be tweened with framer-motions's mix function, like so:

import { type FC, useRef, useEffect } from "react"
import { animate, mix } from "motion" // formerly framer-motion
import { transform as ya_transform } from "ya-svg-transform"

const MyComponent:FC<{initialTransform:string, finalTransform:string}> = 
    ({initialTransform, finalTransform}) => {
   const ref = useRef()
   useEffect(() => {
       const mixer = mix(
           // could also use .transforms instead of .matrix 
           // if you're confident the transform string will
           // always contain the same kinds and ordering of
           // transforms 
           ya_transform(initialTransform).matrix, 
           ya_transform(finalTransform).matrix,
       )
       animate(0,1,
          {update: (n) => {
          ref.current?.setAttribute("transform", ya_transform(mixer(n)).render())
          })   
   },[])
   return <g ref={ref} transform={initialTransform} >
    {... lots of SVG things...}
   </g>
}

// and later...

<MyComponent
   initialTransform="translate(50,300) skewX(19.8)"
   finalTransform="scale(5) rotate(30)"
/>

Upvotes: 0

Related Questions