Search code examples
cssreactjscss-transforms

CSS rotating image gallery with react taking two button clicks when it should take one


I have a rotating image gallery using react and CSS. It works except every time I click the button to rotate forward or backward it takes two clicks to start working correctly. For instance the first click does nothing and second click goes, or if are scrolling right then click the left button it goes right one more time, then subsequent clicks start going left.

I know this maybe because the state is updated but it hasn't rerendered yet but no matter what I try it doesn't change anything. I tried moving the set transform into a separate function. I tried making it a variable that the function then sets equal to the state of currentTransform. I can't seem to get it to stop needing two clicks. Any help would be appreciated.

React code

const Rotating = () => {

const [currentTransform, setCurrentTransform] = useState(`perspective(1000px) rotateY(0deg)`);
const [imageGroup, setImageGroup] = useState("imageSet1");
const [x, setX] = useState(0);
const numberOfPictures = 8;

const handlePreviousButton = () => {
    setX(x + 45);
    setCurrentTransform(`perspective(1000px) rotateY(${x}deg)`);
}

const handleNextButton = () => {
    setX(x - 45);
    setCurrentTransform(`perspective(1000px) rotateY(${x}deg)`);
}

const changeImageSet = (imageSet: string) => {
    setImageGroup(imageSet);
}

return (
    <div className="rotating-gallery-main-div-style">
        <div className="rotating-gallery-btn-container">
            <Button onClick={() => changeImageSet("imageSet1")} className="image-group-btn" variant="primary">
                Pictures
            </Button>
            <Button onClick={() => changeImageSet("imageSet2")} className="image-group-btn" variant="primary">
                Other Pictures
            </Button>
        </div>
        <div className="rotating-gallery-image-container" style={{transform: currentTransform} as React.CSSProperties}>
            {[...Array(numberOfPictures)].map((_, index) =>
                <span style={{ "--i": index } as React.CSSProperties} key={index}>
                    <img
                        src={`/images/${imageGroup}/${index}.jpg`}
                    />
                </span>
            )}
        </div>
        <div className="rotating-gallery-btn-container">
            <Button onClick={handlePreviousButton} className="rotating-gallery-prev-btn rotating-gallery-btn" variant="primary">Previous</Button>
            <Button onClick={handleNextButton} className="rotating-gallery-next-btn rotating-gallery-btn" variant="primary">Next</Button>
        </div>
    </div>
)

}

CSS

.rotating-gallery-main-div-style {
  margin: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  height: 100vh;
  background: black;
  overflow: hidden;
}
.rotating-gallery-image-container {
  position: relative;
  width: 250px;
  height: 250px;
  transform-style: preserve-3d;
  transform: perspective(1000px) rotateY(0deg);
  transition: transform 0.7s;
}
.rotating-gallery-image-container span {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  transform: rotateY(calc(var(--i) * 45deg)) translateZ(400px);
}
.rotating-gallery-image-container span img {
  position: absolute;
  left: 0;
  top: -30px;
  width: 100%;
  max-height: 225px;
}
.rotating-gallery-btn-container {
  position: relative;
  width: 80%;
}
.rotating-gallery-btn {
  position: absolute;
  bottom: -80px;
  border: none;
  padding: 10px 20px;
  border-radius: 5px;
}
.image-group-btn {
  position: relative;
  top: -180px;
  margin-left: 10px;
}
.rotating-gallery-prev-btn {
  left: 20%;
}
.rotating-gallery-next-btn {
  right: 20%;
}
.rotating-gallery-btn:hover {
  filter: brightness(1.5);
}

Solution

  • In react setState is async, so when you call setX(newVal), your x is not updated yet. Try this

    const handlePreviousButton = () => {
        const newXValue = x + 45
        setX(newXValue);
        setCurrentTransform(`perspective(1000px) rotateY(${newXValue}deg)`);
    }