feat(frontend): avatar cropping
This commit is contained in:
parent
f1f777ff82
commit
64ea25e89e
4 changed files with 108 additions and 10 deletions
|
@ -1,7 +1,8 @@
|
|||
<script lang="ts">
|
||||
import Avatar from "$components/Avatar.svelte";
|
||||
import { t } from "$lib/i18n";
|
||||
import { Icon, InputGroup } from "@sveltestrap/sveltestrap";
|
||||
import { Icon, InputGroup, Modal } from "@sveltestrap/sveltestrap";
|
||||
import Cropper, { type CropArea, type OnCropCompleteEvent } from "svelte-easy-crop";
|
||||
import { encode } from "base64-arraybuffer";
|
||||
import prettyBytes from "pretty-bytes";
|
||||
import ShortNoscriptWarning from "./ShortNoscriptWarning.svelte";
|
||||
|
@ -21,6 +22,7 @@
|
|||
let avatar: string = $state("");
|
||||
let avatarExists = $derived(avatar !== "");
|
||||
let avatarTooLarge = $derived(avatar !== "" && avatar.length > MAX_AVATAR_BYTES);
|
||||
let cropperOpen = $state(false);
|
||||
|
||||
$effect(() => {
|
||||
getAvatar(avatarFiles);
|
||||
|
@ -28,7 +30,7 @@
|
|||
|
||||
const getAvatar = async (list: FileList | null) => {
|
||||
if (!list || list.length === 0) {
|
||||
avatar = "";
|
||||
uncroppedAvatar = "";
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -36,7 +38,47 @@
|
|||
const base64 = encode(buffer);
|
||||
|
||||
const uri = `data:${list[0].type};base64,${base64}`;
|
||||
avatar = uri;
|
||||
uncroppedAvatar = uri;
|
||||
cropperOpen = true;
|
||||
};
|
||||
|
||||
let uncroppedAvatar: string = $state("");
|
||||
let crop = $state({ x: 0, y: 0 });
|
||||
let zoom = $state(1);
|
||||
let croppedArea = $state({ x: 0, y: 0, height: 0, width: 0 } satisfies CropArea);
|
||||
|
||||
const onCropComplete = (e: OnCropCompleteEvent) => {
|
||||
croppedArea = e.pixels;
|
||||
};
|
||||
|
||||
const doCrop = () => {
|
||||
cropperOpen = false;
|
||||
if (!uncroppedAvatar) {
|
||||
return;
|
||||
}
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
canvas.width = croppedArea.width;
|
||||
canvas.height = croppedArea.height;
|
||||
|
||||
ctx?.drawImage(
|
||||
img,
|
||||
croppedArea.x,
|
||||
croppedArea.y,
|
||||
croppedArea.width,
|
||||
croppedArea.height,
|
||||
0,
|
||||
0,
|
||||
croppedArea.width,
|
||||
croppedArea.height,
|
||||
);
|
||||
|
||||
avatar = canvas.toDataURL("image/webp", 1);
|
||||
};
|
||||
img.src = uncroppedAvatar;
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -44,6 +86,41 @@
|
|||
<Avatar {name} url={avatarExists ? avatar : current} {alt} />
|
||||
</p>
|
||||
|
||||
<Modal
|
||||
isOpen={cropperOpen}
|
||||
autoFocus
|
||||
backdrop
|
||||
fade
|
||||
keyboard
|
||||
returnFocusAfterClose
|
||||
toggle={() => (cropperOpen = !cropperOpen)}
|
||||
>
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5">{$t("editor.crop-avatar-header")}</h1>
|
||||
</div>
|
||||
<div class="modal-body cropper-wrapper">
|
||||
{#if uncroppedAvatar}
|
||||
<Cropper
|
||||
image={uncroppedAvatar}
|
||||
{crop}
|
||||
{zoom}
|
||||
minZoom={1}
|
||||
maxZoom={4}
|
||||
aspect={1 / 1}
|
||||
oncropcomplete={onCropComplete}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" onclick={() => doCrop()}>
|
||||
{$t("editor.crop-avatar-button")}
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" onclick={() => (cropperOpen = false)}>
|
||||
{$t("cancel")}
|
||||
</button>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<InputGroup class="mb-2">
|
||||
<input
|
||||
class="form-control"
|
||||
|
@ -79,3 +156,9 @@
|
|||
})}
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.cropper-wrapper {
|
||||
min-height: 30em;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -291,7 +291,9 @@
|
|||
"custom-preference-muted": "Show as muted text",
|
||||
"custom-preference-favourite": "Treat like favourite",
|
||||
"custom-preference-notice": "Want to edit your custom preferences?",
|
||||
"custom-preference-notice-link": "Go to settings"
|
||||
"custom-preference-notice-link": "Go to settings",
|
||||
"crop-avatar-header": "Crop avatar",
|
||||
"crop-avatar-button": "Crop"
|
||||
},
|
||||
"cancel": "Cancel",
|
||||
"report": {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue