Lag en lightbox med React



Lag en lightbox med React

av Ole Petter den 28. juli 2021

Sist oppdatert: 16 august, 2021 kl 08:21

Contents

Kildekode

Kildekoden kan finnes her: https://github.com/olepetterkh91/react-bootstrap-lightbox

Les historien på engelsk her:

https://medium.com/@olepetterkh/create-a-simple-lightbox-with-react-abe04e3e6e7b

Create React App

npx create-react-app

I dette eksempelet brukes Bootstrap til CSS. Bootstrap er et css-rammeverk som gjør det kjapt og enkelt å lage en fin nettside. Det finnes også en React plugin for Bootstrap som legger til funksjoner slik som modal og responsiv navigeringsmeny.

Installer framer-motion

For animasjoner er framer-motion et populært alternativ i React, og det er relativt enkelt å bruke.

npm i framer-motion

App.js

import "./App.css";
import ImageGrid from './components/LightBox/ImageGrid'
function App() {
	return (
	    <div className="container my-3">
            <ImageGrid />
        </div>
	);
}

export default App;

App.css

@import url("https://pro.fontawesome.com/releases/v5.10.0/css/all.css");

.bg-transparent-dark {
  background-color: rgba(0, 0, 0, 0.8);
}
.img-wrap {
  opacity: 0.8 !important;
}
.img-fixed {
  object-fit: cover;
  width: 200px;
  height: 200px;
}

.left-arrow {
  position: fixed;
  top: 50%;
  left: 0px;
}
.right-arrow {
  position: fixed;
  top: 50%;
  right: 0px;
}

.imageCount {
  background-color: rgba(0, 0, 0, 0.85);
  font-size: 1;
  padding: 1rem;
  position: fixed;
  top: 0px;
  left: 0px;
}
.closeButton {
  background-color: rgba(0, 0, 0, 0.85);
  font-size: 4rem;
  padding: 1rem;
  position: fixed;
  top: 0px;
  right: 0px;
}
.arrow-left,
.arrow-right {
  background-color: rgba(0, 0, 0, 0.85);
  cursor: pointer;
  position: fixed;
  top: 50%;
  width: auto;
  padding: 16px;
  margin-top: -50px;
  color: white;
  font-weight: bold;
  font-size: 20px;
  transition: 0.6s ease;
  border-radius: 0 3px 3px 0;
  user-select: none;
  -webkit-user-select: none;
}
@media screen and (max-width: 700px) {
  .arrow-left, .arrow-right {
    background-color: rgba(0, 0, 0, 0.45);
  }
}
.arrow-right {
  right: 0px;
}
.blocks-gallery-grid {
  list-style: none;
}
.image-container {
  position: relative;
}

.img-fixed-size {
  object-fit: cover;
  height:100px;
  width: 300px;
}
.image-text {
  position: absolute;
  bottom: 0px;
  width: 100%;
  color: white;
  padding: 1rem;
  background-color: rgba(0, 0, 0, 0.5);
}
.img-fixed-size--wide {
  object-fit: cover;
  height: 300px;
  width: 700px;
}

Sett opp en modal (Modal.js)

import React from "react";

import { motion } from "framer-motion";

const Modal = ({ selectedImg, selectedIndex, setSelectedIndex, images }) => {
    const clickHandler = (e) => {
        if (e.target.classList.contains("backdrop")) {
            setSelectedIndex(-1);
        }
    };

    function nextImage() {
        let imageIndex = selectedIndex
        if (imageIndex >= images.length - 1) {
          imageIndex = 0
        } else {
          imageIndex = selectedIndex + 1
        }
        setSelectedIndex(imageIndex)
      }
    
      function previousImage() {
        let imageIndex = selectedIndex
        if (imageIndex > 0) {
          imageIndex = selectedIndex - 1
        } else {
          imageIndex = images.length - 1
        }
        setSelectedIndex(imageIndex)
      }

    return (
        <motion.div
            className="modal show backdrop bg-transparent-dark"
            style={{display: "block"}}
            onClick={clickHandler}
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
        >
            <div className="modal-dialog modal-dialog-centered modal-xl">
                <div className="modal-content" style={{ border: "none", backgroundColor: "black" }}>
                    <motion.img
                        src={selectedImg ? selectedImg : "https://smorasstats.com/v4/wp-content/uploads/2020/06/71951528_2527097874023921_5910925260533792768_o.jpg"}
                        alt="Modal"
                        initial={{ width: "110%" }}
                        animate={{ width: "100%" }}
                    />
                    
                </div>
            </div>
            <div className="imageCount text-white">
                {selectedIndex + 1} / {images?.length}
            </div>
            <div className="closeButton">
                <button
                    onClick={() => setSelectedIndex(-1)}
                    type="button"
                    class="close"
                    style={{ color: "white" }}
                >
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>
            <div className="lightbox-navigation">
                <div  onClick={previousImage} className="arrow-left">
                    <i className="fas fa-chevron-left"></i>
                </div>
                <div onClick={nextImage} className="arrow-right">
                    <i className="fas fa-chevron-right"></i>
                </div>
            </div>
        </motion.div>
    );
};
export default Modal;

ImageGrid.js

import { useEffect, useState } from "react";
import LightBox from "./Lightbox";

function ImageGrid() {

    const [images, setImages] = useState([])

    useEffect(() => {
        async function getData() {
            const response = await fetch("https://smorasstats.com/v4/wp-json/wp/v2/gallery")
            const data = await response.json()
            const imageUrls = data?.map((post) => post?.acf?.galleri_poster)
            setImages(imageUrls)
        }
        getData()
    }, [])

    return (
        <div>
            <LightBox images={images} />
        </div>
    )
}
export default ImageGrid

LightBox.js

import { useState } from "react";
import Modal from "./Modal";
import LightBoxListTemplate from "./templates/LightboxListTemplate";

function LightBox({images}) {
    
    const [selectedIndex, setSelectedIndex] = useState(null)
    return (
        <div>
            <div className="row">
                {images?.map((image, index) => <LightBoxListTemplate key={index} image={image} index={index} setSelectedIndex={setSelectedIndex} />)}
            </div>
            {selectedIndex >= 0 && <Modal images={images} selectedImg={images[selectedIndex]} selectedIndex={selectedIndex} setSelectedIndex={setSelectedIndex} />}
        </div>
    )
}
export default LightBox

LightBoxListTemplate.js

function LightBoxListTemplate({image, index, setSelectedIndex, setSelectedImage}) {
    return (
        <div className="col-md-4 col-4">
            <img src={image ? image : "https://smorasstats.com/v4/wp-content/uploads/2020/06/71951528_2527097874023921_5910925260533792768_o.jpg"} alt="" className="img-fixed-size img-fluid" onClick={() => setSelectedIndex(index)} />
        </div>
    )
}
export default LightBoxListTemplate
Legg igjen en kommentar