Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removing padding from measurement when converting width/height values #1321

Merged
merged 6 commits into from Nov 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Expand Up @@ -4,8 +4,11 @@ Framer Motion adheres to [Semantic Versioning](http://semver.org/).

## [5.0.2] 2021-11-02

### Fixed

- Convert x/y from percent to pixels before drag. [Issue](https://github.com/framer/motion/issues/424)
- Dynamic functions passed to `controls.start()` can now return variant names. [Issue](https://github.com/framer/motion/issues/503)
- Factors in padding when measuring elements for `width`/`height` unit conversion. [Issue](https://github.com/framer/motion/issues/368)

## [5.0.1] 2021-11-01

Expand Down
59 changes: 59 additions & 0 deletions dev/examples/Animation-height-auto-padding.tsx
@@ -0,0 +1,59 @@
import * as React from "react"
import { useState } from "react"
import { motion, AnimatePresence } from "@framer"

export const App = () => {
const [isExpanded, setExpanded] = useState(false)

return (
<div className="example-container">
<AnimatePresence>
{isExpanded ? (
<motion.div
animate={{
height: "auto",
opacity: 1,
paddingTop: 30,
paddingBottom: 30,
}}
exit={{
height: 0,
opacity: 0,
paddingTop: 0,
paddingBottom: 0,
}}
initial={{
height: 0,
opacity: 0,
paddingTop: 0,
paddingBottom: 0,
}}
style={{ background: "white", width: 200 }}
>
Test
</motion.div>
) : null}
</AnimatePresence>
<button style={{}} onClick={() => setExpanded(!isExpanded)}>
Toggle
</button>
<style>{styles}</style>
</div>
)
}

const styles = `body {
background: white!important;
background-repeat: no-repeat;
padding: 0;
margin: 0;
display: flex;
justify-content: flex-start;
align-items: flex-start;
}

.example-container {
width: 320px;
padding: 20px;
}
}`
25 changes: 25 additions & 0 deletions src/render/dom/utils/__tests__/unit-conversion.test.ts
@@ -0,0 +1,25 @@
import { positionalValues } from "../unit-conversion"

describe("Unit conversion", () => {
test("Correctly factors in padding when measuring width/height", () => {
const testDimensions = {
x: { min: 0, max: 100 },
y: { min: 0, max: 300 },
}
expect(
positionalValues.width(testDimensions, { paddingLeft: "50px" })
).toBe(50)

expect(
positionalValues.width(testDimensions, { paddingRight: "25px" })
).toBe(75)

expect(
positionalValues.height(testDimensions, { paddingTop: "50px" })
).toBe(250)

expect(
positionalValues.height(testDimensions, { paddingBottom: "25px" })
).toBe(275)
})
})
38 changes: 20 additions & 18 deletions src/render/dom/utils/unit-conversion.ts
Expand Up @@ -93,20 +93,24 @@ function removeNonTranslationalTransform(visualElement: VisualElement) {
return removedTransforms
}

const positionalValues: { [key: string]: GetActualMeasurementInPixels } = {
// Dimensions
width: ({ x }) => x.max - x.min,
height: ({ y }) => y.max - y.min,

top: (_bbox, { top }) => parseFloat(top as string),
left: (_bbox, { left }) => parseFloat(left as string),
bottom: ({ y }, { top }) => parseFloat(top as string) + (y.max - y.min),
right: ({ x }, { left }) => parseFloat(left as string) + (x.max - x.min),

// Transform
x: getTranslateFromMatrix(4, 13),
y: getTranslateFromMatrix(5, 14),
}
export const positionalValues: { [key: string]: GetActualMeasurementInPixels } =
{
// Dimensions
width: ({ x }, { paddingLeft = "0", paddingRight = "0" }) =>
x.max - x.min - parseFloat(paddingLeft) - parseFloat(paddingRight),
height: ({ y }, { paddingTop = "0", paddingBottom = "0" }) =>
y.max - y.min - parseFloat(paddingTop) - parseFloat(paddingBottom),

top: (_bbox, { top }) => parseFloat(top as string),
left: (_bbox, { left }) => parseFloat(left as string),
bottom: ({ y }, { top }) => parseFloat(top as string) + (y.max - y.min),
right: ({ x }, { left }) =>
parseFloat(left as string) + (x.max - x.min),

// Transform
x: getTranslateFromMatrix(4, 13),
y: getTranslateFromMatrix(5, 14),
}

const convertChangedValueTypes = (
target: TargetWithKeyframes,
Expand All @@ -116,9 +120,7 @@ const convertChangedValueTypes = (
const originBbox = visualElement.measureViewportBox()
const element = visualElement.getInstance()
const elementComputedStyle = getComputedStyle(element)
const { display, top, left, bottom, right, transform } =
elementComputedStyle
const originComputedStyle = { top, left, bottom, right, transform }
const { display } = elementComputedStyle

// If the element is currently set to display: "none", make it visible before
// measuring the target bounding box
Expand All @@ -140,7 +142,7 @@ const convertChangedValueTypes = (
const value = visualElement.getValue(key) as MotionValue
setAndResetVelocity(
value,
positionalValues[key](originBbox, originComputedStyle)
positionalValues[key](originBbox, elementComputedStyle)
)
target[key] = positionalValues[key](targetBbox, elementComputedStyle)
})
Expand Down