目录
一、预览图
二、源码
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%);
}
}
}
}
}