Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 378fc4c

Browse files
authored
Merge pull request #3761 from motiondivision/worktree-view-target
Resolve selector/Element targets in animateView()
2 parents 830c737 + 94ea505 commit 378fc4c

49 files changed

Lines changed: 5428 additions & 172 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>animateView() — avatar profile</title>
6+
<style>
7+
html,
8+
body {
9+
height: 100%;
10+
}
11+
body {
12+
margin: 0;
13+
}
14+
15+
.avatar-demo,
16+
.avatar-demo * {
17+
box-sizing: border-box;
18+
}
19+
20+
.avatar-demo {
21+
width: 100%;
22+
min-height: 100%;
23+
background: #0e0e10;
24+
color: #fff;
25+
font-family: var(
26+
--font-sans,
27+
-apple-system,
28+
BlinkMacSystemFont,
29+
"Segoe UI",
30+
Roboto,
31+
Helvetica,
32+
Arial,
33+
sans-serif
34+
);
35+
display: flex;
36+
align-items: center;
37+
justify-content: center;
38+
--grad: linear-gradient(140deg, #5b8cff, #9b5bff);
39+
}
40+
41+
.avatar {
42+
width: 72px;
43+
height: 72px;
44+
border: 0;
45+
border-radius: 50%;
46+
background: var(--grad);
47+
color: rgba(255, 255, 255, 0.92);
48+
font-family: inherit;
49+
font-size: 22px;
50+
font-weight: 700;
51+
letter-spacing: 0.04em;
52+
display: flex;
53+
align-items: center;
54+
justify-content: center;
55+
cursor: pointer;
56+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
57+
}
58+
59+
.profile-backdrop {
60+
position: fixed;
61+
inset: 0;
62+
z-index: 10;
63+
background: rgba(0, 0, 0, 0.55);
64+
}
65+
66+
.profile {
67+
position: fixed;
68+
top: 50%;
69+
left: 50%;
70+
transform: translate(-50%, -50%);
71+
width: min(320px, calc(100% - 40px));
72+
z-index: 11;
73+
overflow: hidden;
74+
background: #17171b;
75+
border: 1px solid #26262c;
76+
border-radius: 20px;
77+
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.6);
78+
}
79+
80+
.profile-banner {
81+
height: 120px;
82+
display: flex;
83+
align-items: center;
84+
justify-content: center;
85+
background: var(--grad);
86+
font-size: 40px;
87+
font-weight: 700;
88+
color: rgba(255, 255, 255, 0.92);
89+
}
90+
91+
.profile-body {
92+
display: flex;
93+
flex-direction: column;
94+
gap: 6px;
95+
padding: 20px;
96+
}
97+
98+
.profile-name {
99+
margin: 0;
100+
font-size: 20px;
101+
}
102+
103+
.profile-role {
104+
margin: 0;
105+
font-size: 13px;
106+
color: #9a9aa2;
107+
}
108+
109+
.profile-bio {
110+
margin: 6px 0 14px;
111+
font-size: 14px;
112+
line-height: 1.5;
113+
color: #c9c9d1;
114+
}
115+
116+
.profile-actions {
117+
display: flex;
118+
gap: 10px;
119+
}
120+
121+
.profile-follow,
122+
.profile-message {
123+
flex: 1;
124+
border: 1px solid #2e2e34;
125+
border-radius: 10px;
126+
padding: 10px;
127+
font-family: inherit;
128+
font-size: 14px;
129+
font-weight: 600;
130+
cursor: pointer;
131+
}
132+
133+
.profile-follow {
134+
background: #fff;
135+
color: #111;
136+
border-color: #fff;
137+
}
138+
139+
.profile-message {
140+
background: transparent;
141+
color: #fff;
142+
}
143+
</style>
144+
</head>
145+
<body>
146+
<div class="avatar-demo">
147+
<button class="avatar" id="avatar" aria-label="Open profile">
148+
MP
149+
</button>
150+
</div>
151+
152+
<script type="module" src="/src/imports/view.js"></script>
153+
<script type="module">
154+
const { animateView, spring } = window.MotionDOM
155+
156+
/**
157+
* ============== Setup ================
158+
*/
159+
const root = document.querySelector(".avatar-demo")
160+
const avatar = document.getElementById("avatar")
161+
let open = false
162+
163+
const opts = { type: spring, visualDuration: 0.45, bounce: 0.22 }
164+
165+
const buildProfile = () => {
166+
const backdrop = document.createElement("div")
167+
backdrop.className = "profile-backdrop"
168+
backdrop.addEventListener("click", close)
169+
170+
const profile = document.createElement("div")
171+
profile.className = "profile"
172+
profile.innerHTML = `
173+
<div class="profile-banner">MP</div>
174+
<div class="profile-body">
175+
<h3 class="profile-name">Maya Powell</h3>
176+
<p class="profile-role">Product designer · Motion</p>
177+
<p class="profile-bio">Designs interfaces that move. Currently exploring view transitions and the space between two layouts.</p>
178+
<div class="profile-actions">
179+
<button class="profile-follow">Follow</button>
180+
<button class="profile-message">Message</button>
181+
</div>
182+
</div>`
183+
root.append(backdrop, profile)
184+
}
185+
186+
/**
187+
* ============== View animations ================
188+
*/
189+
const openProfile = () => {
190+
if (open) return
191+
open = true
192+
193+
// The avatar (a circle) morphs into the whole card. Crop is on by
194+
// default, so its corner radius animates from 50% to the card's, and
195+
// its contents are scaled to cover the growing box.
196+
animateView(() => {
197+
buildProfile()
198+
avatar.style.visibility = "hidden"
199+
}, opts) .add(avatar, ".profile")
200+
}
201+
202+
const close = () => {
203+
if (!open) return
204+
open = false
205+
206+
animateView(() => {
207+
document.querySelector(".profile")?.remove()
208+
document.querySelector(".profile-backdrop")?.remove()
209+
avatar.style.visibility = "visible"
210+
}, opts) .add(".profile", avatar)
211+
}
212+
213+
avatar.addEventListener("click", openProfile)
214+
document.addEventListener("keydown", (e) => {
215+
if (e.key === "Escape") close()
216+
})
217+
</script>
218+
</body>
219+
</html>

dev/html/public/examples/clip.html

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>animateView() — circular clip-path enter / leave</title>
6+
<style>
7+
* {
8+
box-sizing: border-box;
9+
}
10+
body {
11+
margin: 0;
12+
min-height: 100vh;
13+
background: #0e0e10;
14+
color: #fff;
15+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI",
16+
sans-serif;
17+
padding: 40px;
18+
}
19+
.bar {
20+
display: flex;
21+
align-items: center;
22+
gap: 16px;
23+
flex-wrap: wrap;
24+
margin-bottom: 28px;
25+
}
26+
h1 {
27+
font-size: 18px;
28+
margin: 0 12px 0 0;
29+
}
30+
button {
31+
border: 1px solid #2e2e34;
32+
background: #1a1a1f;
33+
color: #fff;
34+
border-radius: 8px;
35+
padding: 9px 16px;
36+
font-weight: 600;
37+
cursor: pointer;
38+
}
39+
button:hover {
40+
background: #26262d;
41+
}
42+
label {
43+
display: flex;
44+
align-items: center;
45+
gap: 6px;
46+
font-size: 13px;
47+
color: #9a9aa2;
48+
}
49+
input[type="range"] {
50+
accent-color: #6d5efc;
51+
}
52+
.grid {
53+
display: grid;
54+
grid-template-columns: repeat(4, 130px);
55+
gap: 18px;
56+
}
57+
.tile {
58+
aspect-ratio: 1;
59+
border-radius: 16px;
60+
background: var(--c);
61+
}
62+
</style>
63+
</head>
64+
<body>
65+
<div class="bar">
66+
<h1>Reveal / hide</h1>
67+
<button id="reveal">Reveal</button>
68+
<button id="hide">Hide</button>
69+
<label>
70+
Reveal with
71+
<select id="mode">
72+
<option value="circle" selected>circle clip-path</option>
73+
<option value="opacity">opacity</option>
74+
</select>
75+
</label>
76+
<label>
77+
Stagger
78+
<input
79+
id="stagger"
80+
type="range"
81+
min="0"
82+
max="0.12"
83+
step="0.01"
84+
value="0.05"
85+
/>
86+
</label>
87+
</div>
88+
89+
<div class="grid" id="grid"></div>
90+
91+
<script type="module" src="/src/imports/view.js"></script>
92+
<script type="module">
93+
const { animateView, spring, stagger } = window.MotionDOM
94+
95+
const COLORS = [
96+
"linear-gradient(135deg,#ff512f,#dd2476)",
97+
"linear-gradient(135deg,#2193b0,#6dd5ed)",
98+
"linear-gradient(135deg,#8e2de2,#4a00e0)",
99+
"linear-gradient(135deg,#f7971e,#ffd200)",
100+
"linear-gradient(135deg,#00b09b,#96c93d)",
101+
"linear-gradient(135deg,#e96443,#904e95)",
102+
"linear-gradient(135deg,#11998e,#38ef7d)",
103+
"linear-gradient(135deg,#fc466b,#3f5efb)",
104+
]
105+
const grid = document.getElementById("grid")
106+
const modeEl = document.getElementById("mode")
107+
const staggerEl = document.getElementById("stagger")
108+
let shown = true
109+
110+
const render = () =>
111+
(grid.innerHTML = COLORS.map(
112+
(c) => `<div class="tile" style="--c:${c}"></div>`
113+
).join(""))
114+
render()
115+
116+
// The whole point: swap the enter/exit keyframes and the layers
117+
// iris in/out with a circle instead of fading.
118+
const keyframes = (dir) => {
119+
if (modeEl.value === "opacity") {
120+
return dir === "in" ? { opacity: [0, 1] } : { opacity: 0 }
121+
}
122+
const open = "circle(75% at 50% 50%)"
123+
const closed = "circle(0% at 50% 50%)"
124+
return {
125+
clipPath: dir === "in" ? [closed, open] : [open, closed],
126+
}
127+
}
128+
const opts = () => ({
129+
type: spring,
130+
visualDuration: 0.6,
131+
bounce: 0,
132+
delay: stagger(parseFloat(staggerEl.value)),
133+
})
134+
135+
document.getElementById("reveal").addEventListener("click", () => {
136+
if (shown) return
137+
shown = true
138+
animateView(render).add(".tile").enter(keyframes("in"), opts())
139+
})
140+
141+
document.getElementById("hide").addEventListener("click", () => {
142+
if (!shown) return
143+
shown = false
144+
animateView(() => (grid.innerHTML = ""))
145+
.add(".tile")
146+
.exit(keyframes("out"), opts())
147+
})
148+
</script>
149+
</body>
150+
</html>

0 commit comments

Comments
 (0)