Focal points (#6520)
* Add focus param to media API, center thumbnails on focus point * Add UI for setting a focal point * Improve focal point icon on upload item * Use focal point in upload preview * Add focalPoint property to ActivityPub * Don't show focal point button for non-image attachments
This commit is contained in:
parent
d3a62d2637
commit
90f12f2e5a
15 changed files with 307 additions and 30 deletions
|
@ -12,6 +12,26 @@ const messages = defineMessages({
|
|||
toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Toggle visibility' },
|
||||
});
|
||||
|
||||
const shiftToPoint = (containerToImageRatio, containerSize, imageSize, focusSize, toMinus) => {
|
||||
const containerCenter = Math.floor(containerSize / 2);
|
||||
const focusFactor = (focusSize + 1) / 2;
|
||||
const scaledImage = Math.floor(imageSize / containerToImageRatio);
|
||||
|
||||
let focus = Math.floor(focusFactor * scaledImage);
|
||||
|
||||
if (toMinus) focus = scaledImage - focus;
|
||||
|
||||
let focusOffset = focus - containerCenter;
|
||||
|
||||
const remainder = scaledImage - focus;
|
||||
const containerRemainder = containerSize - containerCenter;
|
||||
|
||||
if (remainder < containerRemainder) focusOffset -= containerRemainder - remainder;
|
||||
if (focusOffset < 0) focusOffset = 0;
|
||||
|
||||
return (focusOffset * -100 / containerSize) + '%';
|
||||
};
|
||||
|
||||
class Item extends React.PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
|
@ -24,6 +44,8 @@ class Item extends React.PureComponent {
|
|||
index: PropTypes.number.isRequired,
|
||||
size: PropTypes.number.isRequired,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
containerWidth: PropTypes.number,
|
||||
containerHeight: PropTypes.number,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
|
@ -62,7 +84,7 @@ class Item extends React.PureComponent {
|
|||
}
|
||||
|
||||
render () {
|
||||
const { attachment, index, size, standalone } = this.props;
|
||||
const { attachment, index, size, standalone, containerWidth, containerHeight } = this.props;
|
||||
|
||||
let width = 50;
|
||||
let height = 100;
|
||||
|
@ -116,16 +138,40 @@ class Item extends React.PureComponent {
|
|||
let thumbnail = '';
|
||||
|
||||
if (attachment.get('type') === 'image') {
|
||||
const previewUrl = attachment.get('preview_url');
|
||||
const previewUrl = attachment.get('preview_url');
|
||||
const previewWidth = attachment.getIn(['meta', 'small', 'width']);
|
||||
|
||||
const originalUrl = attachment.get('url');
|
||||
const originalWidth = attachment.getIn(['meta', 'original', 'width']);
|
||||
const originalUrl = attachment.get('url');
|
||||
const originalWidth = attachment.getIn(['meta', 'original', 'width']);
|
||||
const originalHeight = attachment.getIn(['meta', 'original', 'height']);
|
||||
|
||||
const hasSize = typeof originalWidth === 'number' && typeof previewWidth === 'number';
|
||||
|
||||
const srcSet = hasSize ? `${originalUrl} ${originalWidth}w, ${previewUrl} ${previewWidth}w` : null;
|
||||
const sizes = hasSize ? `(min-width: 1025px) ${320 * (width / 100)}px, ${width}vw` : null;
|
||||
const sizes = hasSize ? `(min-width: 1025px) ${320 * (width / 100)}px, ${width}vw` : null;
|
||||
|
||||
const focusX = attachment.getIn(['meta', 'focus', 'x']);
|
||||
const focusY = attachment.getIn(['meta', 'focus', 'y']);
|
||||
const imageStyle = {};
|
||||
|
||||
if (originalWidth && originalHeight && containerWidth && containerHeight && focusX && focusY) {
|
||||
const widthRatio = originalWidth / (containerWidth * (width / 100));
|
||||
const heightRatio = originalHeight / (containerHeight * (height / 100));
|
||||
|
||||
let hShift = 0;
|
||||
let vShift = 0;
|
||||
|
||||
if (widthRatio > heightRatio) {
|
||||
hShift = shiftToPoint(heightRatio, (containerWidth * (width / 100)), originalWidth, focusX);
|
||||
} else if(widthRatio < heightRatio) {
|
||||
vShift = shiftToPoint(widthRatio, (containerHeight * (height / 100)), originalHeight, focusY, true);
|
||||
}
|
||||
|
||||
imageStyle.top = vShift;
|
||||
imageStyle.left = hShift;
|
||||
} else {
|
||||
imageStyle.height = '100%';
|
||||
}
|
||||
|
||||
thumbnail = (
|
||||
<a
|
||||
|
@ -134,7 +180,14 @@ class Item extends React.PureComponent {
|
|||
onClick={this.handleClick}
|
||||
target='_blank'
|
||||
>
|
||||
<img src={previewUrl} srcSet={srcSet} sizes={sizes} alt={attachment.get('description')} title={attachment.get('description')} />
|
||||
<img
|
||||
src={previewUrl}
|
||||
srcSet={srcSet}
|
||||
sizes={sizes}
|
||||
alt={attachment.get('description')}
|
||||
title={attachment.get('description')}
|
||||
style={imageStyle}
|
||||
/>
|
||||
</a>
|
||||
);
|
||||
} else if (attachment.get('type') === 'gifv') {
|
||||
|
@ -205,7 +258,7 @@ export default class MediaGallery extends React.PureComponent {
|
|||
}
|
||||
|
||||
handleRef = (node) => {
|
||||
if (node && this.isStandaloneEligible()) {
|
||||
if (node /*&& this.isStandaloneEligible()*/) {
|
||||
// offsetWidth triggers a layout, so only calculate when we need to
|
||||
this.setState({
|
||||
width: node.offsetWidth,
|
||||
|
@ -256,12 +309,12 @@ export default class MediaGallery extends React.PureComponent {
|
|||
if (this.isStandaloneEligible()) {
|
||||
children = <Item standalone onClick={this.handleClick} attachment={media.get(0)} />;
|
||||
} else {
|
||||
children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} onClick={this.handleClick} attachment={attachment} index={i} size={size} />);
|
||||
children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} onClick={this.handleClick} attachment={attachment} index={i} size={size} containerWidth={width} containerHeight={height} />);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='media-gallery' style={style}>
|
||||
<div className='media-gallery' style={style} ref={this.handleRef}>
|
||||
<div className={classNames('spoiler-button', { 'spoiler-button--visible': visible })}>
|
||||
<IconButton title={intl.formatMessage(messages.toggle_visible)} icon={visible ? 'eye' : 'eye-slash'} overlay onClick={this.handleOpen} />
|
||||
</div>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue