anki/ts/editor/ImageHandle.svelte
Henrik Giesel f379e18e6f Revert "During resizing, make the smaller dimension decide the new dimensions"
This reverts commit 5bad9a913ce7b4a4cc58106936203fddec37d6da.

The previous behavior was the one you can observe in most graphical editors:
The bigger dimension (width or height) will decide the size, not the smaller one.
2021-09-06 21:15:37 +10:00

328 lines
8.3 KiB
Svelte

<!--
Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="typescript">
import ImageHandleFloat from "./ImageHandleFloat.svelte";
import ImageHandleSizeSelect from "./ImageHandleSizeSelect.svelte";
import { onDestroy, getContext } from "svelte";
import { nightModeKey } from "components/context-keys";
export let image: HTMLImageElement | null = null;
export let imageRule: CSSStyleRule | null = null;
export let isRtl: boolean = false;
export let container: HTMLElement;
$: selector = `:not([src="${image?.getAttribute("src")}"])`;
$: naturalWidth = image?.naturalWidth;
$: naturalHeight = image?.naturalHeight;
$: aspectRatio = naturalWidth && naturalHeight ? naturalWidth / naturalHeight : NaN;
$: showDimensions = image
? parseInt(getComputedStyle(image).getPropertyValue("width")) > 100
: false;
$: active = imageRule?.selectorText.includes(selector) as boolean;
let actualWidth = "";
let actualHeight = "";
let customDimensions = false;
let containerTop = 0;
let containerLeft = 0;
let top = 0;
let left = 0;
let width = 0;
let height = 0;
$: if (image) {
updateSizes();
}
const observer = new ResizeObserver(() => {
if (image) {
updateSizes();
}
});
function startObserving() {
observer.observe(container);
}
function stopObserving() {
observer.unobserve(container);
}
startObserving();
function updateSizes() {
const containerRect = container.getBoundingClientRect();
const imageRect = image!.getBoundingClientRect();
containerTop = containerRect.top;
containerLeft = containerRect.left;
top = imageRect!.top - containerTop;
left = imageRect!.left - containerLeft;
width = image!.clientWidth;
height = image!.clientHeight;
/* we do not want the actual width, but rather the intended display width */
const widthProperty = image!.style.width;
let inPixel = false;
customDimensions = false;
if (widthProperty) {
if (widthProperty.endsWith("px")) {
actualWidth = widthProperty.substring(0, widthProperty.length - 2);
inPixel = true;
} else {
actualWidth = widthProperty;
}
customDimensions = true;
} else {
actualWidth = String(naturalWidth);
}
const heightProperty = image!.style.height;
if (heightProperty) {
actualHeight = heightProperty.endsWith("px")
? heightProperty.substring(0, heightProperty.length - 2)
: heightProperty;
customDimensions = true;
} else if (inPixel) {
actualHeight = String(Math.trunc(Number(actualWidth) / aspectRatio));
} else {
actualHeight = String(naturalHeight);
}
}
function setPointerCapture(event: PointerEvent): void {
if (!active || event.pointerId !== 1) {
return;
}
stopObserving();
(event.target as Element).setPointerCapture(event.pointerId);
}
function resize(event: PointerEvent): void {
const element = event.target! as Element;
if (!element.hasPointerCapture(event.pointerId)) {
return;
}
const dragWidth = event.clientX - containerLeft - left;
const dragHeight = event.clientY - containerTop - top;
const widthIncrease = dragWidth / naturalWidth!;
const heightIncrease = dragHeight / naturalHeight!;
if (widthIncrease > heightIncrease) {
width = Math.trunc(dragWidth);
height = Math.trunc(naturalHeight! * widthIncrease);
} else {
height = Math.trunc(dragHeight);
width = Math.trunc(naturalWidth! * heightIncrease);
}
showDimensions = width > 100;
image!.style.width = `${width}px`;
image!.style.removeProperty("height");
}
let sizeSelect: any;
function onDblclick() {
sizeSelect.toggleActualSize();
}
const nightMode = getContext(nightModeKey);
onDestroy(() => observer.disconnect());
</script>
{#if image && imageRule}
<div
style="--top: {top}px; --left: {left}px; --width: {width}px; --height: {height}px;"
class="image-handle-selection"
>
<div
class="image-handle-bg"
on:mousedown|preventDefault
on:dblclick={onDblclick}
/>
<div class="image-handle-float" class:is-rtl={isRtl}>
<ImageHandleFloat {isRtl} bind:float={image.style.float} />
</div>
<div class="image-handle-size-select" class:is-rtl={isRtl}>
<ImageHandleSizeSelect
bind:this={sizeSelect}
bind:active
{image}
{imageRule}
{selector}
{isRtl}
on:update={updateSizes}
/>
</div>
{#if showDimensions}
<div class="image-handle-dimensions" class:is-rtl={isRtl}>
<span>{actualWidth}&times;{actualHeight}</span>
{#if customDimensions}<span
>(Original: {naturalWidth}&times;{naturalHeight})</span
>{/if}
</div>
{/if}
<div
class:nightMode
class="image-handle-control image-handle-control-nw"
on:mousedown|preventDefault
/>
<div
class:nightMode
class="image-handle-control image-handle-control-ne"
on:mousedown|preventDefault
/>
<div
class:nightMode
class:active
class="image-handle-control image-handle-control-sw"
on:mousedown|preventDefault
on:pointerdown={setPointerCapture}
on:pointerup={startObserving}
on:pointermove={resize}
/>
<div
class:nightMode
class:active
class="image-handle-control image-handle-control-se"
on:mousedown|preventDefault
on:pointerdown={setPointerCapture}
on:pointerup={startObserving}
on:pointermove={resize}
/>
</div>
{/if}
<style lang="scss">
div {
position: absolute;
}
.image-handle-selection {
top: var(--top);
left: var(--left);
width: var(--width);
height: var(--height);
}
.image-handle-bg {
width: 100%;
height: 100%;
background-color: black;
opacity: 0.2;
}
.image-handle-float {
top: 3px;
left: 3px;
&.is-rtl {
left: auto;
right: 3px;
}
}
.image-handle-size-select {
top: 3px;
right: 3px;
&.is-rtl {
right: auto;
left: 3px;
}
}
.image-handle-dimensions {
pointer-events: none;
user-select: none;
font-size: 13px;
color: white;
background-color: rgba(0 0 0 / 0.3);
border-color: black;
border-radius: 0.25rem;
padding: 0 5px;
bottom: 3px;
right: 3px;
margin-left: 3px;
&.is-rtl {
right: auto;
left: 3px;
margin-right: 3px;
}
}
.image-handle-control {
width: 7px;
height: 7px;
border: 1px solid black;
&.active {
background-color: black;
}
&.nightMode {
border-color: white;
&.active {
background-color: white;
}
}
}
.image-handle-control-nw {
top: -5px;
left: -5px;
border-bottom: none;
border-right: none;
}
.image-handle-control-ne {
top: -5px;
right: -5px;
border-bottom: none;
border-left: none;
}
.image-handle-control-sw {
bottom: -5px;
left: -5px;
border-top: none;
border-right: none;
&.active {
cursor: sw-resize;
}
}
.image-handle-control-se {
bottom: -5px;
right: -5px;
border-top: none;
border-left: none;
&.active {
cursor: se-resize;
}
}
</style>