react 实现api打开LightGallery组件

30 阅读1分钟

背景

1.列表页有一些图片素材,希望点击图片可以预览大图,这里使用的是LightGallery组件 2.在每个单元格中使用LightGallery组件有很严重的性能问题

解决方案

  1. 列表页只渲染简单的图片信息
  2. 点击图片时打开lightGallery

代码方案

  1. index.tsx
import { forwardRef, useCallback, useMemo, useRef, useImperativeHandle } from 'react';
import LightGallery, { LightGalleryProps } from 'lightgallery/react';
import lgThumbnail from 'lightgallery/plugins/thumbnail';
import lgVideo from 'lightgallery/plugins/video';
import lgZoom from 'lightgallery/plugins/zoom';
import lgFull from 'lightgallery/plugins/fullscreen';
import lgAutoplay from 'lightgallery/plugins/autoplay';
import lgRotate from 'lightgallery/plugins/rotate';
import openLg from './open';

type DynamicEl = NonNullable<LightGalleryProps['dynamicEl']>;
export type GalleryItem = DynamicEl[number];

const RenderItem = (item: GalleryItem) => {
  const { src, thumb, subHtml, size, title, responsive } = item;

  /** 处理缩略图显示 */
  const thumbUrl = useMemo(() => {
    if (thumb) return thumb;

    const isOss = src && (src.includes('oss-cn') || src.includes('oss-cloud'));
    return isOss ? `${src}?x-oss-process=image/resize,w_288/quality,q_100` : src;
  }, [src, thumb]);

  return (
    <div
      data-lg-size={size}
      data-src={src}
      data-thumb={thumb}
      data-title={title}
      data-sub-html={subHtml}
      data-responsive={responsive}
      data-lg-background-color='rgba(0,0,0,0.85)'
      className=' w-24 h-24 rounded-8 overflow-hidden cursor-pointer'
    >
      <img className=' w-full h-full object-cover' src={thumbUrl} alt={subHtml} />
    </div>
  );
};

/** 图片预览组件 */
const Lg: any = forwardRef(
  ({ items, children, onInit, ...restProps }: LightGalleryProps & { items?: DynamicEl }, ref) => {
    const lightGalleryRef = useRef<any>(null);

    const innerOnInit = useCallback(
      (detail: Parameters<NonNullable<LightGalleryProps['onInit']>>[0]) => {
        if (detail) {
          lightGalleryRef.current = detail.instance;
        }
        onInit?.(detail);
      },
      [],
    );

    const getItems = useCallback(() => {
      return items?.map((item, index) => <RenderItem key={item.id || index} {...item} />);
    }, [items]);

    useImperativeHandle(ref, () => lightGalleryRef.current);

    return (
      <LightGallery
        onInit={innerOnInit}
        plugins={[lgZoom, lgThumbnail, lgFull, lgVideo, lgAutoplay, lgRotate]}
        infiniteZoom
        showZoomInOutIcons
        actualSize={false}
        closeOnTap
        mousewheel
        elementClassNames=' flex items-start flex-wrap gap-4'
        {...restProps}
      >
        {children || getItems()}
      </LightGallery>
    );
  },
);

Lg.displayName = 'LightGallery';
Lg.open = openLg;

export default Lg as any as React.ForwardRefExoticComponent<
  LightGalleryProps & {
    items?: DynamicEl;
  } & React.RefAttributes<unknown>
> & {
  open: typeof openLg;
};

  1. open.tsx
import { createRoot, Root } from 'react-dom/client';
import { useRef } from 'react';
import { InitDetail } from 'lightgallery/lg-events';
import LG, { GalleryItem } from './index';

const CONTAINER_ID = 'LIGHT_GALLERY_WRAPPER';

let lightGalleryContainer: Root | null = null;

function LightGalleryWrapper(props: { index: number; items: GalleryItem[] }) {
  const lightGalleryRef = useRef<any>(null);

  const handleInit = (detail: InitDetail) => {
    lightGalleryRef.current = detail.instance;
    lightGalleryRef.current?.openGallery(props.index);
  };

  const handleAfterClose = () => {
    lightGalleryRef.current?.destroy();
    lightGalleryContainer?.unmount();
    document.getElementById(CONTAINER_ID)?.remove();
  };

  return <LG items={props.items} onInit={handleInit} onAfterClose={handleAfterClose} />;
}
export default function openLg(index: number, items: GalleryItem[]) {
  const div = document.createElement('div');
  div.id = CONTAINER_ID;
  document.body!.append(div);
  const root = createRoot(div);
  lightGalleryContainer = root;
  root.render(<LightGalleryWrapper index={index} items={items} />);
}