cropper: fix incorrect image positioning after zoom-out
This commit is contained in:
+2
-2
@@ -3103,8 +3103,8 @@ ul.tabbar li.active > a {
|
|||||||
|
|
||||||
.cropper .cutout {
|
.cropper .cutout {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 17rem;
|
width: 17.8rem;
|
||||||
height: 17rem;
|
height: 17.8rem;
|
||||||
border: 1px dashed var(--clr-cropper-cutout-border);
|
border: 1px dashed var(--clr-cropper-cutout-border);
|
||||||
margin: auto;
|
margin: auto;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+22
-18
@@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const DEFAULT_MAX_ZOOM = 2.5;
|
const DEFAULT_MAX_ZOOM = 8;
|
||||||
|
|
||||||
export default class Cropper extends React.Component {
|
export default class Cropper extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
// Coordinates of the top left corner of the image in container coordinates.
|
// Coordinates of the top left corner of the image in bounding box (container) coordinates.
|
||||||
panX: 0,
|
panX: 0,
|
||||||
panY: 0,
|
panY: 0,
|
||||||
// Origin of the zoom in image coordinates.
|
// Origin of the zoom in image coordinates.
|
||||||
@@ -32,7 +32,7 @@ export default class Cropper extends React.Component {
|
|||||||
// The main container (static).
|
// The main container (static).
|
||||||
this.boundingBox = React.createRef();
|
this.boundingBox = React.createRef();
|
||||||
|
|
||||||
// The original image width and height before any scaling.
|
// The original image width and height before any scaling (does not change).
|
||||||
this.imageWidth = 0;
|
this.imageWidth = 0;
|
||||||
this.imageHeight = 0;
|
this.imageHeight = 0;
|
||||||
|
|
||||||
@@ -42,10 +42,10 @@ export default class Cropper extends React.Component {
|
|||||||
// Length of the previous mouse move when dragging.
|
// Length of the previous mouse move when dragging.
|
||||||
this.prevDistance = 0;
|
this.prevDistance = 0;
|
||||||
|
|
||||||
// Bounding rectangles of static elements.
|
// Bounding rectangles of static elements (does not change).
|
||||||
this.cutoutRect = {};
|
this.cutoutRect = {};
|
||||||
this.bBoxRect = {};
|
this.bBoxRect = {};
|
||||||
// Center of the bounding box.
|
// Center of the bounding box (does not change).
|
||||||
this.originX = 0;
|
this.originX = 0;
|
||||||
this.originY = 0;
|
this.originY = 0;
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ export default class Cropper extends React.Component {
|
|||||||
this.originX = this.bBoxRect.width / 2;
|
this.originX = this.bBoxRect.width / 2;
|
||||||
this.originY = this.bBoxRect.height / 2;
|
this.originY = this.bBoxRect.height / 2;
|
||||||
|
|
||||||
// The rectangle position is in viewport coordinates.
|
// The cutout circle bounds in viewport coordinates.
|
||||||
this.cutoutRect = this.cutout.current.getBoundingClientRect();
|
this.cutoutRect = this.cutout.current.getBoundingClientRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ export default class Cropper extends React.Component {
|
|||||||
const minZoom = Math.max(this.cutoutRect.width / imgRect.width, this.cutoutRect.height / imgRect.height);
|
const minZoom = Math.max(this.cutoutRect.width / imgRect.width, this.cutoutRect.height / imgRect.height);
|
||||||
this.setState({
|
this.setState({
|
||||||
minZoom: minZoom,
|
minZoom: minZoom,
|
||||||
maxZoom: Math.max(DEFAULT_MAX_ZOOM, minZoom + 1)
|
maxZoom: minZoom * DEFAULT_MAX_ZOOM
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initial zoom level fills the bounding box at the smallest image dimension and overflows the largest, i.e. "fill" not "fit".
|
// Initial zoom level fills the bounding box at the smallest image dimension and overflows the largest, i.e. "fill" not "fit".
|
||||||
@@ -150,25 +150,29 @@ export default class Cropper extends React.Component {
|
|||||||
let panY = this.state.panY;
|
let panY = this.state.panY;
|
||||||
|
|
||||||
// Ensure that the image at the new zoom is not outside of the cutout boundaries.
|
// Ensure that the image at the new zoom is not outside of the cutout boundaries.
|
||||||
|
|
||||||
// Calculate image coordinates at the new zoom.
|
// Calculate image coordinates at the new zoom.
|
||||||
const imgLeft = this.originX - (this.originX - panX) * zoom;
|
const imgLeft = this.originX - (this.originX - panX) * zoom;
|
||||||
const imgRight = imgLeft + this.imageWidth * zoom;
|
const imgRight = imgLeft + this.imageWidth * zoom;
|
||||||
|
const imgTop = this.originY - (this.originY - panY) * zoom;
|
||||||
|
const imgBottom = imgTop + this.imageHeight * zoom;
|
||||||
|
|
||||||
// Cutout in the same coordinates as the image.
|
// Cutout in the same coordinates as the image.
|
||||||
const coLeft = this.cutoutRect.left - this.bBoxRect.left;
|
const coLeft = this.cutoutRect.left - this.bBoxRect.left;
|
||||||
const coRight = coLeft + this.cutoutRect.width;
|
const coRight = coLeft + this.cutoutRect.width;
|
||||||
if (coLeft < imgLeft) {
|
|
||||||
panX -= imgLeft - coLeft;
|
|
||||||
} else if (coRight > imgRight) {
|
|
||||||
panX += coRight - imgRight;
|
|
||||||
}
|
|
||||||
const imgTop = this.originY - (this.originY - panY) * zoom;
|
|
||||||
const imgBottom = imgTop + this.imageHeight * zoom;
|
|
||||||
const coTop = this.cutoutRect.top - this.bBoxRect.top;
|
const coTop = this.cutoutRect.top - this.bBoxRect.top;
|
||||||
const coBottom = coTop + this.cutoutRect.height;
|
const coBottom = coTop + this.cutoutRect.height;
|
||||||
|
|
||||||
|
if (coLeft < imgLeft) {
|
||||||
|
panX -= (imgLeft - coLeft) / zoom;
|
||||||
|
} else if (coRight > imgRight) {
|
||||||
|
panX += (coRight - imgRight) / zoom;
|
||||||
|
}
|
||||||
|
|
||||||
if (coTop < imgTop) {
|
if (coTop < imgTop) {
|
||||||
panY -= imgTop - coTop;
|
panY -= (imgTop - coTop) / zoom;
|
||||||
} else if (coBottom > imgBottom) {
|
} else if (coBottom > imgBottom) {
|
||||||
panY += coBottom - imgBottom;
|
panY += (coBottom - imgBottom) /zoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.positionAll(panX, panY, zoom);
|
this.positionAll(panX, panY, zoom);
|
||||||
@@ -203,9 +207,9 @@ export default class Cropper extends React.Component {
|
|||||||
const imgRect = this.preview.current.getBoundingClientRect();
|
const imgRect = this.preview.current.getBoundingClientRect();
|
||||||
|
|
||||||
// Check if the new position is within the boundaries, and if not if it's closer to them.
|
// Check if the new position is within the boundaries, and if not if it's closer to them.
|
||||||
let panX = Cropper.checkBound(this.state.panX, [imgRect.left, imgRect.right],
|
const panX = Cropper.checkBound(this.state.panX, [imgRect.left, imgRect.right],
|
||||||
[this.cutoutRect.left, this.cutoutRect.right], dX);
|
[this.cutoutRect.left, this.cutoutRect.right], dX);
|
||||||
let panY = Cropper.checkBound(this.state.panY, [imgRect.top, imgRect.bottom],
|
const panY = Cropper.checkBound(this.state.panY, [imgRect.top, imgRect.bottom],
|
||||||
[this.cutoutRect.top, this.cutoutRect.bottom], dY);
|
[this.cutoutRect.top, this.cutoutRect.bottom], dY);
|
||||||
|
|
||||||
this.positionAll(panX, panY, this.state.zoom);
|
this.positionAll(panX, panY, this.state.zoom);
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+15
-15
@@ -17168,7 +17168,7 @@ __webpack_require__.r(__webpack_exports__);
|
|||||||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react");
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react");
|
||||||
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
||||||
|
|
||||||
const DEFAULT_MAX_ZOOM = 2.5;
|
const DEFAULT_MAX_ZOOM = 8;
|
||||||
class Cropper extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component) {
|
class Cropper extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component) {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
@@ -17250,7 +17250,7 @@ class Cropper extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component) {
|
|||||||
const minZoom = Math.max(this.cutoutRect.width / imgRect.width, this.cutoutRect.height / imgRect.height);
|
const minZoom = Math.max(this.cutoutRect.width / imgRect.width, this.cutoutRect.height / imgRect.height);
|
||||||
this.setState({
|
this.setState({
|
||||||
minZoom: minZoom,
|
minZoom: minZoom,
|
||||||
maxZoom: Math.max(DEFAULT_MAX_ZOOM, minZoom + 1)
|
maxZoom: minZoom * DEFAULT_MAX_ZOOM
|
||||||
});
|
});
|
||||||
const zoom = Math.max(this.bBoxRect.width / imgRect.width, this.bBoxRect.height / imgRect.height);
|
const zoom = Math.max(this.bBoxRect.width / imgRect.width, this.bBoxRect.height / imgRect.height);
|
||||||
const panX = this.cutoutRect.left - this.bBoxRect.left - (imgRect.width - this.cutoutRect.width) / 2;
|
const panX = this.cutoutRect.left - this.bBoxRect.left - (imgRect.width - this.cutoutRect.width) / 2;
|
||||||
@@ -17265,21 +17265,21 @@ class Cropper extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component) {
|
|||||||
let panY = this.state.panY;
|
let panY = this.state.panY;
|
||||||
const imgLeft = this.originX - (this.originX - panX) * zoom;
|
const imgLeft = this.originX - (this.originX - panX) * zoom;
|
||||||
const imgRight = imgLeft + this.imageWidth * zoom;
|
const imgRight = imgLeft + this.imageWidth * zoom;
|
||||||
const coLeft = this.cutoutRect.left - this.bBoxRect.left;
|
|
||||||
const coRight = coLeft + this.cutoutRect.width;
|
|
||||||
if (coLeft < imgLeft) {
|
|
||||||
panX -= imgLeft - coLeft;
|
|
||||||
} else if (coRight > imgRight) {
|
|
||||||
panX += coRight - imgRight;
|
|
||||||
}
|
|
||||||
const imgTop = this.originY - (this.originY - panY) * zoom;
|
const imgTop = this.originY - (this.originY - panY) * zoom;
|
||||||
const imgBottom = imgTop + this.imageHeight * zoom;
|
const imgBottom = imgTop + this.imageHeight * zoom;
|
||||||
|
const coLeft = this.cutoutRect.left - this.bBoxRect.left;
|
||||||
|
const coRight = coLeft + this.cutoutRect.width;
|
||||||
const coTop = this.cutoutRect.top - this.bBoxRect.top;
|
const coTop = this.cutoutRect.top - this.bBoxRect.top;
|
||||||
const coBottom = coTop + this.cutoutRect.height;
|
const coBottom = coTop + this.cutoutRect.height;
|
||||||
|
if (coLeft < imgLeft) {
|
||||||
|
panX -= (imgLeft - coLeft) / zoom;
|
||||||
|
} else if (coRight > imgRight) {
|
||||||
|
panX += (coRight - imgRight) / zoom;
|
||||||
|
}
|
||||||
if (coTop < imgTop) {
|
if (coTop < imgTop) {
|
||||||
panY -= imgTop - coTop;
|
panY -= (imgTop - coTop) / zoom;
|
||||||
} else if (coBottom > imgBottom) {
|
} else if (coBottom > imgBottom) {
|
||||||
panY += coBottom - imgBottom;
|
panY += (coBottom - imgBottom) / zoom;
|
||||||
}
|
}
|
||||||
this.positionAll(panX, panY, zoom);
|
this.positionAll(panX, panY, zoom);
|
||||||
}
|
}
|
||||||
@@ -17306,13 +17306,13 @@ class Cropper extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component) {
|
|||||||
document.body.style['userSelect'] = 'none';
|
document.body.style['userSelect'] = 'none';
|
||||||
}
|
}
|
||||||
translate(pageX, pageY) {
|
translate(pageX, pageY) {
|
||||||
const dX = pageX - this.mouseX;
|
const dX = (pageX - this.mouseX) / this.state.zoom;
|
||||||
const dY = pageY - this.mouseY;
|
const dY = (pageY - this.mouseY) / this.state.zoom;
|
||||||
this.mouseX = pageX;
|
this.mouseX = pageX;
|
||||||
this.mouseY = pageY;
|
this.mouseY = pageY;
|
||||||
const imgRect = this.preview.current.getBoundingClientRect();
|
const imgRect = this.preview.current.getBoundingClientRect();
|
||||||
let panX = Cropper.checkBound(this.state.panX, [imgRect.left, imgRect.right], [this.cutoutRect.left, this.cutoutRect.right], dX);
|
const panX = Cropper.checkBound(this.state.panX, [imgRect.left, imgRect.right], [this.cutoutRect.left, this.cutoutRect.right], dX);
|
||||||
let panY = Cropper.checkBound(this.state.panY, [imgRect.top, imgRect.bottom], [this.cutoutRect.top, this.cutoutRect.bottom], dY);
|
const panY = Cropper.checkBound(this.state.panY, [imgRect.top, imgRect.bottom], [this.cutoutRect.top, this.cutoutRect.bottom], dY);
|
||||||
this.positionAll(panX, panY, this.state.zoom);
|
this.positionAll(panX, panY, this.state.zoom);
|
||||||
}
|
}
|
||||||
mouseMove(e) {
|
mouseMove(e) {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -109,6 +109,12 @@ class CreateAccountView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pu
|
|||||||
newAvatar: img,
|
newAvatar: img,
|
||||||
newAvatarMime: mime
|
newAvatarMime: mime
|
||||||
});
|
});
|
||||||
|
if (!img) {
|
||||||
|
this.setState({
|
||||||
|
imageUrl: null,
|
||||||
|
uploadUrl: null
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
handleToggleSaveToken() {
|
handleToggleSaveToken() {
|
||||||
_lib_local_storage_js__WEBPACK_IMPORTED_MODULE_7__["default"].setObject('keep-logged-in', !this.state.saveToken);
|
_lib_local_storage_js__WEBPACK_IMPORTED_MODULE_7__["default"].setObject('keep-logged-in', !this.state.saveToken);
|
||||||
@@ -126,7 +132,6 @@ class CreateAccountView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pu
|
|||||||
handleAvatarCropped(mime, blob, width, height) {
|
handleAvatarCropped(mime, blob, width, height) {
|
||||||
const url = blob ? URL.createObjectURL(blob) : null;
|
const url = blob ? URL.createObjectURL(blob) : null;
|
||||||
this.setState({
|
this.setState({
|
||||||
avatar: url,
|
|
||||||
newAvatar: null,
|
newAvatar: null,
|
||||||
newAvatarMime: null
|
newAvatarMime: null
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user