I'm using React TransitionGroup and Velocity to animate transitions between routes.
I'm trying to do a simple animation where the destination route component slides in from the left and the source route slide outs the right.
I have that animation running now, but they don't get aligned vertically. i.e. the new component gets added to the dom on top of the old one so we get what is in the screenshot below. This is when we navigate from Edit to Preview. The Form header is part of Edit and gets pushed to the bottom as Preview comes in.
Here is what the DOM looks like as the animation is running.
I'm sure I just need some css to achieve this, but not sure how to go about it.
example of Preview code:
import React from 'react';
import ReactDom from 'react-dom';
import Velocity from 'velocity-animate';
import 'velocity-animate/velocity.ui';
class Preview extends React.Component{
componentWillEnter(callback){
const el = ReactDom.findDOMNode(this);
if (!el) {
console.error('not el!', el);
}
Velocity(el, 'transition.slideLeftBigIn', {duration: 1000, complete: callback});
}
componentWillLeave (callback) {
const el = ReactDom.findDOMNode(this);
console.log('otw out')
Velocity(el, 'transition.slideRightBigOut', {duration: 1000, complete: callback});
}
render(){
return (
<div>
<h2>Preview</h2>
<img src=""/>
</div>
);
}
}
Transition Group defintion in App.jsx
<TransitionGroup>
{React.cloneElement(this.props.children, {
key: Math.random()
})}
</TransitionGroup>
Your instinct to solve this with CSS was right on. The cause is elements that have display: block and `position: static' will stack up vertically in source order (all other things equal).
https://jsfiddle.net/ez10xbrv/
You can change the animating components to have position: absolute and their container to have position: relative so they don't leave space for previous siblings. Note that if you go this route, your container (the <span> in your example) will not have any natural size so you may need to assign it a height and width. CSS Tricks has some good information on positioning that might help.
https://jsfiddle.net/ez10xbrv/1/
Related
As much as I have searched about file sizing for react-file-viewer I could not find anything.
I want to use the react-file-viewer to click on a filename hyperlink and open the file (image, document or excel sheeet) in a new page. The rendering works fine, except for the image/document sizing.
I have the following example:
import React from "react";
import FileViewer from "react-file-viewer";
import { Fragment } from "react";
import imGurPic from "./MainBody/imGurPic.ts";
const MainBody = () => {
const file =imGurPic;
const type = "jpeg";
return (
<Fragment>
<FileViewer fileType={type} filePath={file} />
</Fragment>
);
};
export default MainBody;
The imGurPic is an image I picked randomly from imGur because of its large size (3024x4032 pixels) (don't worry it is a cat image... link here... I converted into a base64 string that I use in the filePath prop of the FileViewer component. Ultimately, it will be a base64 string coming from a db as a byte array.
In the following sandbox I managed to create a demo, only to find out that it is WAY too small (72*96px). I do not really understand why it would take so little space. Also, any document or excelsheet I enter, the maximum height is 96px. How can I change it? It seems to inherit from a parent element but the Mainbody takes all the available space between header and footer.
Any help on this will be appreciated.
Here is the sandbox -->sandbox demo
And in case someone cannot open it, here is a screenshot -->
Had to figure this out as well. If you want your image to scale to fit your sizing requirements, just make height=100% in the surrounding div. E.g.
<div style={{ height: '100%' }}>
<ReactFileViewer />
</div>
If you don't want the image scaled at all, then it's a little trickier. I've had to resort to some messy CSS to override the hardwired width and height settings:
.pg-viewer-wrapper {
overflow-y: unset !important;
}
.photo-viewer-container {
width: unset !important;
height: unset !important;
}
.photo-viewer-container > img {
width: unset !important;
height: unset !important;
}
It would be great if this component had features like scaling (e.g. fit, fill, percentage) but I don't think the library is being maintained any more (looking at the old PRs that are still waiting), so would recommend forking and implementing a cleaner solution than what I have done.
I am trying to use a functional component and React hooks to implement a simplified auto-scroller that will automatically scroll a container to the bottom when the child content overflows. But the auto-scrolling should only happen when the scrollbar is already enar the bottom (e.g., if the user has scrolled up to look at output, the scroll position should not change when new content comes in).
I know how to implement the auto-scrolling behavior by using refs and performing computation on clientHeight, scrollTop, and scrollHeight.
The problem I have is that I need to compute a shouldAutoScroll() check BEFORE the component is re-rendered.
My flow needs to look like this:
<container>
{props.children}
</container>
When props.children changes:
1. Check if the scrollbar is near the bottom and store the result
2. Update container to reflect the new props.children
3. If the check from step 1 is true, scroll to the bottom
I can't seem to find a way to to this using useEffect and/or useLayoutEffec. When using these what happens is:
1. Scroll position is at bottom
2. props.children updates with new items
3. <container> is rerendered, pushing the scrollbar up
4. The checkScrollBarBottom() method is called and returns false
5. The scrollbar is not auto-scrolled
I need to keep the component generic, so that it can auto scroll regardless of what type of component or element props.children is. In some cases the change to props.chldren might be a single line. In others, it might be 20 lines, or it might be an image.
If I were using an older-style class component, I could do the computation in componentWillReceiveProps(). How do I replicate this with hooks?
I discovered one solution that works, but seems a little messy.
The solution is to calculate and update shouldAutoScroll() during the container's onScroll() event. This seems messy because I am capturing a ton of useless intermediate scroll information, when all I care about is the scroll position at the moment the update starts (but before the component is re-rendered).
Full code:
import React, { useRef, useEffect, useLayoutEffect } from 'react';
import styles from './AutoScroller.module.scss';
export function AutoScroller({children, className=''}) {
const classNames = `${styles.default} ${className}`;
const containerRef = useRef(null);
const shouldAutoScroll = useRef(false);
function updateShouldAutoScroll(element, tolerance) {
const {scrollHeight, scrollTop, clientHeight} = element;
const result = scrollHeight - scrollTop <= clientHeight + tolerance;
shouldAutoScroll.current = result;
}
function onContainerScroll(e) {
updateShouldAutoScroll(e.target, 25)
}
useEffect(function autoScroll() {
if (shouldAutoScroll.current) {
const element = containerRef.current;
element.scrollTop = element.scrollHeight;
}
});
return (
<div className={classNames} ref={containerRef} onScroll={onContainerScroll}>
{children}
</div>
)
}
I am wondering if it is possible to debounce a jsx element's rendering. I have an animation of a panel expanding that has some content in it. If the panel is empty (just the background) the animation is smooth and works as expected. The animation and associated components are all from Material-UI. The issue arises when the content is in the panel already so the animation (width expansion) just skips out to the width of the content making the animation look choppy. Here is a similar example to what I am referring to. This code is my code I am using in my panel and they work the same in terms of the expansion. Only difference is the content in this example is just lorem ipsum so the animation appears to work fine. Is it possible to debounce the <CardContent /> component's rendering like this?
{ open &&
(
_.debounce(e => {
return (
<CardContent>
{/*content in here*/}
</CardContent>
)
}, 300)
)
}
or something similar (this doesn't work) so the panel will be fully expanded through the animation before the content is rendered?
I'd like to add a comment however my reputation isn't above 50 yet so unfortunately I can't :p.
JS Animation
To answer your question, if you are using JavaScript animations like GSAP or AnimeJS you can have a state flag called. isAnimating = null. Then when your animation starts set the flag to true and when it's finished set it to false. Then in your render() method write a conditional render like this.state.isAnimating === false && <CardContent />.
CSS Animation
If you are using CSS animation you can use the transitionend event to wait for the animation to end. Therefore you have to create a ref on the animating DOM element to which you can attach the event.
Does that help you a bit? Please let me know if you have any other questions.
Here is a solution using react spring library
I added a state variable in order to display or not the card content
const [contentVisible, setContentVisible] = React.useState(false);
I created a spring to handle the width transition, I set contentVisible to true as soon as the width approaches the 300
const { width } = useSpring({
width: open ? 300 : 120,
onFrame: ({ width }) => setContentVisible(width > 299.8)
});
Finally, I created an animated card component
const AnimatedCard = animated(Card);
...
<AnimatedCard classes={{ root: classes.card }} style={{ width }}>
I am trying to find a solution to modify the height of the div for react based on how many elements it will consist to grow automatically.
So, to do the same i have chosen jquery
ul.each(function() {
const self = $(this);
const { listCount, ulCount } = getListItemsCount(self);
const ruleHeight = listCount * listDefaultHeight;
const children = $(self).children();
/* Swapping of elements to adjust the heights */
if (children.length - 1 === 2 && ulCount === 1) {
if ($(children[2]).data('count') > $(children[1]).data('count')) {
$($(self).children()[2]).insertBefore($($(self).children()[1]));
//ruleHeight += 25;
}
}
$(self)
.find('div')
.css({ height: `${ruleHeight}px` });
});
The above code is happening inside componentDidMount(). The reason I am doing here, we are not sure how much height we need to increase as the div position is absolute and also depending upon the first level content, we are swapping the divs as well as a high-level overview.
The blue and ping is an absolute div whose height is growing accordingly, if the first level data is less and second level has nested and more list items, the height was not adjusting, so swapping the divs and its working.
The question here is: Is it the right approach to handle this with jquery?
React DOM is not updated now? How to update the react dom as well, if suppose render method is called, will the jquery code is written become obsolete?
Is there a possibility to adjust the height using flex automatically without using jquery for absolute positions or by just css will be awesome fix?
Please guide the best practice to do the same
I would suggest do not use jquery instead , write a function to calculate height of the div in componentDidMount and set the height of the div in render method.
Here is an example
componentWillMount() {
this.setDivHeight();
}
return () {
render(
<div style={{height: this.state.divHeight}}>
</div>
)
}
This just for reference, you have to call a function to set change value of state divHeight to re render the component and the div height will also change on each render. Also would suggest user in JSX to code your components. Tips about JSX
I'm struggling with react-spring to fade out a loading screen and then unmount it.
The component unmounts but without animation and I can't figure why. I created a sandbox to illustrate:
https://codesandbox.io/s/nkxjxwo2xl
import React from 'react'
import ReactDOM from 'react-dom'
import { Transition } from 'react-spring'
class Loader extends React.PureComponent {
state = { loaded: false }
componentDidMount() {
setTimeout(() => {
this.setState({ loaded: true })
}, 1000)
}
render() {
const { percentage } = this.props
const styles = {
height: '100vh',
width: '100vw',
background: 'tomato'
}
return (
<Transition native from={{ opacity: 1 }} leave={{ opacity: 0 }}>
{!this.state.loaded &&
(style => (
<div style={Object.assign({}, styles, style)}>
<div>{Math.round(percentage)} %</div>
</div>
))}
</Transition>
)
}
}
ReactDOM.render(<Loader percentage={0} />, document.getElementById('root'))
There are no react-spring tags yet if anyone could create one, I think it would be helpful.
Yep, you fixed it yourself, the animated.div component was missing. I would recommend the use of it, though. Even if your view is small, it will still be rendered out 60 times per second by React otherwise (meaning it will go through all component phases 60 times + render). With native set it renders once and the animation will be applied in a requestAnimationFrame-loop directly in the dom (via instance.style.setProperty, it completely skips React - which makes a difference once your app gets bigger.
I found the solution if anyone finds this question.
Using the native attribute, you'll need to use animated.div (or any animated component) in order to have it animated. Or simply remove the native attribute. In my case, it is a loader which is not often displayed so I went for the easier way by simply removing the native attribute.
More info on the Spectrum react-spring community