motiondivision / motionone Goto Github PK
View Code? Open in Web Editor NEWHome Page: https://motion.dev
License: MIT License
Home Page: https://motion.dev
License: MIT License
Hi :),
When using the following timeline:
const contentElements = this.querySelectorAll('.content-over-media__content');
timeline([
[this.items, {opacity: 1, y: [10, 0]}, {duration: 0.3}],
[contentElements, {opacity: [0, 1]}, {duration: 0.2, at: '+0.1'}]
]);
I was initially setting manually for each element the opacity to 0:
contentElements.forEach(item => item.style.opacity = 0);
However, I found that Motion seems to do that under the hood automatically so that the timeline works as we expect it to work (this is awesome btw). I think it would be nice to add this in the doc:
When using a timeline, all the timeline steps will be initialized with their start values at the start of the animation. As a consequence, you do not need to manually change the style of each timeline elements.
As suggested #16
When generating a spring with very low values and near in a timeline (ie [0, 0.5]) the generated spring produces something like [0, 0]
, finishing prematurely. This should at least end in the target value, and ideally produce a proper spring.
For manual control with the .duration
property and progress 0...1
it's now required to call .pause()
directly as timelines start automatically.
Would it be nice to have a { autoplay: false }
option on the timeline? 👍
spring
and glide
easings are actually keyframe generators. When these are visualised in Dev Tools, they are shown as a series of keyframes spaced 10ms apart with linear easing. Whereas we would ideally mark out blocks generated by spring/ease as just a single keyframe. Likewise when the settings for these are changed via the editor we change the placement of subsequent keyframes based on the calculated duration.
When showing and hiding elements you often repeat the same properties but in reverse.
It would be helpful to have a .reverse()
method on AnimationControls.
Hi,
I think this bug is different from the other bug I submitted the other day as this one occurs with plain animate (I could not try your previous fix as the new version has not been published yet).
You can reproduce it here: https://codesandbox.io/s/proud-dew-mk8ss9?file=/src/index.js
You can close if this issue is the same :).
Rendering on Chrome:
Rendering on Safari:
Thanks ;)
animate
uses its own css variables with style.transform
to enable individual transforms.
But this took me by surprise:
el.style.transform = "translateX(100px)";
/** other styles **/
// ... later ...
animate(el, {rotate: 45});
// translateX is lost
// from now on, I have to remember that motion "owns" style.transform on this element (through its --motion-* css vars)
// if I try to change the transform with any other method, I'll mess up the styles
Because individual transforms are so useful, it'd be nice to opt in even when I don't want an animation. For example:
import {style, animate} from "motion";
style(el, {x:100 /** other styles **/});
// ... later ...
animate(el, {rotate: 45});
I don't know if that's API heresy for a library called motion
, though.... 😂
Hi there!
Super great project 👍 Thinking about using it as my main way of doing animations in production projects—looks really promising, fast, lean!
I want to recreate this Svelte spring example. I've seen a demo on sponsoring page, but it's kind of easier in a sense that it only triggers animation on dropEnd
.
Here's a very naive implementation that just doesn't work like I want it to.
I tried the following approaches:
stop
/cancel
/finish
/nothing. stop
results in best animation from them all, but it still looks far from perfect.Can you point me in some direction? Or maybe it's not a thing this library wants to solve at all?
Thanks!
There is a Vue warning during every render of a Motion
component wrapped in Presence
. Not sure if the fix should be in the library or how the docs show to use the components. Here's the warning:
[Vue warn]: Non-function value encountered for default slot. Prefer function slots for better performance.
at <Transition name=undefined onEnter=fn<bound enter> onLeave=fn<bound exit> ... >
at <Presence exitBeforeEnter="" >
...
@mattgperry I see you are working on moving to a mono-repo structure. Moving/renaming existing files to a new folder's respective name will remove the git history of those files.
Git does not support this right now, and there is a considerable discussion about it. What worked for me was moving/renaming files with this script, but be careful it rewrites its history.
https://gist.github.com/emiller/6769886
Thx!
1. Describe the bug
When using an "ease-in" mode for stagger, everything appears right away.
2. IMPORTANT: Provide a CodeSandbox reproduction of the bug
https://codesandbox.io/s/busy-elbakyan-bg3y1k?file=/src/index.js
4. Expected behavior
I am trying to achieve a stagger animation where the items are revealed faster and faster. According to the doc I should be able to use the "easing" with an ease-in value, but unfortunately all the items are revealed at the same time, without any delay at all.
When using "ease-out" it seems items are revealed one after the other properly, but as far as I understand it ease-out is the opposite of what I am looking for (so revealing slower and slower).
Thanks for this awesome library :).
Is your feature request related to a problem? Please describe.
It would be nice to target pseudo elements
Describe the solution you'd like
This article describes the native implementation: https://danielcwilson.com/blog/2020/05/pseudo-waapi/
Is your feature request related to a problem? Please describe.
I'm using motion to animate elements in some web components written in Typescript. I keep a ref to some animate calls to be able to cancel/stop/finish them if needed in certain cases. However, I cannot properly type these ref variables as AnimationControls
instances easily because the types are imported internally from @motionone/types
and not reexported from motion
.
Describe the solution you'd like
If this is a pattern you agree with, you can export * from '@motionone/types'
in the primary motion
package index so that they could be imported with a statement like import { animate, AnimationControls } from 'motion';
for easier use in Typescript projects.
Example use case:
import { animate, AnimationControls } from 'motion';
export class ExampleElement extends LitElement {
// ...
private animationRef?: AnimationControls;
// ...
}
Describe alternatives you've considered
I could install and import the types from @motionone/types
if that's your preferred solution. I'm interested in the context behind why though.
1. Describe the bug
The first time the variable controlling v-show on the Motion component changes, the specified animation doesn't trigger. It is only on subsequent value changes of the variable that the animation triggers.
2. IMPORTANT: Provide a CodeSandbox reproduction of the bug
https://codesandbox.io/s/first-animation-doesnt-work-5vv8z6
3. Steps to reproduce
Steps to reproduce the behavior:
4. Expected behavior
I expect the box to appear with an animation the first time I press the "toggle" button.
5. Video or screenshots
6. Browser details
Bug occurs when using Windows 10 & 11 and when using Brave (Chromium) and FireFox.
1. Describe the bug
The controls returned from animate have a playState field, but this field is a method that throws an exception when called instead of a plain string.
2. IMPORTANT: Provide a CodeSandbox reproduction of the bug
https://codesandbox.io/s/amazing-pare-c2lyrm
3. Steps to reproduce
Steps to reproduce the behavior:
4. Expected behavior
playState should return a string, not a function (that when called fails).
5. Video or screenshots
6. Browser details
Chrome 101, OSX 12.4
Currently, spring and glide generate keyframes. These keyframes appear in DevTools as many keyframes, it should be an opaque editable chunk.
Currently, if a steps
easing is selected, Dev Tools shows a plaintext input field. We should provide a visualisation, as we do with bezier easing.
Hi,
I am not sure if this is done on purpose, but I am trying to create a timeline where the last sequence only should be repeated:
this._timeline = timeline([
[this.contentElement, {y: -10, opacity: 0}, {duration: 0.2}],
[this.animationElement, {y: [10, 0], opacity: 1}, {duration: 0.5}],
[this.animationElement.children, {y: [0, -10], opacity: [1, 0.5]}, {duration: 2, repeat: Infinity, stagger: 0.5}]
]);
This does not seem to work, but I am not sure if this is by design or a bug. Feel free to close this if this is by design.
Currently animations implement the behaviour of committing styles when an animation finishes. They should instead cancel animations if they finish in reverse.
Hi Matt,
Very excited about this library. As a long-time GSAP user, this is a really compelling option and I'm tempted to start migrating a couple of projects from GSAP. Before diving into that process, there is a GSAP feature called autoAlpha
that I use heavily throughout my projects. GSAP CSSPlugin docs describe it as follows:
Identical to
opacity
except that when the value hits0
thevisibility
property will be set tohidden
in order to improve browser rendering performance and prevent clicks/interactivity on the target. When the value is anything other than0
,visibility
will be set toinherit
. It is not set tovisible
in order to honor inheritance (imagine the parent element ishidden
- setting the child tovisible
explicitly would cause it to appear when that’s probably not what was intended). And for convenience, if the element’svisibility
is initially set tohidden
andopacity
is1
, it will assumeopacity
should also start at0
. This makes it simple to start things out on your page as invisible (set your CSSvisibility: hidden
) and then fade them in whenever you want.
Is this something that you would consider adding to this library? (I am definitely not married to the name autoAlpha
as I believe that naming is an artifact from GSAP's flash days)
Going deep into custom timelines with this one, but it's an odd bug.
🟢 Scenario 1: You create a timeline and it starts playing automatically, eventually currentTime
is equal to duration
.
🔴 Scenario 2: You create a timeline and pause it, then manually transition it from 0...1 * duration
(on scroll for example).
For some reason when you run .pause()
and set currentTime = duration
it appears to just reset to the initial state?
If you let it autoplay and finish and then set currentTime = duration
it's fine.
Here's a sandbox that reproduces the scenario: https://jp0on.csb.app/
Setting easing as an array in default options gets converted incorrecty
When I try and use import { Motion, Presence } from "motion/vue"
, the Motion
component's types are automatically discovered but Presence
is missing which makes the typescript compiler think Presence
is not a declared export of the motion/vue
package. This is just a typescript problem and not an actual problem with the package exports (code works fine).
As a temporary fix I've redeclared the module in my own types as:
declare module 'motion/vue' {
import { Motion, Presence } from '@motionone/vue/dist'
export { Motion, Presence }
}
Currently easing curves are segmented across four inputs, we should make a custom control that makes it easier to copy/paste into and out of the controls
1. Describe the bug
Reversing a reversed animation jumps directly to the finish frame.
2. IMPORTANT: Provide a CodeSandbox reproduction of the bug
https://codesandbox.io/s/inspiring-night-qfc3mp?file=/src/App2.tsx
3. Steps to reproduce
Steps to reproduce the behavior:
4. Expected behavior
The box should grow, shrink then grow again until the end of the animation.
5. Video or screenshots
N/A
6. Browser details
OSX 12.4, Chrome
Hi Matt,
I was looking to replace some of my anime timelines with Motion and there's one thing I'm missing: duration.
It's useful for manually playing a timeline (for example on scroll or interaction based on progress):
Right now in anime I do this:
tl.seek(tl.duration * p); // p = 0...1
In Motion I would like to do this:
tl.currentTime = tl.duration * p // p = 0...1
Couldn't do a PR because forking is disabled, codebase is very clear tho! 🙌 💯
Because of the way the proxy object for animation controls is set up, any property on the controls object is reported as a function if you do something like this:
controls.whatever instanceof Function
Some people use the below code to test if an object is a Promise
.
foo.then instanceof Function
Code deep in the framework I was using did this to an animation control object and then tried to call the "then" function, which doesn't actually exist.
Would it make sense to check whether the property actually exists in this part of controls.ts
?
motionone/src/targets/dom/utils/controls.ts
Lines 58 to 59 in 8f06e5c
perhaps something like
(animation[key] instanceof Function) ? animation[key]() : animation[key])
Sorry if my understanding is off, I just learned what Proxy objects are this morning :)
Since Safari does not support animating variables yet, maybe animate using the individual transforms (which it does support) instead.
https://webkit.org/blog/11420/css-individual-transform-properties/
https://drafts.csswg.org/css-transforms-2/#individual-transforms
https://developer.mozilla.org/en-US/docs/Web/CSS/translate
https://caniuse.com/mdn-css_properties_translate
I'm assuming this would require some feature sniffing at runtime to see which functionality is supported by the current browser.
I'm noticing that interrupting animations in Safari seems to cause the new animation not to play.
Expected behavior (Chrome):
Safari (Version 15.0):
And here's a codesandbox:
https://codesandbox.io/s/cocky-chebyshev-fiy4t?file=/src/index.js
Internally, it supports labelled times (at: "arbitrary-label"
), but there's currently no way of defining what these labeled times map to.
1. Describe the bug
The second finished promise resolves immediately if awaited after a stop/play sequence or cancel/play sequence, rather than when the second part of the animation is actually finished.
Note that if the first part (initial play) does not use the finished promise then it works correctly.
According to the web animation API:
Note: Every time the animation leaves the finished play state (that is, when it starts playing again), a new Promise is created for this property. The new Promise will resolve once the new animation sequence has completed.
But it seems to be stuck returing the first finished promise at the time it was created.
2. IMPORTANT: Provide a CodeSandbox reproduction of the bug
https://codesandbox.io/s/focused-germain-l9ln3b?file=/src/App2.tsx
3. Steps to reproduce
4. Expected behavior
5. Video or screenshots
N/A
6. Browser details
OSX 12.4, Chrome
Would be fantastic to have support for Svelte.js :)
Hi,
This is another quirk of Safari implementation and I am not sure this can be solved.
I have written a minimal test case here: https://codesandbox.io/s/adoring-easley-v7rzyd?file=/src/styles.css
Once you click the button, it will start the animation and then wait 4 seconds, and finish the animation.
On Chrome, this works properly:
On Safari, you can see an odd rendering at the end:
To solve this issue, I had to change the line 30 from:
[button.lastElementChild, {y: -10, opacity: 0}, {duration: 0.15}]
to:
[button.lastElementChild, {y: [0, -10], opacity: 0}, {duration: 0.15}]
But from what I understand from Motion doc, it should infer the previous value so this should not be needed.
Thanks!
1. Describe the bug
Environment: Vue 3, Vite, and Vitest (https://github.com/vitest-dev/vitest).
Importing the library in a component works fine, when using vite, but when imported using vitest, it throws an error (see sandbox).
2. IMPORTANT: Provide a CodeSandbox reproduction of the bug
https://codesandbox.io/s/icy-sky-kin15v?file=/README.md:29-97
3. Steps to reproduce
Steps to reproduce the behavior:
4. Expected behavior
Importing the library using vitest works as well.
5. Analysis
Executing the project normally with "yarn dev" works fine, but running the test with "yarn test" throws an error.
The file "dist/motion-vue.cjs.js" referenced in @motionone/vue/package.json#7 does not exist.
Two fixes I found:
Seems to me, the bug at a mismatch between the aforementioned package.json and the rollup build config at vue/rollup.config.js#126, where the output file of the build command for the cjs-format is set to the ssr.js-version of the file name, but in the package.json it is referenced as .cjs.js. Though I admit, I'm certainly no expert on the topic.
Is your feature request related to a problem? Please describe.
Not sure if this is currently supported, but it'd be nice to be able to somehow generate every state when changing params around to better see how they will behave without needing to run the animation. This could potentially be beneficial for snapshot testing or even creating videos like remotion since every frame can be generated. Ideally, we could eventually visualize slices of animation properties and how they will perform.
Describe the solution you'd like
I think something similar to the animate function, but it produces keyframes:
const animation = keyframes(
{ scale: 0.96, opacity: 0 },
{ scale: 1, opacity: 1 },
{ easing: spring() }
);
// [{ scale: 0.9689, opacity: 0 }, { scale: 0.9713, opacity: 0.1231 }, { scale: 0.9798, opacity: 0.2712 }, ...]
I think these should be general numbers if possible so they are flexible for different use cases.
Describe alternatives you've considered
I was looking into these other libraries, but would like to stay in one library for everything if possible 😊
https://github.com/codepunkt/css-spring
https://github.com/ymzuiku/vanilla-spring
https://github.com/hemlok/spring-keyframes
Hi,
I am not sure if this is something that can be integrated, but is it possible to actually have a method defined as a timeline step, like this:
const sequence = [
["nav", { opacity: 1 }],
() => {doSomething},
["nav li", { opacity: 1 }, { at: "-0.2" }],
]
The use case here is that I am trying to have a timeline where I hide a text, change its content and then make it visible again.
I can of course do this by using two different animate method, but using a timeline would make it a bit easier to read.
Thanks :)
Currently reversing animations generated with the Animation
polyfill will snap the animation to its origin. It should match the behaviour of WAAPI.
Is your feature request related to a problem? Please describe.
I'd like to call animate on an element but have it NOT start as soon as animate is called, but rather at a later time via controls.play().
Describe the solution you'd like
const controls = animate(..., {
// guess this would be only available "globally" and not per css property
autoPlay: false
})
// later, for example when a button is pressed
controls.play()
Describe alternatives you've considered
Calling animate only on first play and cache it. It would work, but then I'd have to mock some stuff until it is actually created (e.g. the finished promise, playState, etc).
Another option is calling pause() as soon as the controls get returned.
Additional context
Might be related to issue #22
1. Describe the bug
According to the docs, if an animation is cancelled the finished promise should throw, but it completes instead.
I don't really mind if it doesn't throw, but then either the docs should be changed to match the current behaviour or the behaviour should change to match the docs :)
2. IMPORTANT: Provide a CodeSandbox reproduction of the bug
3. Steps to reproduce
4. Expected behavior
The mouse over finished promise should throw since the animation gets cancelled before completion, but it completes ok.
5. Video or screenshots
N/A
6. Browser details
OSX 12.4, Chrome
Hey there,
I'm finding motion installs it's own version of react when I'm testing requiring an alpha build of react in my project.
This looks to be due to react & react-dom being listed as optionalDependencies on:
https://github.com/motiondivision/motion/blob/v6.0.1-alpha.24/package.json#L57
Would this instead be able to be changed to peerDependencies and then required in devDependencies for tests and such?
I'm unsure if this is a known limitation, but I'm noticing that animating individual transform properties like scale
and x
means that the resulting animation is susceptible to dropped frames caused by main thread load. This is in both Chrome 94 and Safari 15, motion 10.3.2.
If I run this code, while blocking the main thread, my animation freezes:
animate(
el,
{
scale: toggle ? 0 : 1
},
{ duration: 10, allowWebkitAcceleration: true }
);
If I run this same code, but animate using transform
exclusively, my animation continues to play smoothly:
animate(
el,
{
transform: `scale(${toggle ? 0 : 1})`
},
{ duration: 10, allowWebkitAcceleration: true }
);
And unsurprisingly, running that same animation with WAAPI itself runs fine, too.
Unfortunately this means that seemingly all spring based animations are susceptible, since animating transform
with springs doesn't appear to work.
Sandbox: https://codesandbox.io/s/silly-wind-38xgm?file=/src/index.js
If this is off-base just a quick explanation and close issue would be great.
We're specially interested in actually migrating off framer-motion to MotionOne mostly because we're chasing the smallest possible bundle size.
I'm wondering if supporting layout and AnimatePresence in MotionOne conflict with the design goals? Especially if they're implemented in way that results in potentially opt-in bundle size changes and even further potentially only within the React API?
We'd love to be able to leverage 60 FPS animations while cutting the bundle size cost of animations by 36.6KB (minified) (tenative because obviously implementing these features would grow the size).
proposed api
import { AnimationPresence } from "motion/react/animate-presence" // tree shakeable import
import { Layout } from "motion/react/layout"; // tree shakeable import
return <motion.div layout={Layout} animate={{x: Math.random() > 0.5 > 50 : 0}}/>
Dependent on how motion.div could handle an option layout prop whilst also not importing Layout itself.
I think if these libraries are purposely going to take different directions a feature comparison between motion and framer-motion should be added to motion's documentation; Or inversely is there any reason why framer-motion couldn't use motion as its animation engine?
Hi Matt,
I noticed there's a different approach to timelines between Anime and Motion.
In the following example you can see how Anime automatically resets values when a timeline is created / played.
You can see how all the dots are placed on opacity: 0;
because the animation does opacity: [0, 1]
.
Motion doesn't reset values automatically so if you don't do it manually it will look odd.
If I set opacity: 0;
manually on all dots it will look good the first time I run the timeline, but after that it's still mixed.
Example: https://omz56.csb.app/
Could this be done automatically or with an additional option on the timeline? 👍
Hi,
Question 1:
Does motion-one support canceling and resuming an animation for a single element (using animate) or a group of elements (using timeline) like described here?
https://simplabs.com/blog/2021/01/29/web-animations-intro
Question 2:
I was curious and wanted to learn more about the timeline implementation respectively how it differentiates with animate.
It turns out you are using the animate-style function for both, which internally leverages an element's native animate implementation.
Is there a reason for not using the timeline implementation provided by the browser instead, maybe because it is still experimental?
motionone/src/targets/dom/animate-style.ts
Line 148 in c2d5a32
Thx!
Feature comparison here seems to suggest that <motion.path/> animations should work; I.E animating "d" attribute on a SVG path with the same number of points.
I can't find this property in the typings, nor in the source code is there actually support for animating "d"? or is this documentation referencing animating paths in some other way.
The use case for animations targeting tags is super niche and unlikely due to potential isolation problems.
Targeting classes (modules/css-in-js) though should be №1 use case by popularity. In this case it makes up lots of unnecessary template strings just to add a dot.
Would be super-cool if the lib modified the selector, something along these lines:
const realSelector = ['#', '.'].includes(selector.charAt(0)) ? selector : `.${selector}`
UPD:
This will also work fine for isolated combined selectors with tagnames, like selector + " > li"
. Still, targeting all li
shouldn't be a feature, I think 🤔
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.