【图片预览】怎么在 Markdown 文档解析的 html 中实现图片预览功能?
问题背景:大模型的回答都是流式输出,输出的语料大都是 md 格式,前端拿到这些 md 数据后解析为 html 用于流式显示。现在在模型回答的内容中新增一个图片点击预览功能,方便用户查看图片。
解决原理:事件循环机制。
基于事件循环机制和事件委托,我们可以实现一个高效的图片点击预览功能,即使图片是动态加载的。以下是实现原理:
事件委托:利用事件冒泡机制,在父容器上监听点击事件,而不是直接给每个图片绑定事件
动态检测:无论图片何时加载完成,都能通过事件目标判断是否点击了图片
懒处理:不需要预先扫描DOM中的图片,只在点击发生时检查目标元素
示例代码:
import {
createElement,
CSSProperties,
ReactHTML,
Fragment,
useMemo,
useEffect,
useState,
SetStateAction,
} from 'react';
import classNames from 'classnames';
import { parse } from '@sobot/utils/markdown';
import { addTargetToHTMLLinks } from '@/utils';
import { MessageSide, IMessage } from '../types';
import styles from './index.less';
import { Image } from '@sobot/soil-ui';
interface CursorProps {
side?: MessageSide;
status?: string;
}
// 光标效果
const renderCursor = ({ status, side }: CursorProps): string => {
const dom =
status === 'pending' && side !== 'right'
? `<span class=${styles.cursor} />`
: '';
return dom;
};
export interface RichTextProps extends CursorProps {
tag?: keyof ReactHTML;
content?: string;
className?: string;
style?: CSSProperties;
msgType?: IMessage['msgType'];
previewImg?: boolean;
}
export default function RichText({
status,
side,
tag = 'span',
className,
content,
msgType,
previewImg = false, // 是否预览图片
...props
}: RichTextProps) {
const [viewImgSrc, setViewImgSrc] = useState<string | undefined>();
useEffect(() => {
return () => {
setViewImgSrc(undefined);
};
}, []);
const onClickImg = (e: React.MouseEvent<HTMLElement>) => {
const target = e.target as HTMLElement;
if (target.nodeName === 'IMG') {
setViewImgSrc((target as HTMLImageElement).src);
}
};
const html = useMemo(() => {
switch (msgType) {
// 图片
case 'IMG': {
const str = `<img src="${content}" />`;
return parse(str);
}
default:
const cursorStr = renderCursor({ status, side });
const _html = parse((content || '') + cursorStr);
return addTargetToHTMLLinks(_html);
}
}, [content, status]);
const renderNode = createElement(tag, {
...props,
className: classNames(styles.html, className),
dangerouslySetInnerHTML: { __html: html },
onClick: onClickImg,
});
return (
<>
{renderNode}
{previewImg ? (
<div style={{ display: 'none' }}>
<Image
width={0}
preview={{
visible: !!viewImgSrc,
src: viewImgSrc,
onVisibleChange: (value) => {
setViewImgSrc(undefined);
},
}}
/>
</div>
) : null}
</>
);
}
本文来自博客园,作者:muling9955,转载请注明原文链接:https://www.cnblogs.com/muling-blog/p/18959402