[OHIF-Viewers]医疗数字阅片-医学影像-ViewportDownloadForm.js

ViewportDownloadForm.js

源码还是得一行一行阅读,好多自定义的函数,得找到相应的用法

import React, {
  useRef,
  useCallback,
  useEffect,
  useState,
  createRef,
} from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';

import './ViewportDownloadForm.styl';
import { TextInput, Select, Icon } from '@ohif/ui';
import classnames from 'classnames';

const FILE_TYPE_OPTIONS = [
  {
    key: 'jpg',
    value: 'jpg',
  },
  {
    key: 'png',
    value: 'png',
  },
];

const DEFAULT_FILENAME = 'image';
const REFRESH_VIEWPORT_TIMEOUT = 1000;

const ViewportDownloadForm = ({
  activeViewport,
  onClose,
  updateViewportPreview,
  enableViewport,
  disableViewport,
  toggleAnnotations,
  loadImage,
  downloadBlob,
  defaultSize,
  minimumSize,
  maximumSize,
  canvasClass,
}) => {
  const [t] = useTranslation('ViewportDownloadForm');

  const [filename, setFilename] = useState(DEFAULT_FILENAME); //文件名字,初始值image
  const [fileType, setFileType] = useState('jpg'); // 文件类型

  const [dimensions, setDimensions] = useState({ // 尺寸
    width: defaultSize,
    height: defaultSize,
  });

  const [showAnnotations, setShowAnnotations] = useState(true); //是否显示注释

  const [keepAspect, setKeepAspect] = useState(true); // 是否保持比例
  const [aspectMultiplier, setAspectMultiplier] = useState({ // 比例尺寸
    width: 1,
    height: 1,
  });

  const [viewportElement, setViewportElement] = useState(); // 视图元素
  const [viewportElementDimensions, setViewportElementDimensions] = useState({ //视口元素尺寸
    width: defaultSize,
    height: defaultSize,
  });

  const [downloadCanvas, setDownloadCanvas] = useState({ //下载画布
    ref: createRef(), //DOM关于回调 refs
    // ref: null, //DOM关于回调 refs
    width: defaultSize,
    height: defaultSize,

  });
console.log(downloadCanvas.ref);
  const [viewportPreview, setViewportPreview] = useState({ //视图预览
    src: null,
    width: defaultSize,
    height: defaultSize,
  });

  const [error, setError] = useState({ // 错误信息
    width: false,
    height: false,
    filename: false, //文件名
  });

  const hasError = Object.values(error).includes(true); //有错误,判断error里面是否包含true

  /**
   * useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。
   * @type {React.MutableRefObject<null>}
   */
  const refreshViewport = useRef(null); //刷新视图

  const downloadImage = () => { //下载图片
    downloadBlob(
      filename || DEFAULT_FILENAME,
      fileType,
      viewportElement,
      downloadCanvas.ref.current
    );
  };

  /**
   * @param {object} event - Input change event
   * @param {object}事件-输入更改事件
   * @param {string} dimension - "height" | "width"
   * @param {string}尺寸-“高度” | “宽度”
   */
  // 关于尺寸变化
  const onDimensionsChange = (event, dimension) => {
    const oppositeDimension = dimension === 'height' ? 'width' : 'height';
    const sanitizedTargetValue = event.target.value.replace(/\D/, '');
    const isEmpty = sanitizedTargetValue === ''; //是否为空
    const newDimensions = { ...dimensions };
    const updatedDimension = isEmpty
      ? ''
      : Math.min(sanitizedTargetValue, maximumSize);

    if (updatedDimension === dimensions[dimension]) {
      return;
    }

    newDimensions[dimension] = updatedDimension;

    if (keepAspect && newDimensions[oppositeDimension] !== '') {
      newDimensions[oppositeDimension] = Math.round(
        newDimensions[dimension] * aspectMultiplier[oppositeDimension]
      );
    }

    // In current code, keepAspect is always `true` 在当前代码中,keepAspect始终为true。
    // And we always start w/ a square width/height 我们总是以/一个正方形的宽度/高度开始
    setDimensions(newDimensions); //设定尺寸

    // Only update if value is non-empty //仅在值非空时更新
    if (!isEmpty) {
      setViewportElementDimensions(newDimensions); //设置视口元素尺寸
      setDownloadCanvas(state => ({ //设置下载画布
        ...state,
        ...newDimensions, //新尺寸
      }));
    }
  };

  const error_messages = { // 错误信息
    width: t('minWidthError'),
    height: t('minHeightError'),
    filename: t('emptyFilenameError'),
  };

  const renderErrorHandler = errorType => { // 渲染错误处理程序
    if (!error[errorType]) {
      return null;
    }

    return <div className="input-error">{error_messages[errorType]}</div>;
  };

  const onKeepAspectToggle = () => { //保持纵横切换
    const { width, height } = dimensions;
    const aspectMultiplier = { ...aspectMultiplier };
    if (!keepAspect) {
      const base = Math.min(width, height);
      aspectMultiplier.width = width / base;
      aspectMultiplier.height = height / base;
      setAspectMultiplier(aspectMultiplier);
    }

    setKeepAspect(!keepAspect);
  };

  const validSize = value => (value >= minimumSize ? value : minimumSize); // 有效尺寸
  const loadAndUpdateViewports = useCallback(async () => { // 加载并更新视口
    const { width: scaledWidth, height: scaledHeight } = await loadImage( //载入图片
      activeViewport, // 活动视口
      viewportElement, // 视口元素
      dimensions.width,
      dimensions.height
    );

    toggleAnnotations(showAnnotations, viewportElement); // 是否显示注释,切换注释

    const scaledDimensions = { //比例尺
      height: validSize(scaledHeight), //校验是否为有效尺寸
      width: validSize(scaledWidth),//校验是否为有效尺寸
    };

    setViewportElementDimensions(scaledDimensions); // 设置视口元素尺寸
    setDownloadCanvas(state => ({ //设置下载画布
      ...state,
      ...scaledDimensions,
    }));

    const {
      dataUrl, //数据地址
      width: viewportElementWidth, //视口元素宽度
      height: viewportElementHeight,
    } = await updateViewportPreview( //更新视口预览
      viewportElement, //视口元素
      downloadCanvas.ref.current, //下载画布,访问 Refs
      fileType //文件类型
    );

    setViewportPreview(state => ({ // 设置视口预览
      ...state,
      src: dataUrl,
      width: validSize(viewportElementWidth), //验证宽度是否有效
      height: validSize(viewportElementHeight),
    }));
  }, [ //inputs 变化检测
    activeViewport,//活动视图
    viewportElement,//视口元素
    showAnnotations,//显示注释
    loadImage,//加载图片
    toggleAnnotations,//切换注释
    updateViewportPreview,//更新视口预览
    fileType,//文件类型
    downloadCanvas.ref,//下载画布资源,Ref
    minimumSize,//最小尺寸
    maximumSize,//最大尺寸
    viewportElementDimensions,//视口元素尺寸
  ]);
  /**
   * 通过使用这个 Hook,你可以告诉 React 组件需要在渲染后执行某些操作。React 会保存你传递的函数(我们将它称之为 “effect”),并且在执行 DOM 更新之后调用它。在这个 effect 中,我们设置了 document 的 title 属性,不过我们也可以执行数据获取或调用其他命令式的 API。
   */
  useEffect(() => {
    enableViewport(viewportElement);//启用视口

    return () => {
      disableViewport(viewportElement);//禁用视口  useEffect 可以在组件渲染后实现各种不同的副作用。有些副作用可能需要清除,所以需要返回一个函数:
    };
  }, [disableViewport, enableViewport, viewportElement]);//这里是检测

  useEffect(() => {
    if (refreshViewport.current !== null) {
      clearTimeout(refreshViewport.current);
    }

    refreshViewport.current = setTimeout(() => {
      refreshViewport.current = null;
      loadAndUpdateViewports();
    }, REFRESH_VIEWPORT_TIMEOUT);//刷新视口超时时间
  }, [ //检测
    activeViewport,
    viewportElement,
    showAnnotations,
    dimensions,
    loadImage,
    toggleAnnotations,
    updateViewportPreview,
    fileType,
    downloadCanvas.ref,
    minimumSize,
    maximumSize,
  ]);

  useEffect(() => { //Effect Hook 可以让你在函数组件中执行副作用操作
    const { width, height } = dimensions; // 尺寸
    const hasError = { //错误信息
      width: width < minimumSize, //小于最小尺寸
      height: height < minimumSize,
      filename: !filename,
    };

    setError({ ...hasError });//设置错误信息 对象的扩展运算符(...)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。
  }, [dimensions, filename, minimumSize]);
  return (
      console.log(viewportPreview.src),
      console.log(viewportElement),
      // console.log(activeViewport),
      // console.log(downloadCanvas.ref),
    <div className="ViewportDownloadForm" >
      <div
        style={{
          height: viewportElementDimensions.height,
          width: viewportElementDimensions.width,
          position: 'absolute',
          left: '9999px',
        }}
        ref={ref => setViewportElement(ref)}
      >
        <canvas
          className={canvasClass} //cornerstone-canvas
          style={{
            height: downloadCanvas.height,
            width: downloadCanvas.width,
            display: 'block',
          }}
          width={downloadCanvas.width}
          height={downloadCanvas.height}
          ref={downloadCanvas.ref}
        ></canvas>
      </div>

      {viewportPreview.src ? (
        <div className="preview" data-cy="image-preview">
          <div className="preview-header"> {t('imagePreview')}</div>
          <img
            className="viewport-preview"
            src={viewportPreview.src}
            alt={t('imagePreview')}
            data-cy="image-preview"
            data-cy="viewport-preview-img"
          />
        </div>
      ) : (
        <div className="loading-image">
          <Icon name="circle-notch" className="icon-spin" />
          {t('loadingPreview')}
        </div>
      )}

      <div className="actions">
        <div className="action-save">
          <button
            disabled={hasError}
            onClick={downloadImage}
            className="btn btn-primary"
            data-cy="download-btn"
          >
            {t('Buttons:Download')}
          </button>
        </div>
      </div>
    </div>
  );
};

ViewportDownloadForm.propTypes = {
  onClose: PropTypes.func.isRequired,
  activeViewport: PropTypes.object,
  updateViewportPreview: PropTypes.func.isRequired,
  enableViewport: PropTypes.func.isRequired,
  disableViewport: PropTypes.func.isRequired,
  toggleAnnotations: PropTypes.func.isRequired,
  loadImage: PropTypes.func.isRequired,
  downloadBlob: PropTypes.func.isRequired,
  /** A default width & height, between the minimum and maximum size */
  defaultSize: PropTypes.number.isRequired,
  minimumSize: PropTypes.number.isRequired,
  maximumSize: PropTypes.number.isRequired,
  canvasClass: PropTypes.string.isRequired,
};

export default ViewportDownloadForm;

 

 
网名:浩秦;
邮箱:root#landv.pw;
博客:landv.cnblogs.com;
只要我能控制一個國家的貨幣發行,我不在乎誰制定法律。金錢一旦作響,壞話隨之戛然而止。
posted @ 2020-07-11 12:14  landv  阅读(243)  评论(0编辑  收藏  举报