Implement translation

Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
This commit is contained in:
Šimon Brandner 2020-12-20 17:40:16 +01:00
parent 8de019c054
commit f9884b1cc7
2 changed files with 97 additions and 53 deletions

View file

@ -33,7 +33,7 @@ limitations under the License.
} }
.mx_ImageView_imageBox { .mx_ImageView_imageBox {
overflow: scroll; overflow: hidden;
display: flex; display: flex;
padding: 0 50px 50px 50px; padding: 0 50px 50px 50px;
flex-grow: 1; flex-grow: 1;
@ -41,6 +41,10 @@ limitations under the License.
.mainImage { .mainImage {
margin: auto; margin: auto;
&:hover {
cursor: grab;
}
} }
.mx_ImageView_content img { .mx_ImageView_content img {

View file

@ -47,11 +47,19 @@ export default class ImageView extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
rotationDegrees: 0, rotation: 0,
zoom: 100, zoom: 100,
translationX: 0,
translationY: 0,
moving: false,
}; };
} }
initX = 0;
initY = 0;
lastX = 0;
lastY = 0;
componentDidMount() { componentDidMount() {
// We have to use addEventListener() because the listener // We have to use addEventListener() because the listener
// needs to be passive in order to work with Chromium // needs to be passive in order to work with Chromium
@ -74,8 +82,18 @@ export default class ImageView extends React.Component {
if (ev.ctrlKey) { if (ev.ctrlKey) {
ev.stopPropagation(); ev.stopPropagation();
ev.preventDefault(); ev.preventDefault();
const newZoom =this.state.zoom - ev.deltaY;
if (newZoom <= 100) {
this.setState({ this.setState({
zoom: this.state.zoom - ev.deltaY, zoom: 100,
translationX: 0,
translationY: 0,
});
return;
}
this.setState({
zoom: newZoom,
}); });
} }
} }
@ -109,65 +127,72 @@ export default class ImageView extends React.Component {
return name; return name;
} }
rotateCounterClockwise = () => { onRotateCounterClockwiseClick = () => {
const cur = this.state.rotationDegrees; const cur = this.state.rotation;
const rotationDegrees = (cur - 90) % 360; const rotationDegrees = (cur - 90) % 360;
this.setState({ rotationDegrees }); this.setState({ rotation: rotationDegrees });
}; };
rotateClockwise = () => { onRotateClockwiseClick = () => {
const cur = this.state.rotationDegrees; const cur = this.state.rotation;
const rotationDegrees = (cur + 90) % 360; const rotationDegrees = (cur + 90) % 360;
this.setState({ rotationDegrees }); this.setState({ rotation: rotationDegrees });
}; };
zoomIn = () => { onZoomInClick = () => {
this.setState({ this.setState({
zoom: this.state.zoom + 10, zoom: this.state.zoom + 10,
}); });
}; };
zoomOut = () => { onZoomOutClick = () => {
if (this.state.zoom <= 100) {
this.setState({
zoom: 100,
translationX: 0,
translationY: 0,
});
return;
}
this.setState({ this.setState({
zoom: this.state.zoom - 10, zoom: this.state.zoom - 10,
}); });
} }
render() { onStartMoving = ev => {
/* ev.stopPropagation();
// In theory max-width: 80%, max-height: 80% on the CSS should work ev.preventDefault();
// but in practice, it doesn't, so do it manually:
var width = this.props.width || 500; if (this.state.zoom <= 100) return false;
var height = this.props.height || 500;
var maxWidth = document.documentElement.clientWidth * 0.8; this.setState({moving: true});
var maxHeight = document.documentElement.clientHeight * 0.8; this.initX = ev.pageX - this.lastX;
this.initY = ev.pageY - this.lastY;
var widthFrac = width / maxWidth;
var heightFrac = height / maxHeight;
var displayWidth;
var displayHeight;
if (widthFrac > heightFrac) {
displayWidth = Math.min(width, maxWidth);
displayHeight = (displayWidth / width) * height;
} else {
displayHeight = Math.min(height, maxHeight);
displayWidth = (displayHeight / height) * width;
} }
var style = { onMoving = ev => {
width: displayWidth, ev.stopPropagation();
height: displayHeight ev.preventDefault();
};
*/
let res;
const style = {
height: this.state.zoom + "%",
width: this.state.zoom + "%",
};
if (!this.state.moving) return false;
this.lastX = ev.pageX - this.initX;
this.lastY = ev.pageY - this.initY;
this.setState({
translationX: this.lastX,
translationY: this.lastY,
});
}
onEndMoving = ev => {
this.setState({moving: false});
}
render() {
let mayRedact = false;
const showEventMeta = !!this.props.mxEvent;
let res;
if (this.props.width && this.props.height) { if (this.props.width && this.props.height) {
res = this.props.width + "x" + this.props.height + "px"; res = this.props.width + "x" + this.props.height + "px";
} }
@ -184,9 +209,6 @@ export default class ImageView extends React.Component {
sizeRes = size || res; sizeRes = size || res;
} }
let mayRedact = false;
const showEventMeta = !!this.props.mxEvent;
let metadata; let metadata;
if (showEventMeta) { if (showEventMeta) {
// Figure out the sender, defaulting to mxid // Figure out the sender, defaulting to mxid
@ -216,8 +238,16 @@ export default class ImageView extends React.Component {
); );
} }
const rotationDegrees = this.state.rotationDegrees; const rotationDegrees = this.state.rotation + "deg";
const effectiveStyle = {transform: `rotate(${rotationDegrees}deg)`, ...style}; const zoomPercentage = this.state.zoom/100;
const translatePixelsX = this.state.translationX + "px";
const translatePixelsY = this.state.translationY + "px";
const style = {
transform: `rotate(${rotationDegrees})
scale(${zoomPercentage})
translateX(${translatePixelsX})
translateY(${translatePixelsY})`,
};
return ( return (
<FocusLock <FocusLock
@ -239,16 +269,16 @@ export default class ImageView extends React.Component {
<span className="mx_ImageView_size">{ sizeRes }</span> <span className="mx_ImageView_size">{ sizeRes }</span>
</div> </div>
<div className="mx_ImageView_toolbar"> <div className="mx_ImageView_toolbar">
<AccessibleButton className="mx_ImageView_button" title={_t("Zoom in")} onClick={ this.zoomIn }> <AccessibleButton className="mx_ImageView_button" title={_t("Zoom in")} onClick={ this.onZoomInClick }>
<img src={require("../../../../res/img/plus-white.svg")} alt={ _t('Zoom in') } width="18" height="18" /> <img src={require("../../../../res/img/plus-white.svg")} alt={ _t('Zoom in') } width="18" height="18" />
</AccessibleButton> </AccessibleButton>
<AccessibleButton className="mx_ImageView_button" title={_t("Zoom out")} onClick={ this.zoomOut }> <AccessibleButton className="mx_ImageView_button" title={_t("Zoom out")} onClick={ this.onZoomOutClick }>
<img src={require("../../../../res/img/minus-white.svg")} alt={ _t('Zoom out') } width="18" height="18" /> <img src={require("../../../../res/img/minus-white.svg")} alt={ _t('Zoom out') } width="18" height="18" />
</AccessibleButton> </AccessibleButton>
<AccessibleButton className="mx_ImageView_button" title={_t("Rotate Left")} onClick={ this.rotateCounterClockwise }> <AccessibleButton className="mx_ImageView_button" title={_t("Rotate Left")} onClick={ this.onRotateCounterClockwiseClick }>
<img src={require("../../../../res/img/rotate-ccw.svg")} alt={ _t('Rotate counter-clockwise') } width="18" height="18" /> <img src={require("../../../../res/img/rotate-ccw.svg")} alt={ _t('Rotate counter-clockwise') } width="18" height="18" />
</AccessibleButton> </AccessibleButton>
<AccessibleButton className="mx_ImageView_button" title={_t("Rotate Right")} onClick={ this.rotateClockwise }> <AccessibleButton className="mx_ImageView_button" title={_t("Rotate Right")} onClick={ this.onRotateClockwiseClick }>
<img src={require("../../../../res/img/rotate-cw.svg")} alt={ _t('Rotate clockwise') } width="18" height="18" /> <img src={require("../../../../res/img/rotate-cw.svg")} alt={ _t('Rotate clockwise') } width="18" height="18" />
</AccessibleButton> </AccessibleButton>
<a className="mx_ImageView_button" href={ this.props.src } download={ this.props.name } title={_t("Download")} target="_blank" rel="noopener"> <a className="mx_ImageView_button" href={ this.props.src } download={ this.props.name } title={_t("Download")} target="_blank" rel="noopener">
@ -261,7 +291,17 @@ export default class ImageView extends React.Component {
</div> </div>
</div> </div>
<div className="mx_ImageView_imageBox"> <div className="mx_ImageView_imageBox">
<img src={this.props.src} title={this.props.name} style={effectiveStyle} className="mainImage" /> <img
src={this.props.src}
title={this.props.name}
style={style}
className="mainImage"
draggable={true}
onMouseDown={this.onStartMoving}
onMouseMove={this.onMoving}
onMouseUp={this.onEndMoving}
onMouseLeave={this.onEndMoving}
/>
</div> </div>
</div> </div>
</FocusLock> </FocusLock>