1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-06 09:21:54 +00:00

Image zoom rotate (#7425)

* [image file view] added a zoomer(enable users to zoom in/out the image)

* [image file view] added 'rotate'(rotate the image in counter-clockwise direction and save it)

* [image file view] fixup

* [image file view] keep the 'prev/next' icons displayed on top of the image which is zoomed in

* [image file view] improved the display of the 'image saved' tip

* [image file view] don't offer 'zoom in/out' & 'rotate' for images with errors

* [image file view] don't offer 'rotate' for PSD & HEIC files

* [image file view] enable HEIC files to be viewed online
This commit is contained in:
llj
2025-02-08 10:27:34 +08:00
committed by GitHub
parent d65e86731e
commit 3d7b8b3a6b
7 changed files with 161 additions and 10 deletions

View File

@@ -13,7 +13,8 @@ const {
xmindImageSrc // for xmind file
} = window.app.pageOptions;
let previousImageUrl; let nextImageUrl;
let previousImageUrl;
let nextImageUrl;
if (previousImage) {
previousImageUrl = `${siteRoot}lib/${repoID}/file${Utils.encodePath(previousImage)}`;
}
@@ -55,7 +56,7 @@ class FileContent extends React.Component {
// request thumbnails for some files
// only for 'file view'. not for 'history/trash file view'
let thumbnailURL = '';
const fileExtList = ['tif', 'tiff', 'psd'];
const fileExtList = ['tif', 'tiff', 'psd', 'heic'];
if (!repoEncrypted && fileExtList.includes(fileExt)) {
thumbnailURL = `${siteRoot}thumbnail/${repoID}/${thumbnailSizeForOriginal}${Utils.encodePath(filePath)}`;
}
@@ -63,6 +64,17 @@ class FileContent extends React.Component {
// for xmind file
const xmindSrc = xmindImageSrc ? `${siteRoot}${xmindImageSrc}` : '';
const { scale, angle } = this.props;
let style = {};
if (scale && angle != undefined) {
style = { transform: `scale(${scale}) rotate(${angle}deg)` };
} else if (scale) {
style = { transform: `scale(${scale})` };
} else if (angle != undefined) {
style = { transform: `rotate(${angle}deg)` };
}
return (
<div className="file-view-content flex-1 image-file-view">
{previousImage && (
@@ -71,14 +83,16 @@ class FileContent extends React.Component {
{nextImage && (
<a href={nextImageUrl} id="img-next" title={gettext('you can also press →')}><span className="sf3-font sf3-font-down rotate-270 d-inline-block"></span></a>
)}
<img src={xmindSrc || thumbnailURL || rawPath} alt={fileName} id="image-view" onError={this.handleLoadFailure} />
<img src={xmindSrc || thumbnailURL || rawPath} alt={fileName} id="image-view" onError={this.handleLoadFailure} style={ style } />
</div>
);
}
}
FileContent.propTypes = {
tip: PropTypes.string.isRequired,
tip: PropTypes.object.isRequired,
scale: PropTypes.number,
angle: PropTypes.number
};
export default FileContent;

View File

@@ -9,6 +9,7 @@ import ShareDialog from '../dialog/share-dialog';
import { seafileAPI } from '../../utils/seafile-api';
import toaster from '../toast';
import Icon from '../../components/icon';
import ImageZoomer from './image-zoomer';
const propTypes = {
isLocked: PropTypes.bool.isRequired,
@@ -17,13 +18,15 @@ const propTypes = {
isSaving: PropTypes.bool,
needSave: PropTypes.bool,
toggleLockFile: PropTypes.func.isRequired,
toggleDetailsPanel: PropTypes.func.isRequired
toggleDetailsPanel: PropTypes.func.isRequired,
setImageScale: PropTypes.func,
rotateImage: PropTypes.func
};
const {
canLockUnlockFile,
repoID, repoName, repoEncrypted, parentDir, filePerm, filePath,
fileType,
fileType, fileExt,
fileName,
canEditFile, err,
// fileEnc, // for 'edit', not undefined only for some kinds of files (e.g. text file)
@@ -119,6 +122,19 @@ class FileToolbar extends React.Component {
return (
<Fragment>
<div className="d-none d-md-flex justify-content-between align-items-center flex-shrink-0 ml-4">
{(fileType == 'Image' && !err) && (
<>
<ImageZoomer setImageScale={this.props.setImageScale} />
{['psd', 'heic'].indexOf(fileExt) == -1 && (
<IconButton
id="rotate-image"
icon="rotate"
text={gettext('Rotate')}
onClick={this.props.rotateImage}
/>
)}
</>
)}
{fileType == 'PDF' && (
<IconButton
id="seafile-pdf-find"

View File

@@ -25,8 +25,8 @@ const propTypes = {
isSaving: PropTypes.bool,
needSave: PropTypes.bool,
isOnlyofficeFile: PropTypes.bool,
participants: PropTypes.array,
onParticipantsChange: PropTypes.func
setImageScale: PropTypes.func,
rotateImage: PropTypes.func
};
const { isStarred, isLocked, lockedByMe,
@@ -143,6 +143,8 @@ class FileView extends React.Component {
needSave={this.props.needSave}
toggleLockFile={this.toggleLockFile}
toggleDetailsPanel={this.toggleDetailsPanel}
setImageScale={this.props.setImageScale}
rotateImage={this.props.rotateImage}
/>
}
</div>

View File

@@ -0,0 +1,63 @@
import React, { useState, useCallback } from 'react';
import PropTypes from 'prop-types';
import { Button, Input } from 'reactstrap';
import Icon from '../../components/icon';
import '../../metadata/components/data-process-setter/gallery-slider-setter/index.css';
const SCALE_OPTIONS = [0.25, 0.5, 1, 1.5, 2];
const SCALE_MIN = SCALE_OPTIONS[0];
const SCALE_MAX = SCALE_OPTIONS[SCALE_OPTIONS.length - 1];
const ImageZoomer = ({ setImageScale }) => {
const [curScale, setScale] = useState(1);
const scaleImage = useCallback((scale) => {
setImageScale(scale);
}, [setImageScale]);
const changeScale = useCallback((e) => {
const scale = Number(e.target.value);
setScale(scale);
scaleImage(scale);
}, [scaleImage]);
const zoomIn = useCallback(() => {
const scale = SCALE_OPTIONS[SCALE_OPTIONS.indexOf(curScale) + 1];
setScale(scale);
scaleImage(scale);
}, [curScale, scaleImage]);
const zoomOut = useCallback(() => {
const scale = SCALE_OPTIONS[SCALE_OPTIONS.indexOf(curScale) - 1];
setScale(scale);
scaleImage(scale);
}, [curScale, scaleImage]);
return (
<div className='metadata-slider-container image-zoomer ml-0'>
<Button className="metadata-slider-icon-button" onClick={zoomOut} disabled={curScale == SCALE_MIN}>
<Icon symbol='minus_sign' className='metadata-slider-icon' />
</Button>
<Input
type="range"
min={SCALE_MIN}
max={SCALE_MAX}
step="any"
value={curScale}
onChange={changeScale}
className="metadata-slider"
/>
<Button className="metadata-slider-icon-button" onClick={zoomIn} disabled={curScale == SCALE_MAX}>
<Icon symbol='plus_sign' className='metadata-slider-icon' />
</Button>
</div>
);
};
ImageZoomer.propTypes = {
setImageScale: PropTypes.func
};
export default ImageZoomer;