Implement translation
Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
This commit is contained in:
parent
8de019c054
commit
f9884b1cc7
2 changed files with 97 additions and 53 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue