React 图片预览插件 rc-viewer @hanyk/rc-viewer

本文分享了如何在React中使用rc-viewer组件实现图片预览,并记录了遇到的问题与解决方案,包括图片路径处理、预览顺序混乱及关闭操作的优化。

最近一个需求是图片要实现预览并且可以上下切换,react接触不是很久,查了好多资料,最终对@hanyk/rc-viewer下手,jquery用多了伙伴都知道viewer.js,一个很强大的图片预览插件。

rc-viewer就是viewer.js的react版,我分享一下自己使用rc-viewer遇到的问题,希望对要用这个组件的小伙伴有帮助。

一、安装@hanyk/rc-viewer,安装命令如下:

npm install @hanyk/rc-viewer

二、引入

import RcViewer from '@hanyk/rc-viewer';

具体代码如下:

import React, { useState, useEffect } from 'react';
import { UploadOutlined } from '@ant-design/icons';
import { Upload, message, Button, Popover } from 'antd';
import RcViewer from '@hanyk/rc-viewer';

export default function Input(p) {
  const [fileArr, setFileList] = useState([]);  // 附件
  const [previewImgUrl, setPreviewImgUrl] = useState([]);   // 拼接图片路径
  const [preview, setPreview] = useState(null); //预览
  useEffect(() => {
     let imgUrl = [];
     let list =[
        {
        "sFileName": "d009b3de9c82d158d5fdc0c2cf768ddebd3e42cc.jpeg",
        "sFilePath": "group1/M00/09/2E/Cu5rImIceUSAbjK7AAB6wZb2_fI76.jpeg",
        "id": 1,
    },
    {
        "sFileName": "eaafae4424e7596718256329f395e6fe.jpeg",
        "sFilePath": "group1/M00/09/2E/Cu5rImIceVWAOxJaAAAqjqO_OA470.jpeg",
        "id": 2,
  
    },
    {
        "sFileName": "gonggao.png",
        "sFilePath": "group1/M00/09/2E/Cu5rImIceV6ANatPAABNizbaqCc515.png",
        "id": 3,
    }
   ]
   setFileList([...list]);
   for(var i=0;i < list.length;i++){
      let  dd = `http://127.0.0.1:8888/` + list[i].sFilePath;
      imgUrl.push(dd)
   }
   setPreviewImgUrl([...imgUrl])
  }, []);

  // 图片预览
  const options = {
    // 是否显示下面工具栏 1 显示 0 隐藏
    // toolbar: 1,
    navbar: false,       //关闭缩略图
    fullscreen: false,   //播放全屏
    loop: false,         //是否循环 上一个 下一个
    minWidth:'',
    minHeight:'',
    toolbar: {
      zoomIn: { size: 'large' },  //放大
      zoomOut: { size: 'large' }, //缩小
      reset: { size: 'large' },   //重置
      prev: { show: true, size: 'large', }, //上一张
      play: { show: false, size: 'large', },  //播放
      next: { show: true, size: 'large', },  //下一张
      rotateLeft: { size: 'large' },  //左旋转
      rotateRight: { size: 'large' }, //右旋转
      flipHorizontal: { size: 'large' },  //左右翻转
      flipVertical: { size: 'large' },    //上下翻转
    },
    // 关闭时的回调
    hide() {
      setPreviewImgUrl([]);
      let imgUrl = [];
      for(var i=0;i < fileArr.length;i++){
        let  dd = `http://127.0.0.1:8888/` + fileArr[i].sFilePath;
        imgUrl.push(dd);
      }
      //赋值给previewImgUrl
      setPreviewImgUrl([...imgUrl]);
    },
  };
  const previewImg = (fileObj) => {
    let imgUrls = [];
    let url = `http://10.238.107.43:8888/` + fileObj.sFilePath;
    imgUrls.push(url);
    for(var i=0;i < fileArr.length;i++){
      let  dd = `http://127.0.0.1:8888/` + fileArr[i].sFilePath;
      if(imgUrls.indexOf(dd) === -1){
        imgUrls.push(dd)
      }
    }
    //赋值给previewImgUrl
    setPreviewImgUrl([...imgUrls]);
    if (preview) {
      preview.viewer.show();
    }
  }
  return (
    <div>
      {fileArr &&
      fileArr.length > 0 &&(
        <>
          <div style={{ display: "none" }}>
            <RcViewer
              options={options}
              ref={(v) => {
                setPreview(v);
              }}
            >
              <ul id="images">
                <li>
                   {previewImgUrl.map((item,index) => {
                    return (
                      <div key={index}>
                        <img src={item} alt="" />
                      </div>
                    )}
                  )}
                </li>
              </ul>
            </RcViewer>
          </div>
          {fileArr.map((item, index) => {
           return (
            <div key={index} className="ant-file-content">
              <img src={getFileType(item.sFileName)} className="ant-image" />
              <a
              key={index}
              href="javascript:void(0)"
              rel="noopener noreferrer"
              className="ant-text"
              onClick={() => previewImg(item)}
              >
                 {item.sFileName}
              </a>
              <Popover content="预览" title="" trigger="hover">
                  <img
                  src={previewIcon}
                  className="ant-image"
                  onClick={() => previewImg(item)}
                  ></img>
              </Popover>
            </div>
            );
          }
          )}
        </>
    )}
    </div>
  );
}

因为后端传过来的数据不止包含图片路径,所以我把传过来的数据拿到路径重新拼了个只有路径的数组,再进行预览。

在这个过程中有两个坑给大家分享一下:

1.刚开始我的图片路径拼接是在点击预览事件的时候处理的,但是发现第一次点击预览,函数走了,预览失败,需要点击第二次才可以,最后将图片路径拼接在组件初始化的时候也处理了一下,次问题解决;

2.因为我这边图片预览的list和附件展示的list是分开的,导致上下切换的时候,顺序混乱,点击预览的并非自己所点的图片,一直以为是自己处理图片数组有问题,后面发现是在关闭预览的时候没有把图片数组置空,于是就在关闭的时候置空了一下图片数组,该问题解决了。

3.在关闭预览的时候,我又处理了一下图片路径数组,避免在关闭置空数据后再打开需要点两次;

关于@hanyk/rc-viewer,我目前没有找到中文的文档,英文文档如下:

@hanyk/rc-viewer - npm

里面有options的配置,但是我个人感觉可以参考一下viewer.js里的options配置,下面分享的这个博客里options配置说的比较全,可以参考一下。

Viewer.js的使用_神奇的酱油的博客-优快云博客_viewer.js

我实现的效果:

 

 

 

 希望对小伙伴有帮助,有不正确的地方也欢迎指出。

 

 

D:\AwesomeProject>npm ls react AwesomeProject@0.0.1 D:\AwesomeProject +-- @ant-design/icons-react-native@2.3.2 | `-- react@17.0.2 deduped +-- @ant-design/react-native@5.4.2 | +-- @floating-ui/react-native@0.10.7 | | `-- react@17.0.2 deduped | +-- rc-field-form@2.7.0 | | `-- react@17.0.2 deduped | +-- rc-util@5.44.4 | | `-- react@17.0.2 deduped | +-- react-native-collapsible@1.6.2 | | `-- react@17.0.2 deduped | +-- react-native-modal-popover@2.1.3 | | `-- react@17.0.2 deduped | `-- react@17.0.2 deduped +-- @react-native-community/cameraroll@4.1.2 | `-- react@17.0.2 deduped +-- @react-native-community/masked-view@0.1.11 | `-- react@17.0.2 deduped +-- @react-native-community/segmented-control@2.2.2 | `-- react@17.0.2 deduped +-- @react-native-community/slider@3.0.3 | `-- react@17.0.2 deduped +-- @react-native-community/viewpager@4.2.4 | `-- react@17.0.2 deduped +-- @react-native-picker/picker@1.16.8 | `-- react@17.0.2 deduped +-- @react-navigation/native-stack@6.11.0 | +-- @react-navigation/elements@1.3.31 | | `-- react@17.0.2 deduped | `-- react@17.0.2 deduped +-- @react-navigation/native@6.1.18 | +-- @react-navigation/core@6.4.17 | | +-- react@17.0.2 deduped | | `-- use-latest-callback@0.2.4 | | `-- react@17.0.2 deduped invalid: "^16.0.0" from node_modules/react-native-elements/node_modules/react-native-ratings | `-- react@17.0.2 deduped +-- @rneui/base@4.0.0-rc.8 | `-- react-native-ratings@8.1.0 | `-- react@17.0.2 deduped +-- react-native-elements@1.2.7 | `-- react-native-ratings@6.5.0 | `-- react@17.0.2 deduped invalid: "^16.0.0" from node_modules/react-native-elements/node_modules/react-native-ratings +-- react-native-exception-handler@2.10.10 | `-- react@17.0.2 deduped +-- react-native-image-picker@3.8.1 | `-- react@17.0.2 deduped +-- react-native-image-zoom-viewer@3.0.1 | +-- react-native-image-pan-zoom@2.1.12 | | `-- react@17.0.2 deduped invalid: "^16.0.0" from node_modules/react-native-elements/node_modules/react-native-ratings | `-- react@17.0.2 deduped +-- react-native-linear-gradient@2.8.3 | `-- react@17.0.2 deduped +-- react-native-permissions@3.8.0 | `-- react@17.0.2 deduped +-- react-native-reanimated@2.14.4 invalid: ">=3.10.1" from node_modules/@ant-design/react-native | `-- react@17.0.2 deduped +-- react-native-safe-area-context@3.3.2 | `-- react@17.0.2 deduped +-- react-native-screens@3.15.0 | +-- react-freeze@1.0.4 | | `-- react@17.0.2 deduped invalid: "^16.0.0" from node_modules/react-native-elements/node_modules/react-native-ratings | `-- react@17.0.2 deduped +-- react-native-svg-charts@5.4.0 | `-- react@17.0.2 deduped +-- react-native-webview@11.26.1 | `-- react@17.0.2 deduped +-- react-native@0.68.0 | +-- react-shallow-renderer@16.14.1 | | `-- react@17.0.2 deduped invalid: "^16.0.0" from node_modules/react-native-elements/node_modules/react-native-ratings | +-- react@17.0.2 deduped | `-- use-subscription@1.11.0 | +-- react@17.0.2 deduped invalid: "^16.0.0" from node_modules/react-native-elements/node_modules/react-native-ratings | `-- use-sync-external-store@1.5.0 | `-- react@17.0.2 deduped invalid: "^16.0.0" from node_modules/react-native-elements/node_modules/react-native-ratings +-- react-redux@7.2.9 | `-- react@17.0.2 deduped +-- react-test-renderer@17.0.2 | `-- react@17.0.2 deduped `-- react@17.0.2 npm error code ELSPROBLEMS npm error invalid: react@17.0.2 D:\AwesomeProject\node_modules\react npm error A complete log of this run can be found in: C:\Users\临时\AppData\Local\npm-cache\_logs\2025-08-01T01_11_42_653Z-debug-0.log
最新发布
08-02
显示acat-app@1.0.0 D:\phpEnv\phpEnv\www\acat\expoApp\acat-app ├── @babel/core@7.25.2 ├── @babel/plugin-proposal-class-properties@7.18.6 ├── @babel/plugin-proposal-decorators@7.25.9 ├── @expo/vector-icons@14.0.2 ├── @react-native-async-storage/async-storage@2.1.2 ├── @react-navigation/bottom-tabs@7.2.0 ├── @react-navigation/native@7.0.14 ├── @types/axios@0.14.4 ├── @types/jest@29.5.12 ├── @types/lodash.debounce@4.0.9 ├── @types/node@22.13.14 ├── @types/react-test-renderer@18.3.0 ├── @types/react@18.3.12 ├── @types/three@0.176.0 ├── @types/uuid@10.0.0 ├── axios@1.8.4 ├── expo-av@15.1.4 ├── expo-blur@14.0.3 ├── expo-clipboard@7.0.1 ├── expo-constants@17.0.8 ├── expo-font@13.0.4 ├── expo-gl@13.4.0 ├── expo-haptics@14.0.1 ├── expo-image-picker@16.0.6 ├── expo-linking@7.0.5 ├── expo-notifications@0.31.2 ├── expo-router@4.0.19 ├── expo-splash-screen@0.29.22 ├── expo-status-bar@2.0.1 ├── expo-symbols@0.2.2 ├── expo-system-ui@4.0.8 ├── expo-three@8.0.0 ├── expo-web-browser@14.0.2 ├── expo@52.0.39 ├── jest-expo@52.0.6 ├── jest@29.2.1 ├── lodash.debounce@4.0.8 ├── mobx-react-lite@4.1.0 ├── mobx@6.13.7 ├── nativewind@4.1.23 ├── patch-package@8.0.0 ├── react-dom@18.3.1 ├── react-native-gesture-handler@2.20.2 ├── react-native-image-picker@8.2.0 ├── react-native-image-zoom-viewer@3.0.1 ├── react-native-modal@14.0.0-rc.1 ├── react-native-permissions@3.9.0 ├── react-native-reanimated@3.16.2 ├── react-native-safe-area-context@4.12.0 ├── react-native-screens@4.4.0 ├── react-native-uuid@2.0.3 ├── react-native-web@0.19.13 ├── react-native-webview@13.12.5 ├── react-native@0.76.7 ├── react-test-renderer@18.3.1 ├── react@18.3.1 ├── tailwindcss@3.4.17 ├── three@0.166.1 ├── typescript@5.3.3 └── uuid@11.1.0 PS D:\phpEnv\phpEnv\www\acat\expoApp\acat-app> 是什么意思
05-22
{ "name": "AwesomeProject", "version": "0.0.1", "private": true, "scripts": { "android": "react-native run-android", "ios": "react-native run-ios", "start": "react-native start", "test": "jest", "lint": "eslint .", "postinstall": "patch-package", "generate:icons": "node generateIconMap.js" }, "dependencies": { "@ant-design/icons-react-native": "^2.3.2", "@ant-design/react-native": "^5.1.0", "@react-native-async-storage/async-storage": "^1.19.0", "@react-native-community/cameraroll": "^4.0.0", "@react-native-community/masked-view": "0.1.11", "@react-native-community/segmented-control": "^2.1.1", "@react-native-community/slider": "^3.0.3", "@react-native-community/viewpager": "^4.1.6", "@react-native-picker/picker": "^1.16.8", "@react-navigation/native": "^6.1.7", "@react-navigation/native-stack": "^6.9.12", "@rneui/base": "^4.0.0-rc.8", "@rneui/themed": "^4.0.0-rc.8", "d3-shape": "^1.3.7", "minio": "^7.0.15", "react": "17.0.2", "react-native": "0.68.0", "react-native-background-timer": "^2.4.1", "react-native-device-info": "^8.1.3", "react-native-elements": "^1.2.7", "react-native-exception-handler": "^2.10.10", "react-native-fs": "^2.18.0", "react-native-gesture-handler": "2.14.0", "react-native-get-location": "^2.1.0", "react-native-get-random-values": "^1.4.0", "react-native-image-picker": "^3.8.1", "react-native-image-zoom-viewer": "^3.0.1", "react-native-linear-gradient": "^2.8.3", "react-native-modal-dropdown": "^1.0.2", "react-native-permissions": "3.8.0", "react-native-reanimated": "2.14.4", "react-native-safe-area-context": "3.3.2", "react-native-screens": "3.15.0", "react-native-signature-capture": "^0.4.12", "react-native-sound": "^0.11.1", "react-native-sqlite-storage": "^4.1.0", "react-native-svg-charts": "5.4.0", "react-native-table-component": "^1.2.2", "react-native-vector-icons": "^9.2.0", "react-native-webview": "^11.26.0", "react-redux": "^7.2.0", "redux": "^4.0.5", "redux-thunk": "^2.3.0" }, "devDependencies": { "@babel/core": "^7.12.9", "@babel/runtime": "^7.12.5", "@react-native-community/cli-doctor": "^10.0.0", "@react-native-community/eslint-config": "^2.0.0", "@react-navigation/native": "^6.1.7", "@react-navigation/native-stack": "^6.9.12", "babel-jest": "^26.6.3", "babel-plugin-import": "^1.13.8", "eslint": "^7.32.0", "jest": "^26.6.3", "metro-react-native-babel-preset": "^0.68.0", "patch-package": "^6.4.7", "react-native-version-check": "^3.5.0", "react-test-renderer": "17.0.2" }, "jest": { "preset": "react-native" } } 为什么 npm i 后就会报错D:\AwesomeProject>npm i npm error code ERESOLVE npm error ERESOLVE unable to resolve dependency tree npm error npm error While resolving: AwesomeProject@0.0.1 npm error Found: react-native-gesture-handler@1.10.3 npm error node_modules/react-native-gesture-handler npm error react-native-gesture-handler@"^1.10.3" from the root project npm error npm error Could not resolve dependency: npm error peer react-native-gesture-handler@">=2.14.0" from @ant-design/react-native@5.4.2 npm error node_modules/@ant-design/react-native npm error @ant-design/react-native@"^5.1.0" from the root project npm error npm error Fix the upstream dependency conflict, or retry npm error this command with --force or --legacy-peer-deps npm error to accept an incorrect (and potentially broken) dependency resolution. npm error npm error npm error For a full report see: npm error C:\Users\临时\AppData\Local\npm-cache\_logs\2025-07-31T05_28_06_947Z-eresolve-report.txt npm error A complete log of this run can be found in: C:\Users\临时\AppData\Local\npm-cache\_logs\2025-07-31T05_28_06_947Z-debug-0.log
08-01
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值