Skip to content
Snippets Groups Projects
Commit efbdf918 authored by Дмитрий Малюгин's avatar Дмитрий Малюгин :clock4:
Browse files

feat: crop logic in 'Cropper' in process

parent 566f9c3b
No related branches found
No related tags found
1 merge request!6Finish "UI-library v1.0.0"
...@@ -8,6 +8,7 @@ import CornerLeftTopIcon from '@icons/Mono/CornerLeftTopIcon.vue'; ...@@ -8,6 +8,7 @@ import CornerLeftTopIcon from '@icons/Mono/CornerLeftTopIcon.vue';
import CornerRightTopIcon from '@icons/Mono/CornerRightTopIcon.vue'; import CornerRightTopIcon from '@icons/Mono/CornerRightTopIcon.vue';
import CornerLeftBottomIcon from '@icons/Mono/CornerLeftBottomIcon.vue'; import CornerLeftBottomIcon from '@icons/Mono/CornerLeftBottomIcon.vue';
import CornerRightBottomIcon from '@icons/Mono/CornerRightBottomIcon.vue'; import CornerRightBottomIcon from '@icons/Mono/CornerRightBottomIcon.vue';
import { calcContainerRect, onBorderMove } from '@components/Cropper/helpers';
const props = withDefaults(defineProps<ICropperProps>(), { const props = withDefaults(defineProps<ICropperProps>(), {
width: 300, width: 300,
...@@ -18,12 +19,22 @@ const props = withDefaults(defineProps<ICropperProps>(), { ...@@ -18,12 +19,22 @@ const props = withDefaults(defineProps<ICropperProps>(), {
}); });
const canvas = ref(); const canvas = ref();
const layerX = ref(0);
const layerY = ref(0);
const activeSides = ref<[string, string]>(['top', 'left']);
const isMoving = ref<boolean>(false);
const top = ref('0');
const left = ref('0');
const right = ref('0');
const bottom = ref('0');
const ctx = computed(() => canvas.value && canvas.value.getContext('2d')); const ctx = computed(() => canvas.value && canvas.value.getContext('2d'));
const imageSource = computed(() => props.src ?? props.file); const imageSource = computed(() => props.src ?? props.file);
const width = computed(() => props.width); const width = computed(() => props.width);
const height = computed(() => props.height); const height = computed(() => props.height);
const color = computed(() => convertThemeToTextColor(props.theme, props.darknessTheme)); const color = computed(() => convertThemeToTextColor(props.theme, props.darknessTheme));
const container = computed(() => calcContainerRect());
watch( watch(
[imageSource, ctx, width, height], [imageSource, ctx, width, height],
...@@ -41,6 +52,38 @@ watch( ...@@ -41,6 +52,38 @@ watch(
}, },
{ immediate: true }, { immediate: true },
); );
const onPointerDown = (event: PointerEvent, newSides: [string, string]) => {
activeSides.value = newSides;
layerX.value = event.layerX;
layerY.value = event.layerY;
isMoving.value = true;
};
// TODO почему то в самом начале переноса элемент смещается на 1-2 пикселя вниз. Пофиксить
const onBorderMove = (event: PointerEvent) => {
if (!isMoving.value) return;
if (event.clientY + 39 - layerY.value > container.value?.top + height.value) {
console.log('out?');
isMoving.value = false;
}
if (activeSides.value.includes('top')) {
const newTop = event.clientY - container.value?.top - layerY.value;
top.value = newTop + 'px';
}
if (activeSides.value.includes('left')) {
const newLeft = event.clientX - container.value?.left - layerX.value;
left.value = newLeft + 'px';
}
if (activeSides.value.includes('bottom')) {
const newBottom = height.value - event.clientY + container.value?.top - 40 + layerY.value;
bottom.value = newBottom + 'px';
}
if (activeSides.value.includes('right')) {
const newRight = width.value - event.clientX + container.value?.left - 40 + layerX.value;
right.value = newRight + 'px';
}
};
</script> </script>
<template> <template>
...@@ -52,18 +95,20 @@ watch( ...@@ -52,18 +95,20 @@ watch(
}, },
]" ]"
> >
<div class="canvas-container"> <div id="canvas-container" @pointermove="onBorderMove" @pointerup="isMoving = false">
<canvas ref="canvas" id="cropper-canvas"> </canvas> <canvas ref="canvas" id="cropper-canvas"></canvas>
<div class="crop-border left top"> <button @pointerdown="onPointerDown($event, ['left', 'top'])" class="crop-border left top">
<CornerLeftTopIcon color="white" /> <CornerLeftTopIcon color="white" />
</div> </button>
<div class="crop-border right top"><CornerRightTopIcon color="white" /></div> <button @pointerdown="onPointerDown($event, ['right', 'top'])" class="crop-border right top">
<div class="crop-border left bottom"> <CornerRightTopIcon color="white" />
<CornerLeftBottomIcon color="white" /> </button>
</div> <button @pointerdown="onPointerDown($event, ['right', 'bottom'])" class="crop-border right bottom">
<div class="crop-border right bottom">
<CornerRightBottomIcon color="white" /> <CornerRightBottomIcon color="white" />
</div> </button>
<button @pointerdown="onPointerDown($event, ['left', 'bottom'])" class="crop-border left bottom">
<CornerLeftBottomIcon color="white" />
</button>
</div> </div>
<div <div
v-show="imageSource" v-show="imageSource"
...@@ -89,7 +134,7 @@ watch( ...@@ -89,7 +134,7 @@ watch(
align-items: center; align-items: center;
width: max-content; width: max-content;
} }
.canvas-container { #canvas-container {
position: relative; position: relative;
line-height: 0; line-height: 0;
} }
...@@ -117,14 +162,17 @@ watch( ...@@ -117,14 +162,17 @@ watch(
.crop-border:active { .crop-border:active {
opacity: 1; opacity: 1;
} }
.left {
left: v-bind(left);
}
.top { .top {
top: 0; top: v-bind(top);
} }
.right { .right {
right: 0; right: v-bind(right);
} }
.bottom { .bottom {
bottom: 0; bottom: v-bind(bottom);
} }
.flexVertical { .flexVertical {
flex-direction: column; flex-direction: column;
......
...@@ -5,3 +5,10 @@ export const getImageInfo = (image: string | File, instance: HTMLImageElement) = ...@@ -5,3 +5,10 @@ export const getImageInfo = (image: string | File, instance: HTMLImageElement) =
return [instance, 0, 0, instance.width, instance.height]; return [instance, 0, 0, instance.width, instance.height];
}; };
}; };
export const calcContainerRect = () => {
const container = document.querySelector('#canvas-container');
return container?.getBoundingClientRect();
};
export const onBorderMove = (event: PointerEvent, sides: [string, string]) => {};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment