react native Animated.spring doesn't work smoothly - javascript

i'am using react native animated API to make a smooth transition from left to right position.
this is my initial state
constructor(props) {
super(props);
this.state = {
isDrawerOpened: false,
left: new Animated.Value(-100)
};
this.setDrawer = this.setDrawer.bind(this);
}
its still working fine when componentDidMount has called
componentDidMount() {
Animated.spring(this.state.left, {toValue: 200}).start();
}
But when i'am using event to change the state (also starting animation). The transition become rough (not smooth).
This is my event code
setDrawer() {
this.setState({
isDrawerOpened: !this.state.isDrawerOpened
});
if (this.state.isDrawerOpened) {
this.setState({
left: new Animated.Value(200)
});
Animated.spring(this.state.left, {toValue: -100, speed: 1000}).start();
} else {
this.setState({
left: new Animated.Value(-100)
});
Animated.spring(this.state.left, {toValue: 200, speed: 1000}).start();
}
}
can anyone solve this :( , sorry for my bad english

Try going for useNativeDriver: true. Your animations are now run in the JS thread and might be blocked by other stuff going on in your JS code. This will move animations to the UI thread.
See docs for reference.

Related

sveltekit adds new page on top of old one

I just got a really unexpected bug in my sveltekit application and I can't find anything online talking about it
I have a normal sveltekit application but instead of hydrating the new code when navigating to a new page, it just adds the new code on top of the old one, when i refresh the page it removes the old code (from the previous page)
Edit: after a little bit more exploring I realized it only happens on one page, what part of my code could make this happen?
I had the same issue when having a page loading indicator on SvelteKit for internal navigating. Any page DOM modification, to display the loading indicator during the page navigating, caused the paged appearing twice error. The workaround was to not modify the page during the navigating and use only CSS to display, animate and hide the loading indicator.
Here is my loading indicator code and Here is my documentation regarding the issue. I am not sure if this an internal bug in SvelteKit, so I did not file any bug reports, as I do not have a clean repeatable example. You can also see the fixed page loading indicator on the action on this page if you click any of the blockchains.
<script>
/**
* Svelte does not give a load indication if you hit a link that leads to a page with slow load() function.
* Svelte uses internal router, not server-side loading.
* Thus, we need to manually give some indication in the user interface if the loading takes more than a blink of an eye.
*
* The component is originally made for https://tradingstrategy.ai
*
* Based on the original implementation https://github.com/shajidhasan/sveltekit-page-progress-demo by Shajid Hasan.
*
* As this component is absolutely position, you can put it at any part of your __layout.svelte.
*/
import { onDestroy, onMount } from 'svelte';
import { writable } from 'svelte/store';
import { tweened } from 'svelte/motion';
import { cubicOut } from 'svelte/easing';
const navigationState = writable();
const progress = tweened(0, {
duration: 3500,
easing: cubicOut
});
const unsubscribe = navigationState.subscribe((state) => {
// You will always get state=undefined
// event on the server-side rendering, so
// safely ignore it
//console.log("The loading state is", state);
if (state === 'loading-with-progress-bar') {
progress.set(0, { duration: 0 });
progress.set(0.8, { duration: 5000 });
} else if (state === 'loaded') {
progress.set(1, { duration: 1000 });
}
});
onMount(() => {
// progress.set(0.7);
});
onDestroy(() => {
unsubscribe();
});
</script>
<!-- See the (little) documentation of special SvelteKit events here https://kit.svelte.dev/docs#events -->
<svelte:window
on:sveltekit:navigation-start={() => {
// If the page loads fast enough in the preloading state,
// never display the progress bar
$navigationState = 'preloading';
// Delay the progress bar to become visible an eyeblink... only show if the page load takes too long
setTimeout(function() {
// After 250ms switch preloading to loading-with-progress-bar
if($navigationState === 'preloading') {
$navigationState = 'loading-with-progress-bar';
}
}, 500);
}}
on:sveltekit:navigation-end={() => {
$navigationState = 'loaded';
}}
/>
<!--
Make sure the container component is always in the DOM structure.
If we make changes to the page structure during the navigation, we get a page double render error:
https://stackoverflow.com/questions/70051025/sveltekit-adds-new-page-on-top-of-old-one
Not sure if this is a bug or a feature.
Thus, make sure any progress animation is done using CSS only.
-->
<div class="page-progress-bar" class:loaded={$navigationState === 'loaded'} class:preloading={$navigationState === 'preloading'} class:loading={$navigationState === 'loading-with-progress-bar'}>
<div class="progress-sliver" style={`--width: ${$progress * 100}%`} />
</div>
<style>
/* Always stay fixed at the top, but stay transparent if no activity is going on */
.page-progress-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 0.5rem;
background: transparent;
z-index: 100;
opacity: 0;
transition: opacity 0.5s;
}
/* After transitioning from preloading to loading state, make the progress bar visible with CSS transition on opacity */
.page-progress-bar.loading {
opacity: 1;
transition: opacity 0.5s;
}
.progress-sliver {
width: var(--width);
background-color: var(--price-up-green);
height: 100%;
}
</style>
I also saw this issue with the transition directive. The new page loads while the exit animation is playing. I used local transitions to solve this.
https://svelte.dev/tutorial/local-transitions

Framer-motion drag not respecting previously updated props

A simple use-case is to allow a user to either click buttons to paginate in a slider, or drag. Both events call the same paginate function with a param to either go forward or back--simple stuff.
However, the trigger from drag seems to cause bizarre behavior where the slider wants to start the animation from several slides back as if it ignores the updated props. This doesn't happen when using the buttons and both use the same simple paginate call.
Any tips appreciated.
Minimal example:
export default function App() {
const [position, setPosition] = useState<number>(0);
const paginate = (direction: Direction) => {
setPosition((prev) => {
return direction === Direction.Forward
? Math.max(-800, prev - 200)
: Math.min(0, prev + 200);
});
};
return (
<div className="App">
<Slider>
<Wrapper
animate={{ x: position }}
transition={{
x: { duration: 1, type: "tween" }
}}
drag="x"
dragConstraints={{
top: 0,
left: 0,
right: 0,
bottom: 0
}}
onDragEnd={(e, { offset, velocity }) => {
const swipe = swipePower(offset.x, velocity.x);
if (swipe < -swipeConfidenceThreshold) {
paginate(Direction.Forward);
} else if (swipe > swipeConfidenceThreshold) {
paginate(Direction.Back);
}
}}
>
<Slide>1</Slide>
<Slide className="alt">2</Slide>
<Slide>3</Slide>
<Slide className="alt">4</Slide>
<Slide>5</Slide>
</Wrapper>
</Slider>
<button onClick={() => paginate(Direction.Back)}>prev</button>
<button onClick={() => paginate(Direction.Forward)}>next</button>
</div>
);
}
Codesandbox Demo
I have to say, this problem is quite interesting. However, I think I figured out a way for you to handle this. One thing I noticed is that if you comment out
onDragEnd={(e, { offset, velocity }) => {
// const swipe = swipePower(offset.x, velocity.x);
// if (swipe < -swipeConfidenceThreshold) {
// paginate(Direction.Forward);
// } else if (swipe > swipeConfidenceThreshold) {
// paginate(Direction.Back);
// }
}}
the entire onDragEnd prop function, this example still doesn't work, since by the looks of things, the draggable component is not respecting your offset.
I realized that at this point, the problem is the internal state of the component is out of sync with your state. And would you look at that, the Framer Motion API actually provides a way to inspect this.
https://www.framer.com/api/motion/motionvalue/#usemotionvalue
It's the hook useMotionValue() which allows us to see what's actually happening. Turns out, our value is being set wrong when the user starts dragging:
useEffect(
() =>
motionX.onChange((latest) => {
console.log("LATEST: ", latest);
}),
[]
);
We can see this, because the state "jumps" to 200 as soon as we start dragging.
So fixing in theory is easy, we just need to make sure to let that value "know" about our offset, and that way it's gonna start with the proper offset in mind!
Anyway, that was my thought process, here's the solution, all you need to do is set the left constraint to make it work:
dragConstraints={{
top: 0,
left: position,
right: 0,
bottom: 0
}}
And tada! This makes it work. Here's my working solution: https://codesandbox.io/s/lingering-waterfall-2tsfi?file=/src/App.tsx

GSAP ScrollTrigger choppy with custom scroll value

I am experimenting with gsap's ScrollTrigger. I have a custom scroll container and want to use ScrollTiggers scroller proxy to hijack the scroll. The results are very choppy though. Am I doing something wrong? Here is an example of what I have so far. CodeSandbox
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import scrollCtl from "./scrollCtl";
gsap.registerPlugin(ScrollTrigger);
const ctl = new scrollCtl();
ctl.on("scroll", () => ScrollTrigger.update);
ScrollTrigger.scrollerProxy(".container", {
scrollTop(value) {
return ctl.event.scroll; // getter
},
getBoundingClientRect() {
return {
top: 0,
left: 0,
width: window.innerWidth,
height: window.innerHeight
};
}
});
gsap.to(".test", {
scrollTrigger: {
trigger: ".trigger",
scroller: ".container",
scrub: true,
start: "top bottom",
end: "top top",
markers: true
},
scale: "1.5",
ease: "none"
});
As you can see from the codesandbox demo the scroll is very smooth but the markers are bouncing all over the place and the green square is supposed to scale up smoothly but it is bouncing around when you scroll instead of smoothly scaling. There are demo's from other libraries that achieve a very smooth effect like this example ScrollTigger Locomotive Scroll. Here is the documentation page where you can find more examples ScrollTrigger ScrollProxy() I can't figure out why mine is so janky.
Here's the problem:
// BAD
ctl.on("scroll", () => ScrollTrigger.update);
// GOOD
ctl.on("scroll", () => ScrollTrigger.update());
You just forgot to actually call the ScrollTrigger.update() method :)
In the future, it might be worth asking in the GreenSock forums - we're pretty quick to respond there and it's a community totally dedicated to answering GreenSock-related questions.
Happy tweening!

Making text fade in/out on button click in React

I have a random quote generator written in React: https://codepen.io/1000mileworld/pen/RwNMwKE
When I click the new quote button, I would like the previous quote to fade out and the new quote to fade in.
I was trying to use some jQuery that changed the opacity to 0 before setting new state and then return opacity back to 1 afterwards. I probably have to do this outside handleClick() somewhere but not sure how.
handleClick() {
$("#text").animate({ opacity: 0 }, 500);
const quote = getRandomQuote();
this.setState({
text: quote.text,
author: quote.author
});
$("#text").animate({ opacity: 1 }, 500);
}
In general it's not recommended that you query the DOM (ie using selectors to pick an element to call $.animate() on) when rendering those elements with React.
Perhpas you could consider another approach that by-passes JQuery all together and instead uses CSS transitions and React to achieve the equvilant fade transition?
Something like this might work for you:
JSX
const FADE_DURATION = 1000;
class Application extends React.Component {
constructor(props){
super(props);
this.state = {
text: quote.text,
author: quote.author,
// Add extra state to control fading
fadeTransition : null,
fadeState : "fade-in",
}
this.handleClick = this.handleClick.bind(this)
}
handleClick(){
// Create a timer that runs after quote
// fades out
const timeout = setTimeout(() => {
// After current quote faded out, fetch
// next quote and display
const quote = getRandomQuote();
this.setState({
text: quote.text,
author: quote.author,
// Apply additional state to control
// fading transition
fadeTransition : null,
fadeState : 'fade-in'
});
}, FADE_DURATION);
// Stop any existing transition
clearTimeout(this.state.fadeTransition);
// Update state to perform the fade out from
// current quote
this.setState({
fadeState : 'fade-out',
fadeTransition : timeout
});
}
render() {
return (
<div className={`fade-wrapper ${this.state.fadeState}`}
style={{ transitionDuration: `${FADE_DURATION}ms`}}>
<Display text={this.state.text}
author={this.state.author}
click={this.handleClick} />
</div>
);
}
}
CSS
/* Add this to existing css */
.fade-wrapper {
transition:opacity ease-in-out;
}
.fade-out {
opacity:0;
}
.fade-in {
opacity:1;
}
Here's a working example (sorry the CSS is a little broken) - hope that helps!
You can use the callback from animate, and set the state there. Something like:
$( "#txt" ).animate({
opacity: 0,
}, 2000, function() {
that.setState({text:"world"});
$( "#txt" ).animate({
opacity: 1,
}, 2000, function() {
// Animation complete.
});
You'll need to be aware that the scope of 'this' will need to be handled within the callback. I just did that=this in the example just to demonstrate the usage of the callback.
https://jsfiddle.net/pythagoras2357/8zarn94j/9/
https://api.jquery.com/animate/

How to use Ember Liquid-Fire to transition between two elements with different font sizes

I'm using Ember and Liquid Fire to create some interesting material-design-inspired animated transitions between routes.
I've created two transitions. The first occurs during the transition from the index route to the about route and uses the explode, flyTo, and toLeft transitions successfully. Here I match by a data-nav attribute on both routes to create a smooth, seamless transition that makes the matched element appear to move across the screen to the next page as it flies left. Thumbs up!
The second occurs during the transition from the about route back to the index route. Here I match a different element than above by a data-text attribute on both routes, however unlike the example above, the elements are not identical. Specifically, the font-size is different. Unfortunately this has an undesired effect of immediately displaying the larger font-size text and then having it fly across the page.
What I'd like to do is add an additional transition that animates the font-size. Sounds simple enough.
Below is my initial transitions.js file with the two transitions described above.
export default function() {
var duration = 1000;
this.transition(
// --------------------- INDEX to ABOUT ----------------------------------- //
this.fromRoute('index'),
this.toRoute('about'),
this.use('explode', {
matchBy: 'data-nav',
use: [ 'flyTo', { duration } ]
}, {
use: [ 'toLeft', { duration } ]
}),
// --------------------- ABOUT to INDEX ----------------------------------- //
this.reverse('explode', {
matchBy: 'data-text',
use: [ 'flyTo', { duration } ]
}, {
use: [ 'toRight', { duration } ]
})
);
}
I believe the answer is to create my own custom transition, however it is turning out to be rather challenging. I created a custom transition called scale-font.js and added it to my transitions.js file for this transition.
import { animate, Promise } from "liquid-fire";
export default function scaleFont(opts={}) {
// returns a Promise that resolves when the transition is done
if (!this.newElement) {
return Promise.resolve();
} else if (!this.oldElement) {
this.newElement.css({visibility: ''});
return Promise.resolve();
}
var oldFontSize = this.oldElement.css('font-size');
var newFontSize = (opts.selector ? this.newElement.find(opts.selector) : this.newElement).css('font-size');
this.newElement.css({ visibility: 'hidden' });
return animate(
this.oldElement,
{ fontSize: oldFontSize },
{ duration: 1000 }
).then(() => {
return animate(
this.newElement,
{ fontSize: newFontSize },
{ duration: 1000, visibility: 'visible' }
);
});
}
Unfortunately this doesn't quite work. The first problem is that the oldFontSize is not correct. It ends up grabbing the new font size. The second problem is no font scaling occurs.
I've researched animating fonts, so I am fairly certain it can be done. Suggestions?

Categories