[ui] custom canvas-based image fragment rendering (allow resizing)

feat/vaults
Tomáš Mládek 2022-03-19 23:08:21 +01:00
parent 5051ac25c6
commit 04615b3682
3 changed files with 48 additions and 61 deletions

View File

@ -39,6 +39,7 @@
width: 100%;
display: flex;
justify-content: center;
min-height: 0;
}
img {

View File

@ -204,10 +204,10 @@
min-height: 0;
max-height: 100%;
}
}
.preview-image {
margin: auto;
.preview-image {
margin: auto;
}
}
.r6o-editor {

View File

@ -43,24 +43,20 @@ export function xywh(mediaItem: HTMLImageElement | HTMLVideoElement) {
}
/**
* Applies the media fragment when the image has loaded. We need the image's
* original width and height.
* Applies the media fragment when the image has loaded.
*/
function addImageLoadListener(mediaFragment: MediaFragment) {
// Base64-encoded transparent 1x1 pixel GIF
const TRANSPARENT_GIF =
"data:image/gif;base64,R0lGODlhAQABAPAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==";
const mediaItem = mediaFragment.mediaItem;
const onload = function () {
// Prevent onload firing when the 1x1 pixel GIF loads; but still react when `src`
// is changed programatically.
if (mediaItem.src !== TRANSPARENT_GIF) {
// Prevent onload firing when the fragment loads; but still react when `src`
// is changed programatically.
let lastSrc: string;
function onload() {
if (mediaItem.src !== lastSrc) {
// Required on reloads because of size calculations.
mediaItem.style.cssText = "";
applyFragment(mediaFragment);
mediaItem.src = TRANSPARENT_GIF;
lastSrc = mediaItem.src;
}
};
}
mediaItem.addEventListener("load", onload);
}
@ -86,34 +82,26 @@ function addVideoLoadListener(mediaFragment: MediaFragment) {
* 2D transformation according to the fragment's x and y values.
*/
function applyFragment(fragment: MediaFragment) {
let x: string, y: string, w: string, h: string;
const originalWidth =
fragment.mediaType === "img"
? fragment.mediaItem.width
: fragment.mediaItem.videoWidth;
const originalHeight =
fragment.mediaType === "img"
? fragment.mediaItem.height
: fragment.mediaItem.videoHeight;
// Unit is pixel:
if (fragment.unit === "pixel:") {
const scale =
fragment.mediaType === "img"
? originalWidth / fragment.mediaItem.naturalWidth
: originalWidth / fragment.mediaItem.clientWidth;
w = fragment.w * scale + "px";
h = fragment.h * scale + "px";
x = "-" + fragment.x * scale + "px";
y = "-" + fragment.y * scale + "px";
// Unit is percent:
} else {
w = (originalWidth * fragment.w) / 100 + "px";
h = (originalHeight * fragment.h) / 100 + "px";
x = "-" + (originalWidth * fragment.x) / 100 + "px";
y = "-" + (originalHeight * fragment.y) / 100 + "px";
}
// Media item is a video
if (fragment.mediaType === "video") {
let x: string, y: string, w: string, h: string;
const originalWidth = fragment.mediaItem.videoWidth;
const originalHeight = fragment.mediaItem.videoHeight;
// Unit is pixel:
if (fragment.unit === "pixel:") {
const scale = originalWidth / fragment.mediaItem.clientWidth;
w = fragment.w * scale + "px";
h = fragment.h * scale + "px";
x = "-" + fragment.x * scale + "px";
y = "-" + fragment.y * scale + "px";
// Unit is percent:
} else {
w = (originalWidth * fragment.w) / 100 + "px";
h = (originalHeight * fragment.h) / 100 + "px";
x = "-" + (originalWidth * fragment.x) / 100 + "px";
y = "-" + (originalHeight * fragment.y) / 100 + "px";
}
const wrapper = document.createElement("div");
wrapper.style.cssText +=
"overflow:hidden;" +
@ -148,25 +136,23 @@ function applyFragment(fragment: MediaFragment) {
}
// Media item is an image
} else {
fragment.mediaItem.style.cssText +=
"width:" +
w +
";" +
"height:" +
h +
";" +
"background:url(" +
fragment.mediaItem.src +
") " + // background-image
"no-repeat " + // background-repeat
x +
" " +
y +
"; " + // background-position
"background-size: " +
originalWidth +
"px " +
originalHeight +
"px;";
let x: number, y: number, w: number, h: number;
if (fragment.unit === "pixel:") {
x = fragment.x;
y = fragment.y;
w = fragment.w;
h = fragment.h;
} else {
x = (fragment.x / 100) * fragment.mediaItem.naturalWidth;
y = (fragment.y / 100) * fragment.mediaItem.naturalHeight;
w = (fragment.w / 100) * fragment.mediaItem.naturalWidth;
h = (fragment.h / 100) * fragment.mediaItem.naturalHeight;
}
const canvas = document.createElement("canvas");
canvas.width = w;
canvas.height = h;
const context = canvas.getContext("2d");
context.drawImage(fragment.mediaItem, x, y, w, h, 0, 0, w, h);
fragment.mediaItem.src = canvas.toDataURL();
}
}