When my sidebar transitions to width: 0, the content right next to it (on its right) doesn't slide with it. It's like the text waits for the sidebar to be done with its animation before it takes the sidebar's place, even though I set its transition as well.
I came up with a minimal reproducible example below:
//Sidebar.js
import './styles/Sidebar.css'
export const Sidebar = () => {
const [show, setShow] = useState(false);
const toggle = ()=>{
setShow(!show);
}
return (
<div>
<div id={'toggle-btn'}>
<button type='button' className='toggle-btn' onClick={toggle}>
toggle
</button>
</div>
<div style={{display:"flex"}}>
<aside className={'sidebar' + (show ? ' showSidebar':'')}>
<ul className='menuList'>
<li>Perfil</li>
<li>Estatísticas</li>
<li>Adicionar Itens</li>
<li>Procurar</li>
</ul>
</aside>
</div>
</div>
)
}
/*Sidebar.css*/
.sidebar {
width:100%;
overflow: hidden;
box-shadow: 0 8px 8px -4px ;
transform: translateX(0);
transition:all 1s ease;
height:100vh;
}
.showSidebar {
width:0;
}
//Dashboard.js
import './styles/Dashboard.css'
export const Dashboard = () => {
return (
<div className='dashboard'>
<p>
LORE IPSUM BLA BLA
</p>
</div>
)
}
/*Dashboard.css*/
.dashboard {
max-width: 30%;
margin-top:10rem;
transition:all 1s ease;
}
//App.js
function App() {
return (
<div style={{display:"flex"}}>
<Sidebar />
<Dashboard />
</div>
);
}
export default App;
When you change the Sidebar's width from 100% to 0, it simply is taken out of the content flow, and Dashboard then is reposition to left. To make Sidebar and Dashboard transition together while one of the two has width change, you need to establish a relationship between the two component's widths.
Please refer to this CodeSandbox example I put together for you.
In it, I set up a global CSS variable like below:
/* styles.css */
:root {
--sidebar: 150px;
}
And use it in both Sidebar and Dashboard like below:
/* Sidebar.css */
.sidebar {
width: var(--sidebar);
/* no other changes */
}
/* Dashboard.css */
.dashboard {
width: calc(100% - var(--sidebar));
/* no other changes */
}
With the above, whenever Sidebar's width changes, it'll reflect the value in Dashboard's width, making both transition in sync.
Related
I'm building a personal portfolio webpage with React and I'm trying to update the content of the div element but when the elements are updated, I want to involve styling, (like the old content to exit on the left and the new content to enter from the left) Sorry for my bad English.
This is the part I'm trying to update and add styling
I've tried to use the componentDidMount method with a function that changes the heading but I don't know how to add animations/styling...
export default class Home extends Component {
skills_section_update = () => {
let heading = document.getElementById('heading');
const headings = [
{
head: 'Web Development'
},
{
head: 'Game Development'
}
];
const update_head = (index) => {
heading.innerText = headings[index].head;
};
update_head(1);
}
componentDidMount = () => {
setInterval(this.skills_section_update, 2000);
};
I'm new to React so any help would be much appreciated.
:)
I don't know if there are special methods to do this using React but I know that you can't animate the content of a DOM element using CSS. What I would recommend is to add the updated content in another DOM element next to your current div and then animate the two to make a nice transition.
You could, for example, do something like that:
.container {
width: 400px;
height: 400px;
border: #000 solid 1px;
position: relative;
overflow: hidden;
}
.content {
width: 100%;
height: 100%;
transition: all .25s ease-in-out;
}
.container:hover .content {
transform: translateY(-100%);
transition: all .25s ease-in-out;
}
.original {
background-color: wheat;
}
.next {
background-color: teal;
}
<div class="container">
<div class="content original">
Content 1
</div>
<div class="content next">
New content
</div>
</div>
How do I make a div expand smoothly when content is added?
const [render, setRender] = useState(false)
const showHide= () => {
setRender(!render)
}
return (
<div className="container">
<h1>TEST CONTAINER</h1>
{render && <Paragprah />}
<button type="button" class="btn btn-primary" onClick={showHide}>Primary</button>
</div>
);
}
CSS
.container {
margin-left: 30%;
margin-top: 10%;
width: 500px;
height: auto;
background-color: whitesmoke;
}
In the above, a component is being rendered when a button is clicked, when the new content is added to the div, it expands instantly. basically, I would like it to expand smoothly when the content is added. Here is a video as well by what I mean when it expands instantly.
Setting a fixed height wont work in my situation because the content that's being loaded is dynamic, its coming from an API and the length is different everytime. So setting a fixed height will make the content overflow sometimes. I need a way where the transition can occur smoothly and the height still be large enough to fit the content, which would probably require ```height: auto;`` to be present?
Link to Imgur video
you can use the transition css property for a smooth transition.
transition: height 2s;
Follow this link for reference.
Here I updated the code. Since the content that's being loaded has dynamic size then you have to track that content size using useRef hook and store it on a variable and export it with your component.
import { useRef, useEffect} from "react";
var ParagraphHeight;
function Paragraph() {
const ref = useRef(null);
useEffect(() => {
//when you content is loaded store its height
ParagraphHeight = ref.current.offsetHeight;
}, []);
return (
<div ref={ref}>
<p>Your content here for exemple...</p>
</div>
);
}
export { Paragraph, ParagraphHeight };
Now import your Paragraph component and put it inside a .wrapper div that has a default height of 0px (that means your Paragraph will be hidden) and when you click on the button the height of it will change to the ParagraphHeight
import { useState } from "react";
import { Paragraph, ParagraphHeight } from "./Paragraph.js";
export default function App() {
const [isopned, setisopned] = useState(false);
const [wrapperHeight, setWH] = useState(0);
const showHide = () => {
if (!isopned) setWH(ParagraphHeight);
else setWH(0);
setisopned(!isopned);
};
return (
<div className="container">
<h1>TEST CONTAINER</h1>
<div className="wrapper" style={{ height: wrapperHeight }}>
<Paragraph />
</div>
<button type="button" className="btn btn-primary" onClick={showHide}>
Primary
</button>
</div>
);
}
Finally don't forget to update your css, add overflow-y hidden and css transition to to expand your wrapper div smoothly.
.container {
margin-left: 30%;
margin-top: 10%;
width: 500px;
background-color: whitesmoke;
}
.wrapper {
display: grid; /*to prevent some problem caused by overflow hidden*/
transition: height 1s linear;
background-color: wheat;
overflow-y: hidden;
}
This is a live exemple demo
I'm trying to make it so that a box would expand (in width and height) and transition from its origin to the center of a screen upon being clicked. Here's what I have so far:
I'm running into two problems here -- when I click on the box, the DOM automatically shifts, because the clicked element has its position changed to 'absolute'. The other problem is that the box doesn't transition from its origin, it transitions from the bottom right corner (it also doesn't return to its position from the center of the screen, when make inactive is clicked).
What am I doing wrong here?
import React from "react";
import styled from "styled-components";
import "./styles.css";
export default function App() {
const [clickedBox, setClickedBox] = React.useState(undefined);
const handleClick = React.useCallback((index) => () => {
console.log(index);
setClickedBox(index);
});
return (
<Container>
{Array.from({ length: 5 }, (_, index) => (
<Box
key={index}
active={clickedBox === index}
onClick={handleClick(index)}
>
box {index}
{clickedBox === index && (
<div>
<button
onClick={(e) => {
e.stopPropagation();
handleClick(undefined)();
}}
>
make inactive
</button>
</div>
)}
</Box>
))}
</Container>
);
}
const Container = styled.div`
display: flex;
flex-wrap: wrap;
align-items: center;
height: 100vh;
`;
const Box = styled.div`
flex: 1 0 32%;
padding: 0.5rem;
cursor: pointer;
margin: 1rem;
border: 1px solid red;
transition: 2s;
background-color: white;
${({ active }) => `
${
active
? `
position: absolute;
width: 50vw;
height: 50vh;
background-color: tomato;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
`
: ""
}
`}
`;
With CSS
Wery unlikely you can achieve that with plain css. And for sure impossible to achieve a versatile solution.
You have a dynamic size and position to adopt to (starting div)
You have to adapt to the current scrolling position
If you remove the div from the layout is almost impossible to avoid screwing up the layout (even if you can, there will always be some edge case).
transition from a relative to a fixed position.
With the current css standard is impossible to perform these things.
With JS
The solution is to do some javascript magic. Since you are using React i developed you a solution using react-spring (an animation framework). Here you have a wrapping component that will do what you want:
The complete SandBox
import React, { useEffect, useRef, useState } from "react";
import { useSpring, animated } from "react-spring";
export default function Popping(props) {
const cont = useRef(null);
const [oriSize, setOriSize] = useState(null);
const [finalSize, setFinalSize] = useState(null);
useEffect(() => {
if (props.open && cont.current) {
const b = cont.current.getBoundingClientRect();
setOriSize({
diz: 0,
opacity: 0,
top: b.top,
left: b.left,
width: b.width,
height: b.height
});
const w = window.innerWidth,
h = window.innerHeight;
setFinalSize({
diz: 1,
opacity: 1,
top: h * 0.25,
left: w * 0.25,
width: w * 0.5,
height: h * 0.5
});
}
}, [props.open]);
const styles = useSpring({
from: props.open ? oriSize : finalSize,
to: props.open ? finalSize : oriSize,
config: { duration: 300 }
});
return (
<>
<animated.div
style={{
background: "orange",
position: "fixed",
display:
styles.diz?.interpolate((d) => (d === 0 ? "none" : "initial")) ||
"none",
...styles
}}
>
{props.popup}
</animated.div>
<div ref={cont} style={{ border: "2px solid green" }}>
{props.children}
</div>
</>
);
}
Note: This code uses two <div>, one to wrap your content, and the second one is always fixed but hidden. When you toggle the popup visibility, the wrapping div gets measured (we obtain its size and position on the screen) and the fixed div is animated from that position to its final position. You can achieve the illusion you are looking for by rendering the same content in both <div>, but there is always the risk of minor misalignment.
The idea is similar to what newbie did in their post but without any extra libraries. I might have done some things a bit non-standard to avoid using any libraries.
CodeSandbox
import React from "react";
import { StyledBox } from "./App.styles";
export const Box = (props) => {
const boxRef = React.useRef(null);
const { index, active, handleClick } = props;
const handleBoxClick = () => {
handleClick(index);
};
React.useEffect(() => {
const b = boxRef.current;
const a = b.querySelector(".active-class");
a.style.left = b.offsetLeft + "px";
a.style.top = b.offsetTop + "px";
a.style.width = b.offsetWidth + "px";
a.style.height = b.offsetHeight + "px";
});
return (
<StyledBox active={active} onClick={handleBoxClick} ref={boxRef}>
box {index}
<div className="active-class">
box {index}
<div>
<button
onClick={(e) => {
e.stopPropagation();
handleClick(undefined);
}}
>
make inactive
</button>
</div>
</div>
</StyledBox>
);
};
import styled from "styled-components";
export const StyledContainer = styled.div`
display: flex;
flex-wrap: wrap;
align-items: center;
height: 100vh;
`;
export const StyledBox = styled.div`
flex: 1 0 32%;
padding: 0.5rem;
cursor: pointer;
margin: 1rem;
border: 1px solid red;
background-color: white;
.active-class {
position: absolute;
transition: 0.3s all ease-in;
background-color: tomato;
z-index: -1;
${({ active }) =>
active
? `
width: 50vw !important;
height: 50vh !important;
top: 50% !important;
left: 50% !important;
transform: translate(-50%, -50%);
z-index: 1;
opacity: 1;
`
: `
z-index: -1;
transform: translate(0, 0);
opacity: 0;
`}
}
`;
first, transition with position or display don't work on css(it can work but without transition).
here you have:
flex: 1 0 32%;
that is equivalent to :
flex-grow: 1;
flex-shrink: 1;
flex-basis: 32%;
so, when active is true, width would jump to 50vw and height to 50vh but roughly without transition. so the solution is to use scale like this:
z-index: 99;
transform: scaleY(5) scaleX(2) translate(20%, 20%);
background-tomato: tomato
and you need to tweak the values of scaleY, scaleX and translate (for each Box) until you get it to work.
you can take a look at what i did in this codesandbox: https://codesandbox.io/s/peaceful-payne-ewmxi?file=/src/App.js:1362-1432
here is also a link if you want master flex: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Controlling_Ratios_of_Flex_Items_Along_the_Main_Ax
be sure that all your items have the following css properties : transform: translateX(0) translateY(0); transition: transform Xms timing-function-of-your-choice, left Xms timing, top Xms timing;
Until your page is completly loaded, let all the item in your page have the css property : position: static.
When page loads, retrive the items' properties : x offset from left of screen, y offset from top of document, width and height.
Use javascript to change the items properties : set position to fixed and affect the left, top, width and height css properties from the values we just retrieved. this way, the items will keep their exact position after their position property changes.
with javascript, when the box is clicked on, to center it inside your page, just apply the following css properties via javascript : left: 50%; top: 50%; transform: translateX(-50%) translateY(-50%); width: the width of your choice; height: the height of your choice;
This way, your item will move in the center of your screen no matter what their width and origin offset were. Also, the transition will be very smooth.
You might also want to change the z-index of an item when it is clicked.
I am trying to make a slider carousel with VUE, the css classes (3 divs) are running in a for loop. Anytime a div fades out, the next slider creates a double slider at the bottom, meaning two sliders are running concurrently.
an example of the problem
Whenever I use the relative and absolute properties, my divs disappear totally. I don't know what to do
<template>
<h1 class="text-center">SLIDER APP</h1>
<div>
<div v-for="(color, index) in slider" :key="color">
<transition name="fade">
<div v-if="currentslide == index" :class="color"></div>
</transition>
</div>
</div>
</template>
<script>
export default {
data(){
return {
currentslide:0,
intervals:'',
slider:['first-slider', 'second-slider', 'third-slider'],
isshowing:true,
}
},
mounted(){
this.intervals = setInterval(() => {
console.log('This is slide', this.currentslide)
this.currentslide = this.currentslide == 2 ? 0:this.currentslide+1;
}, 2000);
},
beforeUnmount(){
clearInterval(this.intervals)
}
}
</script>
<style>
.first-slider {
background: blue;
height: 350px;
}
.second-slider {
background: red;
height: 350px;
}
.third-slider {
background: orange;
height: 350px;
}
.fade-enter-active, .fade-leave-active {
transition: all 0.5s ease;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
transform: translateX(30px);
}
</style>
Stop using absolute or relative positioning. Instead use display flex on an outer div for good positioning results.
Is there anyway to have a React Native navigation transition by fading to black, then fade out from black to the next screen?
Have been googling for awhile, I have only found methods of changing the screens opacity, or moving it from left to right or right to left, But I haven't found a transition from a screen to black. Any help would be much appreciated.
If you are using latest version of react-navigation-stack, you can use CardAnimationContext/useCardAnimation to achieve this:
import * as React from 'react';
import { Animated, Button, View, StyleSheet } from 'react-native';
import { createAppContainer } from 'react-navigation';
import {
createStackNavigator,
CardStyleInterpolators,
HeaderStyleInterpolators,
useCardAnimation,
} from 'react-navigation-stack';
function ScreenA({ navigation }) {
return (
<View style={{ flex: 1 }}>
<Button onPress={() => navigation.push('ScreenB')} title="Go to B" />
</View>
);
}
function ScreenB() {
const { current } = useCardAnimation();
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Animated.View
style={[
StyleSheet.absoluteFill,
{ backgroundColor: 'black', opacity: current.progress },
]}
/>
</View>
);
}
const Stack = createStackNavigator({
ScreenA,
ScreenB,
});
Just place a black <div> behind that screen and
Thanks #Prajwal, CSS transition solution:
document.getElementsByTagName(`main`)[0].style.opacity = `0`;
main {
position: fixed; /* place it before .black */
background-color: white;
width: 100%;
height: 100%;
transition-property: opacity; /* set opacity to one of the transition properties */
transition-duration: 4s; /* set transition duration */
}
.black {
position: fixed; /* place it behind main */
background-color: black; /* make it black */
width: 100%; /* make it fill available space */
height: 100%; /* make it fill available space*/
}
body {
margin: 0; /* make it fill available space*/
}
<body>
<div class="black"></div>
<main>
<p>lorem</p>
</main>
</body>
You can also use the setInterval() function to lower the opacity bit by bit.
const step = 0.05; // set the step of opacity change
const timeInterval = 200; // set the sleep time of opacity change
const times = 1 / step; // calculate times needed for opacity change
document.getElementsByTagName(`main`)[0].style.opacity = `1`; // set initial opacity to make getting the float value of it work.
let time = 0; // initially, the time of making changes is zero
const lowerOpacity = window.setInterval(function() {
if (time + 1 === times) {
window.clearInterval(lowerOpacity);
} // clearInterval() when the time of changes has reached the needed times
document.getElementsByTagName(`main`)[0].style.opacity =
`${parseFloat(document.getElementsByTagName(`main`)[0].style.opacity) -
0.05}`; // set the opacity to make it dimmer
time += 1; // increment time to match the changed times
}
, timeInterval);
main {
position: fixed; /* place it before .black */
background-color: white;
width: 100%;
height: 100%;
}
.black {
position: fixed; /* place it behind main */
background-color: black; /* make it black */
width: 100%; /* make it fill available space */
height: 100%; /* make it fill available space*/
}
body {
margin: 0; /* make it fill available space*/
}
<body>
<div class="black"></div>
<main>
<p>lorem</p>
</main>
</body>