Description
I'm creating the following app logic:
There is a list of checkable items with a specific price for each.
There is a calculated total price for checked items.
I'm also animating the number change for the total price using the requestAnimationFrame.
So far so good, the number animation itself works fine.
Problem
However, my TransitionGroup move transition starts to lag when I have the following element structure:
<Transition>
<div>
{{ animatedNumber }}
<TransitionGroup>
...
</TransitionGroup>
</div>
</Transition>
Notes
Seems that there is some conflict between the Transition elements and requestAnimationFrame.
The TransitionGroup move animation works fine if I remove the parent Transition element.
The TransitionGroup move animation works fine if I remove the number counter based on requestAnimationFrame.
This issue happens only on Chrome.
Demo
https://stackblitz.com/edit/vitejs-vite-a4hnef?file=src/components/App.vue
To test the performance of your app. You can follow the following steps:
Open chrome dev tool -> performance -> click record -> make animation by checking to a checkbox -> click stop
The result should look like that:
Zooming into a task you will see:
As we can see each task is caused by the componentUpdateFn. That is because, in every animation frame, you update the variable and force Vue to re-render the template. It's too much work. To make your animation smooth, each animation frame should run in a 16ms time frame (60fps)
To solve your problem you should put in less work as possible in each animation frame by:
optimizing your animate function
if you want to update your view in every frame, you should do it by pure JS instead of using Vue
This is the optimal version of your code. I just use JS to update the animating number. It's not perfect but it much better than the original version.
// in the script. update the view by pure JS
watch(animatedTotalPrice, () => {
totalPriceElm.value.innerText = animatedTotalPrice.value;
});
// in the template, DON'T let Vue do the updating work
<h3 ref="totalPriceElm" v-once>{{ animatedTotalPrice }} USD</h3>
Related
I'm building a notes taking Vue app.
The note being edited is auto-saved every X secods.
Every time it auto saves, I want to show a small message on the bottom of the screen saying "Note Saved". This will fade in stay for 2 seconds and then fade out automatically.
I've done transitions in Vue before but not on-off transitions like this.
I can set a property linked to the element with v-if wrapped in a transition block. But then I need to change the property twice (true-false)? Would I need to set up a timer to wait the transition to be over before setting it to false again? Seems a bit hackish. What's the best way to approach this?
I think what you've described with v-if makes sense... you could change the property (or data) in a v-on:enter method on the related component. This would nicely isolate the behavior to the component and be pretty apparent to future-self.
A completely different approach would be defining a single animation with keyframes that transitions from hidden to display to hidden, but that might be somewhat confusing: what is the leave transition of an element with such an enter transition? Hmm.
I need to be able to animate drag and drop in my vertical list. I used vuedraggable, wrapped my list in a transition-group and it all worked sweet. Until I fetch new data from the server. Now because of the introduction of transition-group for a split second the old data and the new data live together in the DOM causing the expansion of the list and pushing subsequent div down and back up.
This is kind of expected per the docs:
the DOM operations for insertion and/or removal will be executed
immediately on next frame (Note: this is a browser animation frame,
different from Vue’s concept of nextTick).
Regardless of being able to drag and drop the element, if we were to fade in/fade out the new/old elements they will co-habitate for the time of the animation, which is not great as seen in this pen
Anyway to fade out, change the data, then fade in the new one, while maintaining the height of the list?
Note that without fade the problem is still obvious:
Pen of my issue: click the switch data button to see the issue.
Turns out it's pretty know issue. By reading through this thread and toying with this example, i was able to achieve something to my liking by using:
list-leave-active {
display: none;
}
Resulting Pen
A css fix may be to wrap the contents within a box of some height and set overflow hidden.
So, even when new elements co-exist the jump in scrollbar can be avoided.
I am using a framework called react-native-svg to draw SVG elements in a react native view.
My goal is that when I tap on the view (I use a PanResponder on the global view to get that), the element that is the closest to the tap changes.
I have everything that I need, except one thing: I need to be able to know the position of my svg elements, as well as their size, in order to find the one that was the closest to the tap event.
Now, I've tried most of the things available I'd say:
onLayout: those svg elements don't appear to be views and therefore don't call the onLayout callback.
this.refs.elem.measure: same as onLayout, they are not views therefore the measure function is undefined.
UIManager.measure(ReactNative.findNodeHandle(this.refs.elem), callback): returns 0 for all values, apart from the react-native-svg component Svg, but not for all the others: G, Path, Line, etc.
So I was wondering if there was any other solution or if I was doomed. :)
Do I have to change something in react-native-svg that would allow me to find these values or is there any solution ready for me?
Thanks!
I finally had to add some functionalities to react-native-svg so I could get the size of the different elements.
It's not very well done, but I may come back to it for a cleaner solution and submit a PR then.
Thanks.
I am playing with the <ListView> component. My screen is capable of displaying 9 rows from a data set of 32. However I see in the console that the ListView component triggers the renderRow function 32 times (basically it seems to render in the background all the items of my data set).
I was expecting that the function would be triggered only when a new row appears on screen.
Is this a normal behavior?
Or is there an option that can specify under what conditions the renderRow function should be triggered?
Thanks for your suggestions.
Paul
I figured it out. In fact the rows of the list are build ahead of time so that the scroll of the list remains as smooth as possible.
The default value (in version 0.1.8) is 1000 pixels.
My list items were 20px high. 32 items * 20px = 640px. The first ten were rendered and visible anyway so there were remaining 440px to be rendered.
You can override this default setting by using the scrollRenderAheadDistance attribute of <ListView> in order to optimize your rendering if needed.
https://facebook.github.io/react-native/docs/listview.html#scrollrenderaheaddistance
I posted this a week or so ago:
Position simplemodal modal on top of an existing div
and thought that I had solved my problem, but when the window is scrolled, the modal container moves.
I think I need to change it from fixed to absolute positioning, but when I change it in the script, the right side of the container lines up with the left side of the div (but it does stay in the same place vertically).
Here's what I'm doing now:
$('.slider-caption #large-spot-two').click(function (e) {
$('#basic-modal-content-two').modal({appendTo:"#slider1", autoPosition: false});
return false;
});
What's the best way for me to keep the modal container above the div, whether the page is scrolled or not?
Thanks,
Wendy
The plugin was created to be fixed (not move when the page is scrolled, etc.). Changing the CSS position works until the page is resized or scrolled, as you have found.
The code required to change that behavior would require more work that I currently have time for. You might try BlockUI, as it sounds like that would fit your needs better?