Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
4d03971
feat: add initial motion support for lynx
f0rdream Nov 13, 2025
daf0ccd
feat: add more features like motionValue, springValue
f0rdream Nov 21, 2025
37f1592
feat: add useMotionValueRefEvent
f0rdream Nov 24, 2025
8e5ec1b
feat: use prototype monkey-patch to do with MotionValue toJSON
f0rdream Nov 24, 2025
79e20a9
feat: add styleEffect
f0rdream Nov 26, 2025
d802bea
chore: update node version
f0rdream Nov 21, 2025
9416f31
fix: types conflict
f0rdream Nov 24, 2025
993520b
feat: add useMotionValueRefEvent
f0rdream Nov 24, 2025
6b20026
fix: motion reset transform bug
f0rdream Nov 26, 2025
1d1ad31
fix: shim conflict on web
f0rdream Nov 27, 2025
4d826c0
feat(react): shared runtime imports
Yradex Nov 28, 2025
f27abc9
Merge branch 'motion_lynx_tmp' into mts/dev-2
f0rdream Dec 2, 2025
76d6bc8
feat: use shared module
f0rdream Dec 2, 2025
ca75aaf
fix: motion not query right element
f0rdream Dec 16, 2025
8bbc408
fix: motion not query right element
f0rdream Dec 16, 2025
fda5877
feat: motion mini
f0rdream Dec 29, 2025
a0d24eb
Merge branch 'mts/dev-2' into motion_lynx
f0rdream Jan 8, 2026
7c08daa
chore: update dist artifacts after merge
f0rdream Jan 8, 2026
41f9f85
feat: remove dist
f0rdream Jan 8, 2026
d4f1fe3
feat: rename into xx_
f0rdream Jan 8, 2026
2e01030
Merge branch 'main' into motion_lynx
f0rdream Jan 9, 2026
9fc7e10
feat: rename into @lynx-js/motion
f0rdream Jan 9, 2026
e9e8187
feat: add more tests for motion
f0rdream Jan 16, 2026
5205f01
Merge branch 'main' into motion_lynx
f0rdream Jan 16, 2026
e460d91
chore: update the tsconfig hierarchy
f0rdream Jan 19, 2026
9697d3c
Merge branch 'main' into motion_lynx
f0rdream Jan 19, 2026
b2b9e3d
style: add comments for useMotionValueRef
f0rdream Jan 19, 2026
0752985
chore: update tsconfig for motion
f0rdream Jan 19, 2026
b93796a
chore: revert prepare commands back
f0rdream Jan 19, 2026
76f72ee
Merge branch 'main' into motion_lynx
f0rdream Jan 19, 2026
459fdf4
fix: motion example test types
f0rdream Jan 19, 2026
144f4a8
style: add useMotionValueRef comments
f0rdream Jan 19, 2026
8aca1a7
chore: fix motion eslint errors
f0rdream Jan 19, 2026
af9888a
chore: fix eslint error
f0rdream Jan 19, 2026
5ccf97f
chore: react build dependents
f0rdream Jan 19, 2026
c977ec5
Merge branch 'main' into motion_lynx
f0rdream Jan 21, 2026
00665e0
fix: remove document import
f0rdream Jan 21, 2026
33fee2f
Merge branch 'main' into motion_lynx
f0rdream Jan 22, 2026
0c25946
chore: fix coding style problems
f0rdream Jan 23, 2026
c8ab4cd
Merge branch 'main' into motion_lynx
f0rdream Jan 23, 2026
91b18b3
chore: turn off rslib build cache to prevent building crash
f0rdream Jan 26, 2026
9058fc5
Merge branch 'main' into motion_lynx
f0rdream Jan 26, 2026
2b584aa
Merge branch 'main' into motion_lynx
f0rdream Jan 26, 2026
1527594
chore: add changeset
f0rdream Jan 27, 2026
8b891d4
chore: remove empty changeset
f0rdream Jan 27, 2026
2c4892e
fix: animate accepts strings to be animated
f0rdream Jan 27, 2026
533a3e8
Merge branch 'main' into motion_lynx
f0rdream Jan 28, 2026
d76dcf9
fix: animate string test
f0rdream Jan 28, 2026
e95f4e2
fix: warn unsupported options for mini animate
f0rdream Jan 28, 2026
d09c41a
Merge branch 'main' into motion_lynx
f0rdream Jan 28, 2026
5a6a2b2
Merge branch 'main' into motion_lynx
f0rdream Jan 28, 2026
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
5 changes: 5 additions & 0 deletions .changeset/moody-baboons-happen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@lynx-js/motion': patch
---

Add initial support for `@lynx-js/motion`
1 change: 1 addition & 0 deletions biome.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"packages/testing-library/**",
"packages/react/testing-library/**",
"packages/lynx/gesture-runtime/__test__/**",
"packages/motion/__tests__/**",

"packages/rspeedy/lynx-bundle-rslib-config/test/fixtures/**",
],
Expand Down
2 changes: 2 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ export default tseslint.config(

// gesture-runtime-testing
'packages/lynx/gesture-runtime/__test__/**',
// motion tests
'packages/motion/__tests__/**',
// TODO: enable eslint for tailwind-preset
// tailwind-preset
'packages/tailwind-preset/**',
Expand Down
26 changes: 26 additions & 0 deletions examples/motion/lynx.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { pluginQRCode } from '@lynx-js/qrcode-rsbuild-plugin';
import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin';
import { defineConfig } from '@lynx-js/rspeedy';

const enableBundleAnalysis = !!process.env['RSPEEDY_BUNDLE_ANALYSIS'];

export default defineConfig({
source: {
entry: {
main: './src/index.tsx',
mini: './src/Mini/index.tsx',
},
},
plugins: [
pluginReactLynx(),
pluginQRCode({
schema(url) {
// We use `?fullscreen=true` to open the page in LynxExplorer in full screen mode
return `${url}?fullscreen=true`;
},
}),
],
performance: {
profile: enableBundleAnalysis,
},
});
22 changes: 22 additions & 0 deletions examples/motion/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "@lynx-js/motion-examples",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"build": "rspeedy build",
"dev": "rspeedy dev"
},
"dependencies": {
"@lynx-js/motion": "workspace:*",
"@lynx-js/react": "workspace:*"
},
"devDependencies": {
"@lynx-js/preact-devtools": "^5.0.1-cf9aef5",
"@lynx-js/qrcode-rsbuild-plugin": "workspace:*",
"@lynx-js/react-rsbuild-plugin": "workspace:*",
"@lynx-js/rspeedy": "workspace:*",
"@lynx-js/types": "3.6.0",
"@types/react": "^18.3.25"
}
}
30 changes: 30 additions & 0 deletions examples/motion/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
.case-area {
flex: 1;
}

.container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
padding-top: 60px;
}

.button-area {
display: flex;
flex-direction: row;
gap: 20px;
padding: 0 16px;
flex-wrap: wrap;
}

.text-area {
padding: 16px 16px;
}

.button {
border-radius: 16px;
padding: 8px;
border: 1px solid #333;
flex-shrink: 0;
}
90 changes: 90 additions & 0 deletions examples/motion/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { useState } from '@lynx-js/react';

import Basic from './Basic/index.js';
import BasicPercent from './BasicPercent/index.js';
import BasicSelector from './BasicSelector/index.js';
import ColorInterception from './ColorInterception/index.js';
import iOSSlider from './iOSSlider/index.js';
import Mini from './Mini/index.js';
import MotionValue from './MotionValue/index.js';
import Spring from './Spring/index.js';
import Stagger from './Stagger/index.js';
import Text from './Text/index.js';

import './App.css';

interface CaseItem {
name: string;
comp: () => JSX.Element;
}

const CASES: CaseItem[] = [
{
name: 'Basic',
comp: Basic,
},
{
name: 'BasicPercent',
comp: BasicPercent,
},
{
name: 'Stagger',
comp: Stagger,
},
{
name: 'ColorInterception',
comp: ColorInterception,
},
{
name: 'Spring',
comp: Spring,
},
{
name: 'Text',
comp: Text,
},
{
name: 'BasicSelector',
comp: BasicSelector,
},
{
name: 'MotionValue',
comp: MotionValue,
},
{
name: 'iOSSlider',
comp: iOSSlider,
},
{
name: 'Mini',
comp: Mini,
},
];

export function App() {
const [current, setCurrent] = useState(0);

const CurrentComp = CASES[current]?.comp;

return (
<view className='container'>
<view className='button-area'>
{CASES.map((item, index) => {
return (
<view
key={item.name}
className='button'
bindtap={() => setCurrent(index)}
>
<text>{item.name}</text>
</view>
);
})}
</view>
<text className='text-area'>Current case is: {CASES[current]?.name}</text>
<view className='case-area'>
{CurrentComp && <CurrentComp />}
</view>
</view>
);
}
61 changes: 61 additions & 0 deletions examples/motion/src/Basic/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { animate } from '@lynx-js/motion';
import { runOnMainThread, useEffect, useMainThreadRef } from '@lynx-js/react';
import type { MainThread } from '@lynx-js/types';

import './styles.css';

export default function Basic() {
const animateMTRef = useMainThreadRef<ReturnType<typeof animate> | null>(
null,
);
const boxMTRef = useMainThreadRef<MainThread.Element>(null);

function startAnimation() {
'main thread';

if (boxMTRef.current) {
animateMTRef.current = animate(
boxMTRef.current,
{ scale: 0.4, rotate: '45deg' },
{
ease: 'circInOut',
duration: 1,
repeat: Number.POSITIVE_INFINITY,
repeatType: 'reverse',
},
);
}
}

function endAnimation() {
'main thread';

animateMTRef.current?.stop();
}

useEffect(() => {
const timeoutId = setTimeout(() => {
void runOnMainThread(startAnimation)();
}, 1500);
return () => {
clearTimeout(timeoutId);
void runOnMainThread(endAnimation)();
};
}, []);

Check warning on line 44 in examples/motion/src/Basic/index.tsx

View workflow job for this annotation

GitHub Actions / eslint / check

React Hook useEffect has missing dependencies: 'endAnimation' and 'startAnimation'. Either include them or remove the dependency array
Comment thread
f0rdream marked this conversation as resolved.

return (
<view className='case-container'>
<view
main-thread:ref={boxMTRef}
style={{
width: '100px',
height: '100px',
backgroundColor: '#8df0cc',
borderRadius: '10px',
transform: 'scale(1.5)',
}}
>
</view>
</view>
);
}
6 changes: 6 additions & 0 deletions examples/motion/src/Basic/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.case-container {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
57 changes: 57 additions & 0 deletions examples/motion/src/BasicPercent/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { animate } from '@lynx-js/motion';
import { runOnMainThread, useEffect, useMainThreadRef } from '@lynx-js/react';
import type { MainThread } from '@lynx-js/types';

import './styles.css';

export default function BasicPercent() {
const animateMTRef = useMainThreadRef<ReturnType<typeof animate> | null>(
null,
);
const boxMTRef = useMainThreadRef<MainThread.Element>(null);

function startAnimation() {
'main thread';

if (boxMTRef.current) {
animateMTRef.current = animate(
boxMTRef.current,
{ width: ['10px', '50px'] },
{
ease: 'circInOut',
duration: 1,
repeat: Number.POSITIVE_INFINITY,
repeatType: 'reverse',
},
);
}
}

function endAnimation() {
'main thread';

animateMTRef.current?.stop();
}

useEffect(() => {
void runOnMainThread(startAnimation)();
return () => {
void runOnMainThread(endAnimation)();
};
}, []);

Check warning on line 41 in examples/motion/src/BasicPercent/index.tsx

View workflow job for this annotation

GitHub Actions / eslint / check

React Hook useEffect has missing dependencies: 'endAnimation' and 'startAnimation'. Either include them or remove the dependency array

return (
<view className='case-container'>
<view
main-thread:ref={boxMTRef}
style={{
width: '300px',
height: '100px',
backgroundColor: '#8df0cc',
borderRadius: '10px',
}}
>
</view>
</view>
);
}
6 changes: 6 additions & 0 deletions examples/motion/src/BasicPercent/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.case-container {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
56 changes: 56 additions & 0 deletions examples/motion/src/BasicSelector/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { animate } from '@lynx-js/motion';
import { runOnMainThread, useEffect, useMainThreadRef } from '@lynx-js/react';

import './styles.css';

export default function Basic() {
const animateMTRef = useMainThreadRef<ReturnType<typeof animate> | null>(
null,
);

function startAnimation() {
'main thread';
animateMTRef.current = animate(
'.box',
{ scale: 0.4, rotate: '45deg' },
{
ease: 'circInOut',
duration: 1,
repeat: Number.POSITIVE_INFINITY,
repeatType: 'reverse',
},
);
}

function endAnimation() {
'main thread';

animateMTRef.current?.stop();
}

useEffect(() => {
const timeoutId = setTimeout(() => {
void runOnMainThread(startAnimation)();
}, 1000);
return () => {
clearTimeout(timeoutId);
void runOnMainThread(endAnimation)();
};
}, []);

Check warning on line 39 in examples/motion/src/BasicSelector/index.tsx

View workflow job for this annotation

GitHub Actions / eslint / check

React Hook useEffect has missing dependencies: 'endAnimation' and 'startAnimation'. Either include them or remove the dependency array
Comment thread
coderabbitai[bot] marked this conversation as resolved.

return (
<view className='case-container'>
<view
className='box'
style={{
width: '100px',
height: '100px',
backgroundColor: '#8df0cc',
borderRadius: '10px',
transform: 'scale(1.5)',
}}
>
</view>
</view>
);
}
6 changes: 6 additions & 0 deletions examples/motion/src/BasicSelector/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.case-container {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
Loading
Loading