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;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值