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
22 changes: 22 additions & 0 deletions packages/motion/src/components/__tests__/svg.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,26 @@ describe('row-value', () => {
expect(path.getAttribute('stroke')).toBe('blue')
expect(path.getAttribute('opacity')).toBe('1')
})
it('should update stroke-width through attributes instead of style', async () => {
const strokeWidth = motionValue(2)
const wrapper = render(Motion, {
props: {
as: 'path',
style: {
strokeWidth,
},
},
attrs: {
'data-testid': 'path',
},
})
await nextTick()
const path = wrapper.getByTestId('path')
expect(path.style.strokeWidth).toBeFalsy()
expect(path.getAttribute('stroke-width')).toBe('2')
strokeWidth.set(4)
await delay(100)
expect(path.style.strokeWidth).toBeFalsy()
expect(path.getAttribute('stroke-width')).toBe('4')
})
})
1 change: 1 addition & 0 deletions packages/motion/src/state/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ const SVG_STYLE_TO_ATTRIBUTES = {
'fill': true,
'stroke': true,
'opacity': true,
'stroke-width': true,
'fill-opacity': true,
'stroke-opacity': true,
'stroke-linecap': true,
Expand Down
19 changes: 1 addition & 18 deletions playground/nuxt/pages/layout.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<script setup lang="ts">
import { LazyMotion, m, motion } from 'motion-v'
import { motion } from 'motion-v'
import { ref } from 'vue'

const allFeatures = import('./test').then(res => res.default)
const isExpanded = ref(false)

function handleAnimationComplete() {
Expand All @@ -19,22 +18,6 @@ function handleAnimationStart() {
<button @click="isExpanded = !isExpanded">
{{ isExpanded ? 'Shrink' : 'Expand' }}
</button>
<LazyMotion :features="allFeatures">
<m.button
:layout="true"
:initial="{ borderRadius: '12px' }"
class="bg-purple-500 h-20"
:style="{ width: isExpanded ? '300px' : '100px' }"
tabindex="-1"
:transition="{
layout: {
type: 'spring',
stiffness: 100,
damping: 10,
},
}"
/>
</LazyMotion>

<motion.button
:layout="true"
Expand Down
3 changes: 0 additions & 3 deletions playground/nuxt/pages/test.ts

This file was deleted.

199 changes: 98 additions & 101 deletions playground/nuxt/pages/test.vue
Original file line number Diff line number Diff line change
@@ -1,88 +1,73 @@
<script setup lang="tsx">
/** @jsxImportSource vue */
import { AnimateNumber } from 'motion-number-vue'
import { motion } from 'motion-v'
import { ref } from 'vue'
<script setup lang="ts">
import { animate, motion, useMotionValue, useTransform } from 'motion-v'

const isCompact = ref(true)
const isCurrency = ref(false)
const progress = useMotionValue(0)

function Switch({ isOn, toggle }: { isOn: boolean, toggle: () => void }) {
return (
<motion.button
class="switch-container"
style={{
justifyContent: `flex-${isOn ? 'end' : 'start'}`,
}}
initial={false}
animate={{
backgroundColor: isOn
? 'var(--hue-6-transparent)'
: '#586d8c33',
}}
onClick={toggle}
focus={{
outline: '2px solid #4ff0b7',
}}
>
<motion.div
class="switch-handle"
layout
data-is-on={isOn}
transition={{
type: 'spring',
visualDuration: 0.2,
bounce: 0.2,
}}
/>
</motion.button>
)
const circleStrokeWidth = useTransform(progress, [0, 1], [0, 20])
const circleRotation = useTransform(progress, [0, 1], ['-90deg', '-90deg'])
const circleColor = useTransform(progress, [0, 1], ['#ffffff', '#8df0cc'])

const buttonScale = useTransform(progress, [0, 1], [1, 0.85])
const buttonProgressX = useTransform(progress, [0, 1], ['-200%', '0%'])

function handlePointerDown() {
progress.set(0)
animate(progress, 1, {
duration: 2,
ease: 'easeOut',
})
}
const value = ref(5385)
function changeCurrency() {
// value.value = 58
value.value = Math.random() * 1000 * (Math.random() > 0.5 ? 1 : -1) * 10 ** Math.floor(Math.random() * 3)

function handlePointerUp() {
animate(progress, 0, { duration: 0.3 })
}
</script>

<template>
<div
class="container"
@click="changeCurrency"
>
<AnimateNumber
:format="{
notation: isCompact ? 'compact' : undefined,
compactDisplay: isCompact ? 'short' : undefined,
roundingMode: isCompact ? 'trunc' : undefined,
style: isCurrency ? 'currency' : undefined,
currency: isCurrency ? 'USD' : undefined,
}"
locales="en-US"
class="number"
:transition="{
visualDuration: 0.6,
type: 'spring',
bounce: 0.25,
opacity: { duration: 0.3, ease: 'linear' },
}"
:value="value"
/>
<div class="controls">
<div>
Currency:
<Switch
:is-on="isCurrency"
:toggle="() => isCurrency = !isCurrency"
<div class="container">
<div class="button-wrapper">
<motion.button
class="button"
:style="{
scale: buttonScale,
}"
@pointerdown="handlePointerDown"
@pointerup="handlePointerUp"
@pointerleave="handlePointerUp"
>
<motion.div
class="button-background"
:style="{
x: buttonProgressX,
}"
/>
</div>
<div>
Compact:
<Switch
:is-on="isCompact"
:toggle="() => isCompact = !isCompact"
Hold to confirm
</motion.button>

<motion.svg
class="progress-ring"
width="320"
height="320"
viewBox="0 0 320 320"
>
<motion.circle
cx="160"
cy="160"
r="120"
fill="none"
stroke="var(--white-feint)"
stroke-width="24"
stroke-linecap="round"
:style="{
rotate: circleRotation,
transformOrigin: 'center',
opacity: progress,
stroke: circleColor,
strokeWidth: circleStrokeWidth,
pathLength: progress,
}"
/>
</div>
</motion.svg>
</div>
</div>
</template>
Expand All @@ -91,40 +76,52 @@ function changeCurrency() {
.container {
display: flex;
flex-direction: column;
gap: 16px;
align-items: center;
gap: 20px;
}

.number {
font-size: 78px;
justify-content: center;
padding: 16px;
height: 80px;
}

.controls {
.button-wrapper {
position: relative;
display: flex;
gap: 20px;
border-radius: 50px;
align-items: center;
justify-content: center;
}

.controls > div {
display: flex;
align-items: center;
gap: 10px;
font-size: 18px;
.progress-ring {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
pointer-events: none;
}

.switch-container {
width: 40px;
height: 20px;
border-radius: 25px;
cursor: pointer;
display: flex;
padding: 5px;
.button {
color: var(--black);
background-color: var(--white);
border-radius: 999px;
padding: 12px 20px;
position: relative;
isolation: isolate;
overflow: hidden;
will-change: transform;
user-select: none;
-webkit-user-select: none;
-webkit-touch-callout: none;
}

.switch-handle {
width: 20px;
height: 20px;
background-color: #4ff0b7;
border-radius: 50%;
.button-background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: var(--green);
border-radius: 999px;
z-index: -1;
filter: blur(20px);
scale: 2;
}
</style>