Updated ExtendedImage to composition api & typescript.
This commit is contained in:
parent
6332514ccc
commit
a125d2c356
@ -13,205 +13,228 @@
|
|||||||
<img class="image-ex-img image-ex-img-center" ref="imgex" @load="onLoad" />
|
<img class="image-ex-img image-ex-img-center" ref="imgex" @load="onLoad" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import throttle from "lodash/throttle";
|
import throttle from "lodash/throttle";
|
||||||
import UTIF from "utif";
|
import UTIF from "utif";
|
||||||
|
import { onBeforeUnmount, onMounted, ref, watch } from "vue";
|
||||||
|
|
||||||
export default {
|
interface IProps {
|
||||||
props: {
|
src: string;
|
||||||
src: String,
|
moveDisabledTime: number;
|
||||||
moveDisabledTime: {
|
classList: any[];
|
||||||
type: Number,
|
zoomStep: number;
|
||||||
default: () => 200,
|
}
|
||||||
},
|
|
||||||
classList: {
|
const props = withDefaults(defineProps<IProps>(), {
|
||||||
type: Array,
|
moveDisabledTime: () => 200,
|
||||||
default: () => [],
|
classList: () => [],
|
||||||
},
|
zoomStep: () => 0.25,
|
||||||
zoomStep: {
|
});
|
||||||
type: Number,
|
|
||||||
default: () => 0.25,
|
const scale = ref<number>(1);
|
||||||
},
|
const lastX = ref<number | null>(null);
|
||||||
},
|
const lastY = ref<number | null>(null);
|
||||||
data() {
|
const inDrag = ref<boolean>(false);
|
||||||
return {
|
const touches = ref<number>(0);
|
||||||
scale: 1,
|
const lastTouchDistance = ref<number | null>(0);
|
||||||
lastX: null,
|
const moveDisabled = ref<boolean>(false);
|
||||||
lastY: null,
|
const disabledTimer = ref<number | null>(null);
|
||||||
inDrag: false,
|
const imageLoaded = ref<boolean>(false);
|
||||||
touches: 0,
|
const position = ref<{
|
||||||
lastTouchDistance: 0,
|
center: { x: number; y: number };
|
||||||
moveDisabled: false,
|
relative: { x: number; y: number };
|
||||||
disabledTimer: null,
|
}>({
|
||||||
imageLoaded: false,
|
|
||||||
position: {
|
|
||||||
center: { x: 0, y: 0 },
|
center: { x: 0, y: 0 },
|
||||||
relative: { x: 0, y: 0 },
|
relative: { x: 0, y: 0 },
|
||||||
},
|
});
|
||||||
maxScale: 4,
|
const maxScale = ref<number>(4);
|
||||||
minScale: 0.25,
|
const minScale = ref<number>(0.25);
|
||||||
};
|
|
||||||
},
|
// Refs
|
||||||
mounted() {
|
const imgex = ref<HTMLImageElement | null>(null);
|
||||||
if (!this.decodeUTIF()) {
|
const container = ref<HTMLDivElement | null>(null);
|
||||||
this.$refs.imgex.src = this.src;
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!decodeUTIF() && imgex.value !== null) {
|
||||||
|
imgex.value.src = props.src;
|
||||||
}
|
}
|
||||||
let container = this.$refs.container;
|
|
||||||
this.classList.forEach((className) => container.classList.add(className));
|
props.classList.forEach((className) =>
|
||||||
|
container.value !== null ? container.value.classList.add(className) : ""
|
||||||
|
);
|
||||||
|
|
||||||
|
if (container.value === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// set width and height if they are zero
|
// set width and height if they are zero
|
||||||
if (getComputedStyle(container).width === "0px") {
|
if (getComputedStyle(container.value).width === "0px") {
|
||||||
container.style.width = "100%";
|
container.value.style.width = "100%";
|
||||||
}
|
}
|
||||||
if (getComputedStyle(container).height === "0px") {
|
if (getComputedStyle(container.value).height === "0px") {
|
||||||
container.style.height = "100%";
|
container.value.style.height = "100%";
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("resize", this.onResize);
|
window.addEventListener("resize", onResize);
|
||||||
},
|
});
|
||||||
beforeUnmount() {
|
|
||||||
window.removeEventListener("resize", this.onResize);
|
onBeforeUnmount(() => {
|
||||||
document.removeEventListener("mouseup", this.onMouseUp);
|
window.removeEventListener("resize", onResize);
|
||||||
},
|
document.removeEventListener("mouseup", onMouseUp);
|
||||||
watch: {
|
});
|
||||||
src: function () {
|
|
||||||
if (!this.decodeUTIF()) {
|
// @ts-ignore
|
||||||
this.$refs.imgex.src = this.src;
|
watch(props.src, () => {
|
||||||
|
if (!decodeUTIF() && imgex.value !== null) {
|
||||||
|
imgex.value.src = props.src;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.scale = 1;
|
scale.value = 1;
|
||||||
this.setZoom();
|
setZoom();
|
||||||
this.setCenter();
|
setCenter();
|
||||||
},
|
});
|
||||||
},
|
|
||||||
methods: {
|
// Modified from UTIF.replaceIMG
|
||||||
// Modified from UTIF.replaceIMG
|
const decodeUTIF = () => {
|
||||||
decodeUTIF() {
|
|
||||||
const sufs = ["tif", "tiff", "dng", "cr2", "nef"];
|
const sufs = ["tif", "tiff", "dng", "cr2", "nef"];
|
||||||
let suff = document.location.pathname.split(".").pop().toLowerCase();
|
if (document?.location?.pathname === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let suff = document.location.pathname.split(".")?.pop()?.toLowerCase() ?? "";
|
||||||
|
|
||||||
if (sufs.indexOf(suff) == -1) return false;
|
if (sufs.indexOf(suff) == -1) return false;
|
||||||
let xhr = new XMLHttpRequest();
|
let xhr = new XMLHttpRequest();
|
||||||
UTIF._xhrs.push(xhr);
|
UTIF._xhrs.push(xhr);
|
||||||
UTIF._imgs.push(this.$refs.imgex);
|
UTIF._imgs.push(imgex.value);
|
||||||
xhr.open("GET", this.src);
|
xhr.open("GET", props.src);
|
||||||
xhr.responseType = "arraybuffer";
|
xhr.responseType = "arraybuffer";
|
||||||
xhr.onload = UTIF._imgLoaded;
|
xhr.onload = UTIF._imgLoaded;
|
||||||
xhr.send();
|
xhr.send();
|
||||||
return true;
|
return true;
|
||||||
},
|
};
|
||||||
onLoad() {
|
|
||||||
let img = this.$refs.imgex;
|
|
||||||
|
|
||||||
this.imageLoaded = true;
|
const onLoad = () => {
|
||||||
|
imageLoaded.value = true;
|
||||||
|
|
||||||
if (img === undefined) {
|
if (imgex.value === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
img.classList.remove("image-ex-img-center");
|
imgex.value.classList.remove("image-ex-img-center");
|
||||||
this.setCenter();
|
setCenter();
|
||||||
img.classList.add("image-ex-img-ready");
|
imgex.value.classList.add("image-ex-img-ready");
|
||||||
|
|
||||||
document.addEventListener("mouseup", this.onMouseUp);
|
document.addEventListener("mouseup", onMouseUp);
|
||||||
|
|
||||||
let realSize = img.naturalWidth;
|
let realSize = imgex.value.naturalWidth;
|
||||||
let displaySize = img.offsetWidth;
|
let displaySize = imgex.value.offsetWidth;
|
||||||
|
|
||||||
// Image is in portrait orientation
|
// Image is in portrait orientation
|
||||||
if (img.naturalHeight > img.naturalWidth) {
|
if (imgex.value.naturalHeight > imgex.value.naturalWidth) {
|
||||||
realSize = img.naturalHeight;
|
realSize = imgex.value.naturalHeight;
|
||||||
displaySize = img.offsetHeight;
|
displaySize = imgex.value.offsetHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scale needed to display the image on full size
|
// Scale needed to display the image on full size
|
||||||
const fullScale = realSize / displaySize;
|
const fullScale = realSize / displaySize;
|
||||||
|
|
||||||
// Full size plus additional zoom
|
// Full size plus additional zoom
|
||||||
this.maxScale = fullScale + 4;
|
maxScale.value = fullScale + 4;
|
||||||
},
|
};
|
||||||
onMouseUp() {
|
|
||||||
this.inDrag = false;
|
const onMouseUp = () => {
|
||||||
},
|
inDrag.value = false;
|
||||||
onResize: throttle(function () {
|
};
|
||||||
if (this.imageLoaded) {
|
|
||||||
this.setCenter();
|
const onResize = throttle(function () {
|
||||||
this.doMove(this.position.relative.x, this.position.relative.y);
|
if (imageLoaded.value) {
|
||||||
|
setCenter();
|
||||||
|
doMove(position.value.relative.x, position.value.relative.y);
|
||||||
}
|
}
|
||||||
}, 100),
|
}, 100);
|
||||||
setCenter() {
|
|
||||||
let container = this.$refs.container;
|
|
||||||
let img = this.$refs.imgex;
|
|
||||||
|
|
||||||
this.position.center.x = Math.floor(
|
const setCenter = () => {
|
||||||
(container.clientWidth - img.clientWidth) / 2
|
if (container.value === null || imgex.value === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
position.value.center.x = Math.floor(
|
||||||
|
(container.value.clientWidth - imgex.value.clientWidth) / 2
|
||||||
);
|
);
|
||||||
this.position.center.y = Math.floor(
|
position.value.center.y = Math.floor(
|
||||||
(container.clientHeight - img.clientHeight) / 2
|
(container.value.clientHeight - imgex.value.clientHeight) / 2
|
||||||
);
|
);
|
||||||
|
|
||||||
img.style.left = this.position.center.x + "px";
|
imgex.value.style.left = position.value.center.x + "px";
|
||||||
img.style.top = this.position.center.y + "px";
|
imgex.value.style.top = position.value.center.y + "px";
|
||||||
},
|
};
|
||||||
mousedownStart(event) {
|
|
||||||
this.lastX = null;
|
const mousedownStart = (event: Event) => {
|
||||||
this.lastY = null;
|
lastX.value = null;
|
||||||
this.inDrag = true;
|
lastY.value = null;
|
||||||
|
inDrag.value = true;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
},
|
};
|
||||||
mouseMove(event) {
|
const mouseMove = (event: MouseEvent) => {
|
||||||
if (!this.inDrag) return;
|
if (!inDrag.value) return;
|
||||||
this.doMove(event.movementX, event.movementY);
|
doMove(event.movementX, event.movementY);
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
},
|
};
|
||||||
mouseUp(event) {
|
const mouseUp = (event: Event) => {
|
||||||
this.inDrag = false;
|
inDrag.value = false;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
},
|
};
|
||||||
touchStart(event) {
|
const touchStart = (event: TouchEvent) => {
|
||||||
this.lastX = null;
|
lastX.value = null;
|
||||||
this.lastY = null;
|
lastY.value = null;
|
||||||
this.lastTouchDistance = null;
|
lastTouchDistance.value = null;
|
||||||
if (event.targetTouches.length < 2) {
|
if (event.targetTouches.length < 2) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.touches = 0;
|
touches.value = 0;
|
||||||
}, 300);
|
}, 300);
|
||||||
this.touches++;
|
touches.value++;
|
||||||
if (this.touches > 1) {
|
if (touches.value > 1) {
|
||||||
this.zoomAuto(event);
|
zoomAuto(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
},
|
};
|
||||||
zoomAuto(event) {
|
|
||||||
switch (this.scale) {
|
const zoomAuto = (event: Event) => {
|
||||||
|
switch (scale.value) {
|
||||||
case 1:
|
case 1:
|
||||||
this.scale = 2;
|
scale.value = 2;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
this.scale = 4;
|
scale.value = 4;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
case 4:
|
case 4:
|
||||||
this.scale = 1;
|
scale.value = 1;
|
||||||
this.setCenter();
|
setCenter();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.setZoom();
|
setZoom();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
},
|
};
|
||||||
touchMove(event) {
|
|
||||||
|
const touchMove = (event: TouchEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (this.lastX === null) {
|
if (lastX.value === null) {
|
||||||
this.lastX = event.targetTouches[0].pageX;
|
lastX.value = event.targetTouches[0].pageX;
|
||||||
this.lastY = event.targetTouches[0].pageY;
|
lastY.value = event.targetTouches[0].pageY;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let step = this.$refs.imgex.width / 5;
|
if (imgex.value === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let step = imgex.value.width / 5;
|
||||||
if (event.targetTouches.length === 2) {
|
if (event.targetTouches.length === 2) {
|
||||||
this.moveDisabled = true;
|
moveDisabled.value = true;
|
||||||
clearTimeout(this.disabledTimer);
|
if (disabledTimer.value) clearTimeout(disabledTimer.value);
|
||||||
this.disabledTimer = setTimeout(
|
disabledTimer.value = window.setTimeout(
|
||||||
() => (this.moveDisabled = false),
|
() => (moveDisabled.value = false),
|
||||||
this.moveDisabledTime
|
props.moveDisabledTime
|
||||||
);
|
);
|
||||||
|
|
||||||
let p1 = event.targetTouches[0];
|
let p1 = event.targetTouches[0];
|
||||||
@ -219,55 +242,59 @@ export default {
|
|||||||
let touchDistance = Math.sqrt(
|
let touchDistance = Math.sqrt(
|
||||||
Math.pow(p2.pageX - p1.pageX, 2) + Math.pow(p2.pageY - p1.pageY, 2)
|
Math.pow(p2.pageX - p1.pageX, 2) + Math.pow(p2.pageY - p1.pageY, 2)
|
||||||
);
|
);
|
||||||
if (!this.lastTouchDistance) {
|
if (!lastTouchDistance.value) {
|
||||||
this.lastTouchDistance = touchDistance;
|
lastTouchDistance.value = touchDistance;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.scale += (touchDistance - this.lastTouchDistance) / step;
|
scale.value += (touchDistance - lastTouchDistance.value) / step;
|
||||||
this.lastTouchDistance = touchDistance;
|
lastTouchDistance.value = touchDistance;
|
||||||
this.setZoom();
|
setZoom();
|
||||||
} else if (event.targetTouches.length === 1) {
|
} else if (event.targetTouches.length === 1) {
|
||||||
if (this.moveDisabled) return;
|
if (moveDisabled.value) return;
|
||||||
let x = event.targetTouches[0].pageX - this.lastX;
|
let x = event.targetTouches[0].pageX - (lastX.value ?? 0);
|
||||||
let y = event.targetTouches[0].pageY - this.lastY;
|
let y = event.targetTouches[0].pageY - (lastY.value ?? 0);
|
||||||
if (Math.abs(x) >= step && Math.abs(y) >= step) return;
|
if (Math.abs(x) >= step && Math.abs(y) >= step) return;
|
||||||
this.lastX = event.targetTouches[0].pageX;
|
lastX.value = event.targetTouches[0].pageX;
|
||||||
this.lastY = event.targetTouches[0].pageY;
|
lastY.value = event.targetTouches[0].pageY;
|
||||||
this.doMove(x, y);
|
doMove(x, y);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
doMove(x, y) {
|
|
||||||
let style = this.$refs.imgex.style;
|
const doMove = (x: number, y: number) => {
|
||||||
let posX = this.pxStringToNumber(style.left) + x;
|
if (imgex.value === null) {
|
||||||
let posY = this.pxStringToNumber(style.top) + y;
|
return;
|
||||||
|
}
|
||||||
|
const style = imgex.value.style;
|
||||||
|
|
||||||
|
let posX = pxStringToNumber(style.left) + x;
|
||||||
|
let posY = pxStringToNumber(style.top) + y;
|
||||||
|
|
||||||
style.left = posX + "px";
|
style.left = posX + "px";
|
||||||
style.top = posY + "px";
|
style.top = posY + "px";
|
||||||
|
|
||||||
this.position.relative.x = Math.abs(this.position.center.x - posX);
|
position.value.relative.x = Math.abs(position.value.center.x - posX);
|
||||||
this.position.relative.y = Math.abs(this.position.center.y - posY);
|
position.value.relative.y = Math.abs(position.value.center.y - posY);
|
||||||
|
|
||||||
if (posX < this.position.center.x) {
|
if (posX < position.value.center.x) {
|
||||||
this.position.relative.x = this.position.relative.x * -1;
|
position.value.relative.x = position.value.relative.x * -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (posY < this.position.center.y) {
|
if (posY < position.value.center.y) {
|
||||||
this.position.relative.y = this.position.relative.y * -1;
|
position.value.relative.y = position.value.relative.y * -1;
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
wheelMove(event) {
|
const wheelMove = (event: WheelEvent) => {
|
||||||
this.scale += -Math.sign(event.deltaY) * this.zoomStep;
|
scale.value += -Math.sign(event.deltaY) * props.zoomStep;
|
||||||
this.setZoom();
|
setZoom();
|
||||||
},
|
};
|
||||||
setZoom() {
|
const setZoom = () => {
|
||||||
this.scale = this.scale < this.minScale ? this.minScale : this.scale;
|
scale.value = scale.value < minScale.value ? minScale.value : scale.value;
|
||||||
this.scale = this.scale > this.maxScale ? this.maxScale : this.scale;
|
scale.value = scale.value > maxScale.value ? maxScale.value : scale.value;
|
||||||
this.$refs.imgex.style.transform = `scale(${this.scale})`;
|
if (imgex.value !== null)
|
||||||
},
|
imgex.value.style.transform = `scale(${scale.value})`;
|
||||||
pxStringToNumber(style) {
|
};
|
||||||
|
const pxStringToNumber = (style: string) => {
|
||||||
return +style.replace("px", "");
|
return +style.replace("px", "");
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user