refactor: change avatar cropper to image cropper

pull/105/head
moonrailgun 2 years ago
parent 94090e6664
commit e95e87f593

@ -3,7 +3,7 @@ import { closeModal, openModal } from './Modal';
import { showToasts, t } from 'tailchat-shared';
import { Avatar } from 'antd';
import { Icon } from 'tailchat-design';
import { ModalAvatarCropper } from './modals/AvatarCropper';
import { ImageCropperModal } from './modals/ImageCropper';
import { isGIF } from '@/utils/file-helper';
interface AvatarPickerProps extends PropsWithChildren {
@ -41,7 +41,7 @@ export const AvatarPicker: React.FC<AvatarPickerProps> = React.memo((props) => {
reader.addEventListener('load', () => {
if (reader.result) {
const key = openModal(
<ModalAvatarCropper
<ImageCropperModal
imageUrl={reader.result.toString()}
onConfirm={(croppedImageBlobUrl) => {
closeModal(key);

@ -2,18 +2,65 @@ import Cropper from 'react-easy-crop';
import type { Area } from 'react-easy-crop/types';
import _isNil from 'lodash/isNil';
import { showToasts, t } from 'tailchat-shared';
import React, { useRef, useState } from 'react';
import React, { useState } from 'react';
import { Button } from 'antd';
import { ModalWrapper } from '../Modal';
const createImage = (url: string): Promise<HTMLImageElement> =>
new Promise((resolve, reject) => {
/**
*
*/
export const ImageCropperModal: React.FC<{
imageUrl: string;
aspect?: number;
onConfirm: (croppedImageBlobUrl: string) => void;
}> = React.memo((props) => {
const aspect = props.aspect ?? 1;
const [crop, setCrop] = useState({ x: 0, y: 0 });
const [zoom, setZoom] = useState(1);
const [area, setArea] = useState<Area>({ width: 0, height: 0, x: 0, y: 0 });
const handleConfirm = async () => {
const blobUrl = await getCroppedImg(
await createImage(props.imageUrl),
area
);
props.onConfirm(blobUrl);
};
return (
<ModalWrapper
className="flex flex-col"
style={{ width: '80vw', height: '80vh' }}
>
<div className="flex-1 relative mb-4">
<Cropper
image={props.imageUrl}
crop={crop}
zoom={zoom}
aspect={aspect}
onCropChange={setCrop}
onZoomChange={setZoom}
onCropComplete={(_, area) => setArea(area)}
/>
</div>
<Button type="primary" onClick={handleConfirm}>
{t('确认')}
</Button>
</ModalWrapper>
);
});
ImageCropperModal.displayName = 'ImageCropperModal';
function createImage(url: string): Promise<HTMLImageElement> {
return new Promise((resolve, reject) => {
const image = new Image();
image.addEventListener('load', () => resolve(image));
image.addEventListener('error', (error) => reject(error));
image.setAttribute('crossOrigin', 'anonymous'); // needed to avoid cross-origin issues on CodeSandbox
image.src = url;
});
}
function getRadianAngle(degreeValue: number) {
return (degreeValue * Math.PI) / 180;
@ -93,47 +140,3 @@ function getCroppedImg(
}
});
}
/**
*
*/
export const ModalAvatarCropper: React.FC<{
imageUrl: string;
onConfirm: (croppedImageBlobUrl: string) => void;
}> = React.memo((props) => {
const [crop, setCrop] = useState({ x: 0, y: 0 });
const [zoom, setZoom] = useState(1);
const [area, setArea] = useState<Area>({ width: 0, height: 0, x: 0, y: 0 });
const handleConfirm = async () => {
const blobUrl = await getCroppedImg(
await createImage(props.imageUrl),
area
);
props.onConfirm(blobUrl);
};
return (
<ModalWrapper
className="flex flex-col"
style={{ width: '80vw', height: '80vh' }}
>
<div className="flex-1 relative mb-4">
<Cropper
image={props.imageUrl}
crop={crop}
zoom={zoom}
aspect={1}
onCropChange={setCrop}
onZoomChange={setZoom}
onCropComplete={(_, area) => setArea(area)}
/>
</div>
<Button type="primary" onClick={handleConfirm}>
{t('确认')}
</Button>
</ModalWrapper>
);
});
ModalAvatarCropper.displayName = 'ModalAvatarCropper';
Loading…
Cancel
Save