I am creating a carousel and there are 2 buttons Next and Previous. on clicking Next it should slide from left to right and on clicking Previous it should slide from right to left. (I don't want to use any plugin)
My React container:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { prevItem, nextItem, initItem } from '../actions/index';
import { bindActionCreators } from 'redux';
import CSSTransitionGroup from 'react-transition-group/CSSTransitionGroup';
class Carousel extends Component{
previous(){
this.props.prevItem();
}
next(){
this.props.nextItem();
}
componentDidMount(){
this.props.initItem();
}
renderItem(){
const {item} = this.props;
const webLink = `http://${item.link}`;
const transitionOptions = {
transitionName: 'slide',
transitionEnterTimeout: 1000,
transitionLeaveTimeout: 1000
};
return(
<CSSTransitionGroup {...transitionOptions}>
<div className="carousel__item" key={item.id}>
<img className="carousel__image" src={item.imageurl}/>
<div className="carousel__text">
<h3>{item.title}</h3>
<p>{item.synopsis}</p>
{item.link}
</div>
</div>
</CSSTransitionGroup>
)
}
render(){
return(
<div className="carousel">
{this.renderItem()}
<div className="carousel__prev" onClick={this.previous.bind(this)}>
<i className="fa fa-chevron-left"></i>
</div>
<div className="carousel__next" onClick={this.next.bind(this)}>
<i className="fa fa-chevron-right"></i>
</div>
</div>
)
}
}
function mapStateToProps(state){
return {item: state.item};
}
function mapDispatchToProps(dispatch){
return bindActionCreators({
initItem: initItem,
nextItem: nextItem,
prevItem: prevItem
}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Carousel);
Any my Style.css is:
.carousel{
width: 100%;
border: 1px solid #ccc;
margin-top: 100px;
position: relative;
}
.carousel__item{
width: 100%;
color: #fff;
position: relative;
}
.carousel__item img{
width: 100%;
background: rgba(0,0,0,.6);
}
.carousel__text{
text-align: center;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.carousel__text a:hover,
.carousel__text a{
color: #fff;
}
.carousel__prev{
position: absolute;
left: 0;
top: 50%;
color: #fff;
margin-left: 10px;
cursor: pointer;
font-size: 25px;
}
.carousel__next{
position: absolute;
right: 0;
top: 50%;
color: #fff;
margin-right: 10px;
cursor: pointer;
font-size: 25px;
}
.slide-enter{
transform: translate(100%);
}
.slide-enter-active{
transform: translate(0%);
transition: transform 1000ms ease-in-out;
}
.slide-leave{
transform: translate(0%);
}
.slide-leave-active{
transform: translate(-100%);
transition: transform 1000ms ease-in-out;
}
Apart from the direction, the current slide animation is not correct. when it slides the previous slide is showing below. So basically 2 slides are showing on screen on transition. Do you know how to fix this problem and also address the direction of slide?
I just worked through this problem.
In order to slide in the correct direction, you'll need to maintain some state and apply this state to your items via className. Something like this:
render () {
<CSSTransitionGroup
component={MenuWrapper}
transitionName="slide"
transitionEnterTimeout={500}
transitionLeaveTimeout={500}>
{this._renderMenu(this.props.selectedSubMenus[menuIdx], menuIdx)}
</CSSTransitionGroup>
}
_renderMenu = (menu, idx) => {
return (
<Menu key={idx} className={this.state.animationDirection} order={idx}>
content
</Menu>
)
}
Your css should look something like this (note that I'm using styled-components for a few elements):
const menuWidth = "256px";
const MenuWrapper = styled.div`
display: flex;
overflow: hidden;
max-width: ${menuWidth};
`
const Menu = styled.div`
min-width: ${menuWidth};
order: ${props => props.order};
`
.slide-enter.left {
transform: translate(-100%);
}
.slide-enter.slide-enter-active.left {
transform: translate(0%);
transition: transform 500ms ease-in-out;
}
.slide-leave.left {
transform: translate(-100%);
}
.slide-leave.slide-leave-active.left {
transform: translate(0%);
transition: transform 500ms ease-in-out;
}
.slide-enter.right {
transform: translate(0%);
}
.slide-enter.slide-enter-active.right {
transform: translate(-100%);
transition: transform 500ms ease-in-out;
}
.slide-leave.right {
transform: translate(0%);
}
.slide-leave.slide-leave-active.right {
transform: translate(-100%);
transition: transform 500ms ease-in-out;
}
Note that I used flexbox in order to fix the display problem (as well as some hard-coded widths)
Hope this helps
We can create slide animation in the following way using slide for Dialog
const Transition = React.forwardRef(function Transition(props, ref) {
return <Slide direction='up' ref={ref} {...props} />
})
<Dialog
open={open}
TransitionComponent={Transition} // Transition fade
keepMounted
onClose={handleClose}
aria-labelledby='alert-dialog-slide-title'
aria-describedby='alert-dialog-slide-description'
fullWidth
></Dialog>
Related
I'm building a website with React and Frame Motion and in the slider I can't make sure that when the image changes there is a transition or effect other than the simple detachment of the image change.
I tried to do the same thing using the img tag instead of the backgroundImage css property but still couldn't create the effect.
This is the component:
import { useState, useEffect, useCallback } from 'react';
import { motion } from 'framer-motion';
function ImgSlider({ slides, children }) {
const [current, setCurrent] = useState(0),
length = slides.length,
time = 4000;
const handleNext = useCallback(() => {
current + 1 >= length ?
setCurrent(0) :
setCurrent(current + 1);
}, [current, length]);
const handlePrev = () => {
current === 0 ?
setCurrent(length - 1) :
setCurrent(current - 1);
};
useEffect(() => {
const autoplay = setInterval(handleNext, time);
return () => clearInterval(autoplay);
}, [handleNext]);
return (
<motion.section
className="slider flex space-around g-6"
style={{ backgroundImage: `url(${slides[current]})` }}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: .5, ease: 'easeInOut' }}
exit={{ opacity: 0 }}
>
{children}
<button className="prev" onClick={handlePrev}>
<span className="sr-only">Slide Precedente</span>
</button>
<button className="next" onClick={handleNext}>
<span className="sr-only">Slide Successiva</span>
</button>
</motion.section>
);
}
export default ImgSlider;
and his css:
/* Slider */
section.slider {
min-height: 95vh;
padding: 1rem;
background-repeat: no-repeat;
background-size: cover;
background-position: center;
color: var(--bg);
}
section.slider button.prev,
section.slider button.next {
position: absolute;
right: 1rem;
top: 50%;
width: 3rem;
aspect-ratio: 1;
background-image: url(./imgs/arrow.svg);
background-repeat: no-repeat;
background-position: center;
opacity: .8;
transition: .3s ease-in-out;
}
section.slider button.prev {
transform: rotate(180deg);
left: 1rem;
right: unset;
}
section.slider button.prev:hover,
section.slider button.next:hover {
opacity: 1;
}
I don't know how I could do... Can anyone give me some suggestions?
Thank you :)
I have a modal which appears on the press of a button and is animated as it is displayed.
This works fine, BUT only if the modal code is already in the DOM when the button is pressed.
You can see the example here:
https://codesandbox.io/s/loving-dan-7fwrkr
This is the problem: if the button adds the modal code to the DOM then the modal simply appears with no animation.
I've spent many hours trying various ways to make this work and the best I can come up with is to use window.setTimeout to trigger the animation 200 milliseconds after the modal code is added to the DOM. I don't like such a solution because it seems like a hack - I don't have any clear understanding why such a hack would work.
The example below demonstrates both cases.
Without the commented code, the animation works.
With the commented code, the modal simply appears without animation.
If anyone has any idea how to fix this it would be much appreciated.
My specific goal is to NOT have the modal code in the DOM prior to pressing a button to make it appear.
I've worked pretty hard to make the minimum possible example below, but it is still fairly large I apologise. If you have suggesting for cutting it further whilst still being relevant please let me know.
import ReactDOM from 'react-dom';
import React, {useState} from 'react';
const theStyle = `
.md-modal {
position: fixed;
top: 50%;
left: 50%;
width: 50%;
height: auto;
z-index: 2000;
visibility: hidden;
transform: translateX(-50%) translateY(-50%);
}
.md-show {
visibility: visible;
}
.md-overlay {
position: fixed;
width: 100%;
height: 100%;
visibility: hidden;
top: 0;
left: 0;
z-index: 1000;
opacity: 0;
background: rgba(143, 27, 15, 0.8);
transition: all 0.3s;
}
.md-show ~ .md-overlay {
opacity: 1;
visibility: visible;
}
.md-content {
color: #fff;
background: #e74c3c;
position: relative;
border-radius: 3px;
margin: 0 auto;
}
.md-content h3 {
opacity: 0.8;
}
.md-effect-1 .md-content {
transform: scale(0.7);
opacity: 0;
transition: all 0.3s;
}
.md-show.md-effect-1 .md-content {
transform: scale(1);
opacity: 1;
}
`
function App() {
const [getVisible, setVisible] = useState(false);
/*
THE MODAL APPEAR ANIMATION DOES NOT WORK WHEN THIS IS UNCOMMENTED
if (!getVisible) {
return (
<button onClick={() => setVisible(true)}>
show modal
</button>)
}
*/
return (
<>
<style>
{theStyle}
</style>
<div className={`md-modal md-effect-1 ${(getVisible) && "md-show"}`}>
<div className="md-content">
This is a modal window.<br/>
<button onClick={() => setVisible(false)} className="md-close">close</button>
</div>
</div>
<div onClick={() => setVisible(false)} className="md-overlay"/>
<button onClick={() => setVisible(true)} className="md-trigger">
show modal
</button>
</>
);
}
ReactDOM.render(<App/>, document.getElementById('root'));
I've had similar issues, the reason was that the transition does not trigger if the modal immediately gets the end value of being visible when you add it to the DOM.
I solved it by putting the transition into an #keyframes animation. Then, after adding the modal to the DOM, you use classList.add() to trigger the animation.
Something like this
.modal {
opacity:0
}
.animated {
animation: showModal 1s forwards easeOut
}
#keyframes showModal {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
JS after the modal is added to the DOM:
myModel.classList.add("animated")
Self answer to my future self.
With the answer of #Kokodoko as my start point, I gained a better understanding of how animation works in CSS/JS and rewrote my modal entirely so it now does what I want.
Here's the code:
import ReactDOM from 'react-dom';
import React, {useState} from 'react';
const theStyle = `
.animated {
animation: showModal .2s forwards
}
#keyframes showModal {
from {
opacity: 0;
transform: scale(0.7);
}
to {
opacity: 1;
transform: scale(1);
}
}
.modalOverlay {
z-index: 1500;
background: rgba(40,91,218,0.5); /* you must use this and not opacity because opacity changes the front color */
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
margin: 0;
padding: 0;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: center;
align-content: stretch;
align-items: center;
}
.modalContainer {
z-index: 1600;
order: 0;
flex: 0 1 auto;
align-self: auto;
}
#modalContent {
z-index: 1700;
opacity: 0;
color: #fff;
width: 500px;
height: 200px;
background: #e74c3c;
position: relative;
border-radius: 3px;
margin: 0 auto;
}
`
function Button() {
const [getVisible, setVisible] = useState(false);
return (
<div>
<button onClick={() => setVisible(true)}>
show modal
</button>
{(getVisible) && <Modal setVisible={setVisible}/>}
</div>
)
}
function Modal({setVisible}) {
React.useEffect(
//() => window.setTimeout(document.getElementById("modalContent").classList.add("animated"))
() => document.getElementById("modalContent").classList.add("animated")
, [])
const handleClickOnOverlay = (e) => {
// clicks on the are sent through to the background so we must prevent that
e.stopPropagation()
setVisible(false)
}
const handleClickOnContainer = (e) => {
// clicks on the modal are sent through to the background so we must prevent that
e.stopPropagation()
}
const handleClickOnModal = (e) => {
console.log('clicked on modal')
}
return (
<>
<style>
{theStyle}
</style>
<div onClick={handleClickOnOverlay} className="modalOverlay">
<div className={`modalContainer`} onClick={handleClickOnContainer}>
<div id="modalContent" onClick={handleClickOnModal}>
This is a modal window.<br/>
<button onClick={() => setVisible(false)} className="md-close">close</button>
</div>
</div>
</div>
</>
);
}
ReactDOM.render(<Button/>, document.getElementById('root'));
I created a sidebar (hidden by default) in a web store that contains a list of items you like. When clicked on a button the likes-list will show sliding in from the right smoothly. However on page refresh/reload the sidebar slides in and out rapidly. I'm using css animation to do the trick.
It works fine but somehow on page reload the animation is triggered without clicking the button. How to prevent this from happening?
App is built in React (create-react-app).
React component
import React from 'react';
import { useGlobalContext } from '../Context'
import './likes.css';
const Likes = ({ showLikes, setShowLikes }) => {
let { liked, setLiked, paintings, setPaintings } = useGlobalContext()
//Remove item from likes list
const removeItemFromLikeslist = (id) => {
liked = liked.filter(item => {
return item.id !== id;
});
//Change likes-heart to green
paintings = paintings.map(el => {
if (el.id === id) {
el.like = false
}
return el
})
//remove sidebar overlay
if (liked.length === 0) {
setShowLikes(false)
}
setPaintings(paintings)
setLiked(liked)
localStorage.setItem("PAINTINGS", JSON.stringify(paintings))
localStorage.setItem('LIKES', JSON.stringify(liked))
}
return (
<div className={showLikes ? "likesContainer show" : "likesContainer"} >
<div className="likesHeader">You like these paintings</div>
{liked.map(item => {
const { id, name, imgSrc } = item;
return (
<div className="like-item" onClick={() => removeItemFromLikeslist(id)} key={id}>
<div className="like-info">
<img src={imgSrc} style={{ width: "100px" }} alt={name} className="picInCart" />
</div>
<div className="like-name">
<h5>{name}</h5>
</div>
</div>
)
})}
</div>
)
}
export default Likes
CSS file
.likesContainer {
position: fixed;
top: 83px;
right: -328px;
width: 320px;
background-color: rgb(70, 70, 70);
height:auto;
display: flex;
align-items: center;
justify-content: flex-start;
flex-direction: column;
z-index: 2;
box-shadow: 0px 4px 10px black;
animation: animateHide .5s linear forwards;
}
#keyframes animateHide {
from {
right: 0;
}
to {
right: -328px;
}
}
.likesContainer.show {
right: 0;
animation: animateShow .5s linear forwards;
}
#keyframes animateShow {
from {
right: -328px;
}
to {
right: 0;
}
}
Problem solved, changed from animation to transition like so:
.likesContainer {
position: fixed;
top: 83px;
right: -328px;
width: 320px;
background-color: rgb(70, 70, 70);
height:auto;
display: flex;
align-items: center;
justify-content: flex-start;
flex-direction: column;
z-index: 2;
box-shadow: 0px 4px 10px black;
transition: .5s ease-in-out;
}
.likesContainer.show {
right: 0;
}
I've been searching for a couple of days to add a fade transition effect on the backgroundImage that I'm changing through a Vue app.
Here is the code snippet I've been testing on:
new Vue({
el: "#main",
data: {
images: [
"https://images.freeimages.com/images/large-previews/bfd/clouds-1371838.jpg",
"https://images.freeimages.com/images/large-previews/ffa/water-lilly-1368676.jpg",
"https://images.freeimages.com/images/large-previews/efb/lotus-flower-1382251.jpg"
],
current: 0,
show: false
},
methods: {
changeBG: function () {
if (this.current < this.images.length - 1) {
this.current = this.current + 1;
} else {
this.current = 0;
}
}
}
});
.main {
height: 800px;
width: 100%;
margin-left: auto;
margin-right: auto;
z-index: 0;
background-repeat: no-repeat;
background-size: cover;
background-position: center 0px;
}
button {
width: 200px;
height: 25px;
margin: 10px;
}
p.hello{
color: white;
margin: 10px;
font-size: 50px;
}
.fade-enter-active,
.fade-leave-active {
transition: all 2s linear;
}
.fade-enter-to,
.fade-leave {
opacity: 0;
}
.fade-enter,
.fade-leave-to {
opacity: 1;
}
/* hello example transition */
.slide-fade-enter-active {
transition: all 1s ease;
}
.slide-fade-leave-active {
transition: all 1s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter,
.slide-fade-leave-to {
transform: translateX(10px);
opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<transition name="fade">
<div v-if="changeBG" class="main" id="main" :style="{ backgroundImage: 'url(\'' + images[current] + '\')' }">
<button v-on:click="changeBG">
changeBG
</button>
<div id="testFromGuide">
<button #click="show = !show">
toggleHello
</button>
<transition name="slide-fade">
<p class="hello" v-if="show">all your base are belong to us</p>
</transition>
</div>
</div>
</transition>
My first question if this is simply possible? The reason I'm using backgroundImage is because the website I'm using this on has a background that is most easy to handle responsively through this (always covering, no repeat and keeping it center). And my scond question would be, if not, is there a possibility to make it work with a background set as described here?
In the codepen I've added an example of the vue guide to make sure it works and nothing is else is wrong. And the example works perfectly. Can't seem to find the answer for my example but I've been beginning to suspect it is simply not possible or I can't seem to find why vue isn't detecting something is changing.
For Vue Transitions to work, you need to change the DOM elements. So this way would work if you were changing actual images out. In your example, you're only changing an attribute value. The DOM doesn't trigger a transition since its the same element.
However, you can use the :key attribute to convince VUE to replace the element thus giving you a transition between 2 elements.
You can also set the image with inline CSS like you're doing in the example. You'll still have to create the transition in your CSS.
Here's an example using Vue Transition
new Vue({
el: "#main",
data: {
currentID: 0,
images: [
'https://images.freeimages.com/images/large-previews/efb/lotus-flower-1382251.jpg',
'https://images.freeimages.com/images/large-previews/ffa/water-lilly-1368676.jpg',
'https://images.freeimages.com/images/large-previews/bfd/clouds-1371838.jpg'
]
},
methods: {
toggleImage: function(){
if(this.currentID < this.images.length-1){
this.currentID +=1
} else {
this.currentID = 0
}
}
}
});
body {
overflow: hidden;
}
.main {
position: relative;
}
img {
width: 100%;
height: auto;
display: block;
position: absolute;
-webkit-transition: all 3s ease;
transition: all 3s ease;
}
button {
z-index: 100;
position: relative;
width: 200px;
height: 25px;
margin: 20px;
}
/* prefix with transition name */
.slide-fade-enter-active {
opacity: 1;
z-index: 10;
}
.slide-fade-leave-active {
opacity: 1;
}
.slide-fade-enter,
.slide-fade-leave-to {
opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js"></script>
<div class="main" id="main">
<transition name="slide-fade">
<!-- SRC comes from the array of images the :key is important for vue to believe its a 'new' DOM element and do the transition -->
<img v-bind:src="images[currentID]" v-bind:key="currentID" />
</transition>
<button #click="toggleImage">
Toggle Image
</button>
</div>
However, you don't get a lot of control over that since it uses image tags. Instead, it might be better to use a background image like this:
new Vue({
el: "#main",
data: {
currentID: 0,
images: [
'https://images.freeimages.com/images/large-previews/efb/lotus-flower-1382251.jpg',
'https://images.freeimages.com/images/large-previews/ffa/water-lilly-1368676.jpg',
'https://images.freeimages.com/images/large-previews/bfd/clouds-1371838.jpg'
]
},
methods: {
toggleImage: function(){
if(this.currentID < this.images.length-1){
this.currentID +=1
} else {
this.currentID = 0
}
}
}
});
.main {
/* make this the size of the window */
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.theImage {
width: 100%;
height: 100%;
position: absolute;
background-color: #333;
background-size: contain;
background-repeat: no-repeat;
background-position: center center;
-webkit-transition: all 3s ease;
transition: all 3s ease;
}
button {
z-index: 100;
position: relative;
width: 200px;
height: 25px;
margin: 20px;
}
/* prefix with transition name */
.slide-fade-enter-active {
opacity: 1;
z-index: 10;
}
.slide-fade-leave-active {
opacity: 1;
}
.slide-fade-enter,
.slide-fade-leave-to {
opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js"></script>
<div class="main" id="main">
<transition name="slide-fade">
<!-- SRC comes from the array of images the :key is important for vue to believe its a 'new' DOM element and do the transition -->
<div class="theImage" v-bind:style="{'background-image': 'url(' + images[currentID] + ')'}" v-bind:key="currentID"></div>
</transition>
<button #click="toggleImage">
Toggle Image
</button>
</div>
The answer was indeed to forget about vue transitions and let css do the work. A working example can be found here:
new Vue({
el: "#main",
data: {
show: false,
BG1: true,
BG2: false,
BG3: false
},
methods: {
changeBG: function(){
if (this.BG1 == true){
this.BG1 = false;
this.BG2 = true;
this.BG3 = false;
} else if (this.BG2 == true) {
this.BG1 = false;
this.BG2 = false;
this.BG3 = true;
} else if (this.BG3 == true) {
this.BG1 = true;
this.BG2 = false;
this.BG3 = false;
}
},
showBG1: function(){
if (this.BG1 == true){
return "";
} else {
return "transparent";
}
},
showBG2: function(){
if (this.BG2 == true){
return "";
} else {
return "transparent";
}
},
showBG3: function(){
if (this.BG3 == true){
return "";
} else {
return "transparent";
}
}
}
});
.main {
}
#bgs img.transparent {
opacity:0;
transform: translateY(-0.0px);
}
#bgs img{
/* Set rules to fill background */
min-height: 100%;
min-width: 1024px;
/* Set up proportionate scaling */
width: 100%;
height: auto;
/* Set up positioning */
position: fixed;
top: 0;
left: 0;
z-index: -1;
-webkit-transition: all 0.5s ease-in-out;
-moz-transition: all 0.5s ease-in-out;
-o-transition: all 0.5s ease-in-out;
transition: all 0.5s ease-in-out;
}
#media screen and (max-width: 1024px) { /* Specific to this particular image */
#bgs img{
left: 50%;
margin-left: -512px; /* 50% */
}
}
button {
z-index: 100;
position: relative;
width: 200px;
height: 25px;
margin: 20px;
}
p.hello{
color: white;
margin: 40px;
font-size: 50px;
}
.fade-enter-active {
transition: all 1s ease;
}
.fade-leave-active {
transition: all 1s ease;
}
.fade-enter, .fade-leave-to{
transform: translateY(-5px);
opacity: 0;
}
/* hello example transition */
.slide-fade-enter-active {
transition: all 1s ease;
}
.slide-fade-leave-active {
transition: all 1s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter,
.slide-fade-leave-to {
transform: translateY(-5px);
opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js"></script>
<div class="main" id="main">
<div id="bgs">
<img :class="showBG1()" src="https://images.freeimages.com/images/large-previews/efb/lotus-flower-1382251.jpg">
<img :class="showBG2()" src="https://images.freeimages.com/images/large-previews/ffa/water-lilly-1368676.jpg">
<img :class="showBG3()" src="https://images.freeimages.com/images/large-previews/bfd/clouds-1371838.jpg">
</div>
<button #click="changeBG">
changeBG
</button>
<div id="testFromGuide">
<button #click="show = !show">
toggleHello
</button>
<transition name="slide-fade">
<p class="hello" v-if="show">all your base are belong to us</p>
</transition>
</div>
</div>
It's not perfect yet as for every extra background I need to add a function and add an extra if else loop to the changeBG function. It would be more elegant if this could be done with a list variable but I've not found a way to make this work with the :class method. I hope to look into this at a later time.
I'm looking for a way to show a small animation around the area where I clicked, for instance, a circle that expands and then diminishes until disappearing. The part which looks easy is creating the animation, which would be a CSS transition, what's hard for me is making something appear right where I clicked (with CSS).
If there's no CSS-based solution I'd like to know how to do it with React, please.
Thanks
You can use CSSTransition from react-transition-group.
A little example for what you're looking for could look like this.
import React from "react";
import "./Style.css";
import { CSSTransition } from "react-transition-group";
export default class Modal extends React.Component {
state = {
animate: false
}
render() {
return (
<React.Fragment>
<CSSTransition
in={this.state.animate}
classNames="animate-circle"
timeout={500}
>
<div
className="circle"
onClick={()=>this.setState({animate: animate ? false : true})}
>
Click to expand and click again to diminish
</div>
</CSSTransition>
</React.Fragment>
)
}
}
And Style.css should've code something like this
.circle {
width: 300px;
height: 300px;
border-radius: 50%;
background-color: red;
}
.animate-circle-enter-active {
width: 500px;
height: 500px;
transition: all 500ms infinite;
}
.animate-circle-enter-done {
width: 500px;
height: 500px;
}
.animate-circle-exit {
width: 500px;
height: 500px;
}
.animate-circle-exit-active {
width: 0px;
height: 0px;
transition: all 500ms infinite;
}
.animate-circle-exit-done {
width: 0px;
height: 0px;
}
CSS button click animations use the :active pseudo class. The button itself should have a transition, and the static design. The button:active should have the active design (the moment before the animation starts) with transition: 0s;. The transition: 0s; rule is important because it makes the button move to the active design immediately (without animation).
Whenever the button is clicked, it becomes active, and moves straight to the active design state. When the button is released, the transition between the active and the static states creates the effect.
body {
padding: 5em;
}
button {
position: relative;
font-size: 1.5em;
padding: 0.5em 1em;
border: none;
outline: none;
}
button::before {
position: absolute;
z-index: -1;
top: -1em;
right: -1em;
left: -1em;
bottom: -1em;
background: radial-gradient(transparent 0, gold 100%);
opacity: 0;
content: '';
transition: all 0.3s;
}
button:active::before {
top: 0;
right: 0;
left: 0;
bottom: 0;
opacity: 1;
transition: 0s;
}
<button>Click</button>