Skip to content

Commit

Permalink
Fixing animating to CSS variables with SVG elements
Browse files Browse the repository at this point in the history
  • Loading branch information
mattgperry committed Nov 23, 2021
1 parent bfece69 commit a68a72f
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 39 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

Framer Motion adheres to [Semantic Versioning](http://semver.org/).

## [5.3.3] 2021-11-Unreleased

### Fixed

- Fixing animating to CSS variables with `SVGElement`. [Issue](https://github.com/framer/motion/issues/1334)

## [5.3.2] 2021-11-23

### Fixed
Expand Down
62 changes: 26 additions & 36 deletions cypress/integration/svg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,8 @@ describe("SVG", () => {
.get("[data-testid='rotate']")
.should(($rotate: any) => {
const rotate = $rotate[0] as SVGRectElement
const {
top,
left,
right,
bottom,
} = rotate.getBoundingClientRect()
const { top, left, right, bottom } =
rotate.getBoundingClientRect()
expect(Math.round(top)).to.equal(29)
expect(Math.round(left)).to.equal(29)
expect(Math.round(right)).to.equal(171)
Expand All @@ -19,12 +15,8 @@ describe("SVG", () => {
.get("[data-testid='scale']")
.should(($scale: any) => {
const scale = $scale[0] as SVGRectElement
const {
top,
left,
right,
bottom,
} = scale.getBoundingClientRect()
const { top, left, right, bottom } =
scale.getBoundingClientRect()
expect(top).to.equal(150)
expect(left).to.equal(0)
expect(right).to.equal(200)
Expand All @@ -33,12 +25,8 @@ describe("SVG", () => {
.get("[data-testid='translate']")
.should(($translate: any) => {
const translate = $translate[0] as SVGRectElement
const {
top,
left,
right,
bottom,
} = translate.getBoundingClientRect()
const { top, left, right, bottom } =
translate.getBoundingClientRect()
expect(top).to.equal(350)
expect(left).to.equal(150)
expect(right).to.equal(250)
Expand All @@ -51,12 +39,8 @@ describe("SVG", () => {
.get("[data-testid='rotate']")
.should(($rotate: any) => {
const rotate = $rotate[0] as SVGRectElement
const {
top,
left,
right,
bottom,
} = rotate.getBoundingClientRect()
const { top, left, right, bottom } =
rotate.getBoundingClientRect()
expect(Math.round(top)).to.equal(29)
expect(Math.round(left)).to.equal(29)
expect(Math.round(right)).to.equal(171)
Expand All @@ -65,12 +49,8 @@ describe("SVG", () => {
.get("[data-testid='scale']")
.should(($scale: any) => {
const scale = $scale[0] as SVGRectElement
const {
top,
left,
right,
bottom,
} = scale.getBoundingClientRect()
const { top, left, right, bottom } =
scale.getBoundingClientRect()
expect(top).to.equal(150)
expect(left).to.equal(0)
expect(right).to.equal(200)
Expand All @@ -79,16 +59,26 @@ describe("SVG", () => {
.get("[data-testid='translate']")
.should(($translate: any) => {
const translate = $translate[0] as SVGRectElement
const {
top,
left,
right,
bottom,
} = translate.getBoundingClientRect()
const { top, left, right, bottom } =
translate.getBoundingClientRect()
expect(top).to.equal(350)
expect(left).to.equal(150)
expect(right).to.equal(250)
expect(bottom).to.equal(450)
})
})

it("Correctly animates to CSS variables", () => {
cy.visit("?test=svg-css-vars")
.wait(50)
.get("svg")
.click()
.wait(50)
.get("circle")
.should(([$circle]: any) => {
expect($circle.getAttribute("fill")).to.equal(
"rgba(180, 0, 180, 1)"
)
})
})
})
29 changes: 29 additions & 0 deletions dev/tests/svg-css-vars.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as React from "react"
import { motion } from "@framer"

/**
* An example of providing a MotionValue to an SVG component via its props
*/

export const App = () => {
const [state, setState] = React.useState(false)
return (
<svg
width="250"
height="250"
viewBox="0 0 250 250"
xmlns="http://www.w3.org/2000/svg"
onClick={() => setState(!state)}
style={{ "--color": "#f00" } as any}
>
<motion.circle
initial={false}
cx={125}
cy={125}
r="100"
animate={{ fill: state ? "var(--color)" : "#00f" }}
transition={{ duration: 3, ease: () => 0.5 }}
/>
</svg>
)
}
7 changes: 4 additions & 3 deletions src/render/dom/utils/css-variables-conversion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ function isCSSVariable(value: any): value is string {
*
* @param current
*/
export const cssVariableRegex = /var\((--[a-zA-Z0-9-_]+),? ?([a-zA-Z0-9 ()%#.,-]+)?\)/
export const cssVariableRegex =
/var\((--[a-zA-Z0-9-_]+),? ?([a-zA-Z0-9 ()%#.,-]+)?\)/
export function parseCSSVariable(current: string) {
const match = cssVariableRegex.exec(current)
if (!match) return [,]
Expand All @@ -27,7 +28,7 @@ export function parseCSSVariable(current: string) {
const maxDepth = 4
function getVariableValue(
current: string,
element: HTMLElement,
element: Element,
depth = 1
): string | undefined {
invariant(
Expand Down Expand Up @@ -64,7 +65,7 @@ export function resolveCSSVariables(
transitionEnd: Target | undefined
): { target: TargetWithKeyframes; transitionEnd?: Target } {
const element = visualElement.getInstance()
if (!(element instanceof HTMLElement)) return { target, transitionEnd }
if (!(element instanceof Element)) return { target, transitionEnd }

// If `transitionEnd` isn't `undefined`, clone it. We could clone `target` and `transitionEnd`
// only if they change but I think this reads clearer and this isn't a performance-critical path.
Expand Down

0 comments on commit a68a72f

Please sign in to comment.