Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions dev/html/public/examples/avatar-profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,9 @@ <h3 class="profile-name">Maya Powell</h3>
animateView(() => {
buildProfile()
avatar.style.visibility = "hidden"
}, opts).add(avatar, ".profile")
}, opts)
.add(avatar, ".profile")
.crop(true)
}

const close = () => {
Expand All @@ -210,7 +212,9 @@ <h3 class="profile-name">Maya Powell</h3>
document.querySelector(".profile")?.remove()
document.querySelector(".profile-backdrop")?.remove()
avatar.style.visibility = "visible"
}, opts).add(".profile", avatar)
}, opts)
.add(".profile", avatar)
.crop(true)
}

avatar.addEventListener("click", openProfile)
Expand Down
87 changes: 87 additions & 0 deletions dev/html/public/playwright/view/view-crop-timing.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<!doctype html>
<html>
<head>
<style>
body {
margin: 0;
}
.box {
width: 100px;
height: 100px;
background: red;
margin: 40px;
border-radius: 8px;
view-transition-name: box;
}
/* Different aspect ratio AND border-radius -> auto-cropped. */
.box.big {
width: 320px;
height: 120px;
border-radius: 28px;
background: blue;
}
</style>
</head>
<body>
<div class="box" id="box"></div>

<script type="module" src="/src/imports/view.js"></script>
<script type="module">
const { animateView } = window.MotionDOM

window.__view = {
ready: false,
supported: !!document.startViewTransition,
error: null,
radiusIterations: null,
groupIterations: null,
}

const box = document.getElementById("box")

const groupKeyframeMatch = (kind) => (a) => {
const eff = a.effect
return (
eff &&
eff.pseudoElement === "::view-transition-group(box)" &&
eff
.getKeyframes()
.some((k) =>
Object.keys(k).some((p) => kind.test(p))
)
)
}

// `repeat` (and a string `type`) must NOT leak into the radius
// animation: the group geometry's updateTiming ignores repeat, so a
// leaking radius would run twice and desync the clip from the box.
animateView(() => box.classList.add("big"), {
type: "spring",
duration: 1,
ease: "linear",
repeat: 1,
})
.add("#box")
.then(
() => {
const anims = document.getAnimations()
const radius = anims.find(groupKeyframeMatch(/Radius$/))
const group = anims.find(
groupKeyframeMatch(/transform|width|height/i)
)
window.__view.radiusIterations = radius
? radius.effect.getComputedTiming().iterations
: null
window.__view.groupIterations = group
? group.effect.getComputedTiming().iterations
: null
window.__view.ready = true
},
(e) => {
window.__view.error = String(e)
window.__view.ready = true
}
)
</script>
</body>
</html>
7 changes: 3 additions & 4 deletions packages/motion-dom/src/animation/waapi/utils/px-values.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { cornerRadiusProps } from "../../../utils/border-radius"

export const pxValues = new Set([
// Border props
"borderWidth",
Expand All @@ -6,10 +8,7 @@ export const pxValues = new Set([
"borderBottomWidth",
"borderLeftWidth",
"borderRadius",
"borderTopLeftRadius",
"borderTopRightRadius",
"borderBottomRightRadius",
"borderBottomLeftRadius",
...cornerRadiusProps,
// Positioning props
"width",
"maxWidth",
Expand Down
11 changes: 3 additions & 8 deletions packages/motion-dom/src/projection/animation/mix-values.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,9 @@ import {
noop,
} from "motion-utils"
import type { ResolvedValues } from "../../node/types"
import { cornerRadiusProps } from "../../utils/border-radius"

const borderLabels = [
"borderTopLeftRadius",
"borderTopRightRadius",
"borderBottomLeftRadius",
"borderBottomRightRadius",
]
const numBorders = borderLabels.length
const numBorders = cornerRadiusProps.length

const asNumber = (value: AnyResolvedKeyframe) =>
typeof value === "string" ? parseFloat(value) : value
Expand Down Expand Up @@ -54,7 +49,7 @@ export function mixValues(
* Mix border radius
*/
for (let i = 0; i < numBorders; i++) {
const borderLabel = borderLabels[i]
const borderLabel = cornerRadiusProps[i]
let followRadius = getRadius(follow, borderLabel)
let leadRadius = getRadius(lead, borderLabel)

Expand Down
8 changes: 2 additions & 6 deletions packages/motion-dom/src/projection/styles/scale-correction.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import { isCSSVariableName } from "../../animation/utils/is-css-variable"
import { cornerRadiusProps } from "../../utils/border-radius"
import { correctBorderRadius } from "./scale-border-radius"
import { correctBoxShadow } from "./scale-box-shadow"
import type { ScaleCorrectorMap } from "./types"

export const scaleCorrectors: ScaleCorrectorMap = {
borderRadius: {
...correctBorderRadius,
applyTo: [
"borderTopLeftRadius",
"borderTopRightRadius",
"borderBottomLeftRadius",
"borderBottomRightRadius",
],
applyTo: [...cornerRadiusProps],
},
borderTopLeftRadius: correctBorderRadius,
borderTopRightRadius: correctBorderRadius,
Expand Down
12 changes: 12 additions & 0 deletions packages/motion-dom/src/utils/border-radius.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* The four corner-radius longhands. Shared so the projection mixer, scale
* corrector, WAAPI px-value set and view-transition crop pass don't each carry
* their own copy. Order is irrelevant - every consumer mixes/corrects/animates
* each corner independently.
*/
export const cornerRadiusProps = [
"borderTopLeftRadius",
"borderTopRightRadius",
"borderBottomRightRadius",
"borderBottomLeftRadius",
] as const
Loading