当前位置: 首页 > web >正文

antd+react实现html图片预览效果

import { Image } from ‘antd’;
import { useEffect, useRef, useState } from ‘react’;
import styles from ‘./index.module.less’;

interface PreviewHtmlWithImagesProps {
htmlContent: string;
}

const PreviewHtmlWithImages: React.FC = ({ htmlContent }) => {
const containerRef = useRef(null);
const [imageSrc, setImageSrc] = useState(’’);
const [visible, setVisible] = useState(false);
const [isZoomed, setIsZoomed] = useState(false);
const actionsRef = useRef(null);

// 新增:用于跟踪拖动状态
const isDraggingRef = useRef(false);
const startPosRef = useRef({ x: 0, y: 0 });

const processedHtml = htmlContent.replace(
/<img([^>]*)src="’"’>/gi,
(match, beforeSrc, src, afterSrc) => {
if (/data-src=/i.test(match)) return match;
return <img${beforeSrc}src="${src}" data-src="${src}" class="preview-image"${afterSrc}>;
},
);

useEffect(() => {
const images: any = containerRef.current?.querySelectorAll(’.preview-image’);
images?.forEach((img: HTMLImageElement) => {
const clickHandler = () => {
const src = img.getAttribute(‘data-src’);
setImageSrc(src || ‘’);
setVisible(true);
setIsZoomed(false);
};

  img.style.cursor = 'zoom-in';img.addEventListener('click', clickHandler);return () => {img.removeEventListener('click', clickHandler);img.style.cursor = '';};
});

}, [processedHtml]);

useEffect(() => {
if (!visible) {
setIsZoomed(false);
return;
}

const previewContent: HTMLImageElement | null = document.querySelector('.ant-image-preview-content',
);
const previewImg: HTMLImageElement | null = document.querySelector('.ant-image-preview-img',
) as HTMLImageElement | null;
const imgWrapper: HTMLImageElement | null = document.querySelector('.ant-image-preview-img-wrapper',
);if (!previewContent || !previewImg || !imgWrapper) return;// 阻止滚轮事件
const handleWheel = (e: WheelEvent) => {e.preventDefault();e.stopPropagation();
};// 鼠标按下事件
const handleMouseDown = (e: MouseEvent) => {isDraggingRef.current = false;startPosRef.current = { x: e.clientX, y: e.clientY };
};// 鼠标移动事件
const handleMouseMove = (e: MouseEvent) => {// 移动超过5px才认为是拖动if (Math.abs(e.clientX - startPosRef.current.x) > 5 ||Math.abs(e.clientY - startPosRef.current.y) > 5) {isDraggingRef.current = true;}
};// 鼠标松开事件
const handleMouseUp = () => {// 如果是拖动操作,则设置一个短暂的时间窗口忽略点击if (isDraggingRef.current) {setTimeout(() => {isDraggingRef.current = false;}, 100);}
};// 点击事件处理
const handleClick = (e: MouseEvent) => {// 如果是拖动后的点击,则忽略if (isDraggingRef.current) {e.preventDefault();e.stopPropagation();return;}if (!actionsRef.current) return;setIsZoomed((prev) => {const newState = !prev;previewImg.style.cursor = newState ? 'grab' : 'zoom-in';if (newState) {actionsRef.current.onZoomIn();} else {actionsRef.current.onZoomOut();}return newState;});
};previewImg.style.cursor = isZoomed ? 'grab' : 'zoom-in';
previewContent.addEventListener('wheel', handleWheel, { passive: false });
imgWrapper.addEventListener('mousedown', handleMouseDown);
imgWrapper.addEventListener('mousemove', handleMouseMove);
imgWrapper.addEventListener('mouseup', handleMouseUp);
previewImg.addEventListener('click', handleClick);return () => {previewContent.removeEventListener('wheel', handleWheel);imgWrapper.removeEventListener('mousedown', handleMouseDown);imgWrapper.removeEventListener('mousemove', handleMouseMove);imgWrapper.removeEventListener('mouseup', handleMouseUp);previewImg.removeEventListener('click', handleClick);previewImg.style.cursor = '';
};

}, [visible, isZoomed]);

return (
<>


<div className={styles.documentation} dangerouslySetInnerHTML={{ __html: processedHtml }} />

<Image
src={imageSrc}
rootClassName=“preview-images”
preview={{
visible,
src: imageSrc,
movable: isZoomed,
onVisibleChange: (vis) => setVisible(vis),
toolbarRender: (_: any, { actions }: any) => {
actionsRef.current = actions;
return null;
},
scaleStep: 0.5,
maxScale: 5,
}}
/>
</>
);
};

export default PreviewHtmlWithImages;

http://www.xdnf.cn/news/2911.html

相关文章:

  • 人事管理系统6
  • react-native 安卓APK打包流程
  • 【Linux学习笔记】进程替换和自定义shell
  • 自动化立库/AGV物流仿真详细步骤
  • MarkItDown:如何高效将各类文档转换为适合 LLM 处理的 Markdown 格式
  • Objective-C Block 底层原理深度解析
  • Hearts of Iron IV 钢铁雄心 4 [DLC 解锁] [Windows SteamOS macOS]
  • 基于tabula对pdf中多个excel进行识别并转换成word中的优化(四)
  • 防爆风扇储能轴流风机风量风压如何保障通风安全?
  • dify1.3.1更新又给我们带来了什么?
  • 已知条件概率,反推设计值
  • Vue3取消网络请求的方法(AbortController)
  • android开发中的多线程、数据存储同步功能实现方案和应用场景
  • 【SpringBoot】基于MybatisPlus的博客管理系统(1)
  • 常见的硬盘分类
  • SpringBoot、微服务与AI场景题深度解析
  • neo4j基础操作:命令行增删改查
  • java web 过滤器
  • 华为云IAM用户权限设置主要有哪些问题需要注意?
  • 医疗生态全域智能化:从技术革新到价值重塑的深度探析
  • 激光驱鸟:以科技重构生态防护边界
  • JavaAPI — 包装类与正则表达式
  • 从厨房到云端:从预制菜到云原生
  • kotlin flatMap 变换函数的特点和使用场景
  • SpringBoot Actuator未授权访问漏洞的全面解析与解决方案
  • 【uom】 0 配置文件(Cargo.toml)
  • vscode chrome调试怎么在所有浏览器都好使
  • jmeter-Beashell获取请求body data
  • 音视频之H.265/HEVC网络适配层
  • 打造惊艳的渐变色下划线动画:CSS实现详解