前端组件-简易图片、视频查看器(兼容PC端、H5端)

10 篇文章 0 订阅
4 篇文章 0 订阅

目录

一、预览图

二、源码


一、预览图

 

 

 

二、源码

import React, { useState, useEffect, useRef } from "react";
import Store from "./store";
import ReIcon from "bases/ReIcon";
import Loading from "./Loading";
import "./index.less";

const getQueryParams = () => {
  const searchParams = new URLSearchParams(window.location.search);
  const queryParams = {};
  for (const [key, value] of searchParams.entries()) {
    queryParams[key] = isNaN(value) ? value : Number(value);
  }
  return queryParams;
};

const IntroductionImageViewer = ({ name, history }) => {
  const [currentIndex, setCurrentIndex] = useState(0);
  const [isButtonDisabled, setButtonDisabled] = useState(false);
  const [multipleImagesUrlList, setMultipleImagesUrlList] = useState([]);
  const [selectedThumbnailIndex, setSelectedThumbnailIndex] = useState(0);
  const [currentPageIndex, setCurrentPageIndex] = useState(1); // Define currentPageIndex state
  const [totalItemCount, setTotalItemCount] = useState(0);
  const [currentItemCount, setCurrentItemCount] = useState(0);
  const [loading, setLoading] = useState(false);
  const scrollContainerRef = useRef(null);
  const queryParams = getQueryParams();

  const store = Store.stores[name]
    ? Store.stores[name]
    : new Store({ name, history });

  useEffect(() => {
    if (Store.stores[name]) {
      store.afterMethodOnEvent();
    } else {
      store.mount();
    }
    fetchData(
      queryParams.english,
      queryParams.userId,
      queryParams.listId,
      queryParams.coverIndex
    );
    return () => {
      store.unMount();
    };
  }, []);

  const handlePrev = (imagesData) => {
    if (isButtonDisabled) {
      return;
    }
    setSelectedThumbnailIndex(-1);
    setCurrentIndex((prevIndex) => {
      const newIndex = prevIndex === 0 ? imagesData.length - 1 : prevIndex - 1;
      return newIndex;
    });
    setButtonDisabled(true);
    handleScrollLeft();
    setLoading(true);
    setTimeout(() => {
      setSelectedThumbnailIndex(-1); // 重置 selectedThumbnailIndex
      setButtonDisabled(false);
      if (currentIndex === 0) {
        // 到达第一页,滚动到最后一页的位置
        if (scrollContainerRef.current) {
          const scrollContainerWidth = scrollContainerRef.current.offsetWidth;
          const scrollContentWidth = scrollContainerRef.current.scrollWidth;
          scrollContainerRef.current.scrollLeft =
            scrollContentWidth - scrollContainerWidth;
        }
      }
      setLoading(false);
    }, 500);
  };

  const handleNext = (imagesData) => {
    if (isButtonDisabled) {
      return;
    }
    setSelectedThumbnailIndex(-1);
    setCurrentIndex((prevIndex) => (prevIndex + 1) % imagesData.length);
    setButtonDisabled(true);
    handleScrollRight();
    setLoading(true);
    setTimeout(() => {
      setSelectedThumbnailIndex(-1);
      setButtonDisabled(false);
      if (currentIndex + 1 === imagesData.length) {
        if (shouldFetchNextPage(currentItemCount, totalItemCount)) {
          fetchNextPageData(currentPageIndex);
          setCurrentIndex(currentItemCount);
          setSelectedThumbnailIndex(currentItemCount);
        }
        if (scrollContainerRef.current) {
          scrollContainerRef.current.scrollLeft = 0;
        }
      }
      setLoading(false);
    }, 500);
  };

  const shouldFetchNextPage = (currentPage, totalPages) => {
    return currentPage < totalPages;
  };

  const fetchNextPageData = (currentPage) => {
    const nextPageNumber = currentPage + 1;
    setCurrentPageIndex(nextPageNumber);
    fetchData(
      queryParams.english,
      queryParams.userId,
      queryParams.listId,
      queryParams.coverIndex,
      nextPageNumber
    );
  };

  const handleThumbnailClick = (index) => {
    setSelectedThumbnailIndex(index === selectedThumbnailIndex ? -1 : index);
    setCurrentIndex(index);
  };

  const handleScrollLeft = () => {
    console.log("向左滚动");
    if (scrollContainerRef.current) {
      const scrollAmount = isMobile() ? 60 : 122; // 根据移动端或非移动端设备调整滚动距离
      scrollContainerRef.current.scrollLeft -= scrollAmount;
    }
  };

  const handleScrollRight = () => {
    console.log("向右滚动");
    if (scrollContainerRef.current) {
      const scrollAmount = isMobile() ? 60 : 122; // 根据移动端或非移动端设备调整滚动距离
      scrollContainerRef.current.scrollLeft += scrollAmount;
    }
  };

  const isMobile = () => {
    return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
      navigator.userAgent
    );
  };

  const fetchData = (english, userId, listId, coverIndex, pageNumber = 1) => {
    stores.Frame.showLoading();
    let url = "";
    let albumRequestData = {
      id: listId,
      pageNumber,
      pageSize: 50,
    };
    let requestData = {
      userId,
    };
    switch (english) {
      case "#research":
        url = api.getResearchList;
        break;
      case "#treatise":
        url = api.getTreatiseList;
        break;
      case "#newsReport":
        url = api.getNewsReport;
        break;
      case "#bigEvent":
        url = api.getHistoryList;
        break;
      case "#artist":
        url = api.getAwardList;
        break;
      case "#activityAlbum":
        url = api.getActivityAlbumList;
        setCurrentIndex(coverIndex);
        setSelectedThumbnailIndex(coverIndex);
        break;
      default:
        return;
    }
    request({
      url,
      data: english === "#activityAlbum" ? albumRequestData : requestData,
    })
      .then((res) => {
        if (res && res.code === 200 && res.data && Array.isArray(res.data)) {
          stores.Frame.hideLoading();
          const multipleImagesUrlDataList = res.data.filter(
            (item) => item.id === listId
          );
          const { multipleImagesUrl } = multipleImagesUrlDataList[0];
          const parsedMultipleImagesUrlList = JSON.parse(multipleImagesUrl);
          setMultipleImagesUrlList(parsedMultipleImagesUrlList);
        } else if (
          english === "#activityAlbum" &&
          res.code === 200 &&
          res.data &&
          Array.isArray(res.data.list)
        ) {
          stores.Frame.hideLoading();
          setTotalItemCount(res.data.total);
          const multipleImagesUrlDataList = res.data.list.map((Item) => {
            if (!isValidFileFormat(Item.url)) {
              return Item.cover;
            }
            return Item.url;
          });
          setMultipleImagesUrlList((prevList) => [
            ...prevList,
            ...multipleImagesUrlDataList,
          ]);
          setCurrentItemCount(
            multipleImagesUrlDataList.length + (pageNumber - 1) * 50
          );
          if (shouldFetchNextPage(pageNumber, Math.ceil(totalItemCount / 50))) {
            fetchNextPageData(pageNumber);
          }
        }
      })
      .catch((err) => {
        stores.Frame.error(err.message);
      });
  };

  const isValidFileFormat = (url) => {
    // Remove query parameters from the URL
    const cleanUrl = url.split("?")[0];
    // List of valid file extensions or formats
    const validExtensions = /\.(jpg|jpeg|png|gif|mp4)$/i;
    // Use the regular expression to test if the URL's format is valid
    return validExtensions.test(cleanUrl);
  };

  const conditionalRender = () => {
    const currentItem = multipleImagesUrlList[currentIndex];
    if (utils.isVideo(currentItem)) {
      return (
        <video
          controls
          className="video_page"
          alt={`multipleImagesUrlList ${currentIndex + 1}`}
          muted // 添加 muted 属性
          key={`multipleImagesUrlList ${currentIndex + 1}`} // 添加 key 属性
        >
          <source
            src={utils.pictureThumb(currentItem, false)}
            type="video/mp4"
          />
        </video>
      );
    } else {
      return (
        <img
          src={utils.pictureThumb(currentItem, false)}
          alt={`multipleImagesUrlList ${currentIndex + 1}`}
          className="cover"
        />
      );
    }
  };

  const renderImages = () => {
    return multipleImagesUrlList.map((item, index) => (
      <div
        key={index}
        className={`scroll_cover ${
          selectedThumbnailIndex === index ? "selected" : ""
        } ${currentIndex === index ? "highlight" : ""}`}
        onClick={() => handleThumbnailClick(index)}
      >
        <img
          src={utils.pictureThumb(
            utils.isVideo(item)
              ? item +
                  "?x-oss-process=video%2Fsnapshot%2Ct_7000%2Cf_jpg%2Cw_800%2Ch_600%2Cm_fast"
              : item,
            !utils.isVideo(item)
          )}
          alt={`${item}${currentIndex + 1}`}
          className="cover_image"
        />
        {utils.isVideo(item) && (
          <div className="artwork-cover-mask">
            <ReIcon icon="shipin1" className="icon" />
          </div>
        )}
      </div>
    ));
  };
  return (
    <div className="introduction_image_viewer">
      <div className="image-container">
        <div className="nav-buttons">
          <span
            className="prev-button"
            onClick={() => handlePrev(multipleImagesUrlList)}
            disabled={isButtonDisabled}
          >
            <ReIcon icon="prev" />
          </span>
          <span
            className="next-button"
            onClick={() => handleNext(multipleImagesUrlList)}
            disabled={isButtonDisabled}
          >
            <ReIcon icon="next" />
          </span>
        </div>
        <div className="image_page">
          {conditionalRender()}
          <div className="index_Page">
            {`${currentIndex + 1} / ${multipleImagesUrlList.length}`}
          </div>
        </div>
      </div>
      {/* Scrollable container */}
      <div className="scroll_image_container_page">
        <span className="scoll_icon" onClick={handleScrollLeft}>
          <ReIcon icon="prev" />
        </span>
        <div className="scroll_image_list" ref={scrollContainerRef}>
          {renderImages()}
        </div>
        <span className="scoll_icon" onClick={handleScrollRight}>
          <ReIcon icon="next" />
        </span>
      </div>
      {/* {loading && <Loading />} */}
    </div>
  );
};

export default IntroductionImageViewer;
@import "~styles/constant.less";

.introduction_image_viewer {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100vh;
  background: #222222;
  position: relative;
  flex-direction: column;

  .root-page-loading {
    background-color: rgba(0, 0, 0, 0.5) !important; /* 设置背景半透明颜色 */
  }

  ::selection {
    background-color: transparent;
  }

  .scroll_image_container_page {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 72%;
    position: absolute;
    height: 102 / @remScale;
    bottom: 5%;

    @media (max-width: @max1) {
      width: 80%;
    }

    .scoll_icon {
      width: 39 / @remScale;
      height: 102 / @remScale;
      background: rgba(0, 0, 0, 0.4);
      backdrop-filter: blur(1 / @remScale);
      display: flex;
      justify-content: center;
      align-items: center;
      color: #999999;
      cursor: pointer;

      @media (max-width: @max1) {
        height: 50 / @remScale;
      }

      .re-icon-root {
        width: 28 / @remScale;
        height: 28 / @remScale;

        @media (max-width: @max1) {
          width: 15 / @remScale;
          height: 15 / @remScale;
        }
      }
    }

    .scroll_image_list {
      width: 100%;
      height: 100%;
      display: flex;
      justify-content: flex-start; /* 将内容靠左对齐 */
      align-items: center;
      overflow: auto; /* 修改为auto */
      overflow-x: hidden; /* 隐藏水平滚动条 */
      overflow-y: hidden; /* 隐藏垂直滚动条 */
      position: relative;

      .selected {
        border: 2px solid #ffff;
      }

      .highlight {
        border: 2px solid #ffff;
      }

      .scroll_cover {
        width: 102 / @remScale;
        height: 102 / @remScale;
        margin-right: 20 / @remScale;
        cursor: pointer;
        position: relative;

        @media (max-width: @max1) {
          width: 56 / @remScale;
          height: 56 / @remScale;
          margin-right: 10 / @remScale;
        }
        .artwork-cover-mask {
          position: absolute;
          width: 100%;
          height: 100%;
          background: rgba(0, 0, 0, 0.4);
          color: @white;
          display: flex;
          z-index: 9;
          top: 0;
          @media (max-width: @max1) {
            border-radius: 4 / @remScale 4 / @remScale 0 0;
          }
          .icon {
            width: 40 / @remScale;
            height: 30 / @remScale;
            opacity: 0.6;

            @media (max-width: @max1) {
              width: 20 / @remScale;
              height: 10 / @remScale;
            }
          }
        }

        .cover_image {
          width: 100%;
          height: 100%;
          white-space: normal;
          object-fit: cover;

          
          @media (max-width: @max1) {
            width: 52 / @remScale;
            height: 52 / @remScale;
            margin-right: 10 / @remScale;
            white-space: normal;
            object-fit: cover;
          }
        }

        &:first-child {
          margin-left: 20 / @remScale;
        }
      }
    }
  }

  .image-container {
    display: flex;
    justify-content: center;
    align-items: center;
    margin-bottom: 8%;
    position: relative;
    width: 100%;
    .nav-buttons {
      position: absolute;
      width: 100%;
      top: 50%;
      transform: translateY(-50%);
      display: flex;
      justify-content: space-between;
      padding: 0 46 / @remScale;
      color: #ffff;
      font-size: 20 / @remScale;
      z-index: 99;

      @media (max-width: @max1) {
        padding: 0 5 / @remScale;
      }
      .re-icon-root {
        width: 28 / @remScale;
        height: 28 / @remScale;

        @media (max-width: @max1) {
          width: 15 / @remScale;
          height: 15 / @remScale;
        }
      }

      .prev-button {
        cursor: pointer;
        width: 60 / @remScale;
        height: 60 / @remScale;
        background: rgba(153, 153, 153, 0.4);
        backdrop-filter: blur(1 / @remScale);
        border-radius: 50%;
        text-align: center;
        line-height: 60 / @remScale;
        color: #ffff;

        @media (max-width: @max1) {
          width: 30 / @remScale;
          height: 30 / @remScale;
          line-height: 30 / @remScale;
        }
      }

      .next-button {
        cursor: pointer;
        width: 60 / @remScale;
        height: 60 / @remScale;
        background: rgba(153, 153, 153, 0.4);
        backdrop-filter: blur(1 / @remScale);
        line-height: 60 / @remScale;
        border-radius: 50%;
        text-align: center;

        @media (max-width: @max1) {
          width: 30 / @remScale;
          height: 30 / @remScale;
          line-height: 30 / @remScale;
        }
      }
    }

    .image_page {
      width: 72%;
      height: 730 / @remScale;
      position: relative;

      @media (max-width: @max1) {
        width: 15rem;
      }

      
      .video_page{ 
        width: 100%;
        height: 100%;
        object-fit: contain;
        @media (max-width: @max1) { 
          height: 90%;
        }
      }

      .cover {
        width: 100%;
        height: 100%;
        object-fit: contain;
      }
    
      .index_Page {
        position: absolute;
        bottom: 0;
        right: 0%;
        width: 58 / @remScale;
        height: 29 / @remScale;
        background: rgba(0, 0, 0, 0.4);
        backdrop-filter: blur(1 / @remScale);
        color: #fff;
        text-align: center;
        line-height: 29 / @remScale;
        @media (max-width: @max1) {
          right: 50%;
          bottom: 20%;
          transform: translateX(50%);
        }
      }
    }
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

web_icon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值