源码还是得一行一行阅读,好多自定义的函数,得找到相应的用法
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;