source: main/trunk/model-interfaces-dev/atea/ocr/src/components/OcrImageDisplay.vue@ 35957

Last change on this file since 35957 was 35957, checked in by cstephen, 2 years ago

Refactor OcrImageDisplay to act as a magnification container for an image

File size: 5.6 KB
Line 
1<script>
2import { mapState } from "vuex";
3
4export default {
5 name: "OcrImageDisplay",
6 props: {
7 imageUrl: String,
8 enableMagnifier: Boolean,
9 magnifierZoom: Number
10 },
11 data() {
12 return {
13 /** @type {DOMRect} */
14 magnifierBounds: null
15 }
16 },
17 computed: {
18 ...mapState({
19 translations: state => state.translations
20 })
21 },
22 methods: {
23 /**
24 * Updates the magnifier to operate on a new image.
25 * @param {HTMLImageElement} newImage The new image.
26 * @param {HTMLImageElement} oldImage The old image.
27 * @param {Number} zoom The zoom level.
28 */
29 updateMagnifier(newImage, oldImage = null) {
30 if (oldImage !== null) {
31 oldImage.removeEventListener("mousemove", this.moveMagnifier);
32 }
33
34 /** @type {HTMLDivElement} */
35 const magnifier = this.$refs.magnifier;
36 const magWidth = newImage.width * this.magnifierZoom;
37 const magHeight = newImage.height * this.magnifierZoom;
38
39 magnifier.style.backgroundImage = `url('${newImage.src}')`;
40 magnifier.style.backgroundSize = `${magWidth}px ${magHeight}px`;
41
42 this.magnifierBounds = newImage.getBoundingClientRect();
43 newImage.addEventListener("mousemove", this.moveMagnifier);
44 },
45 /**
46 * Moves the magnifier
47 * @param {MouseEvent} e The movement event.
48 */
49 moveMagnifier(e) {
50 e.preventDefault();
51
52 /** @type {DOMRect} */
53 const magContainerBounds = this.$refs.magContainer.getBoundingClientRect();
54 /** @type {HTMLDivElement} */
55 const magnifier = this.$refs.magnifier;
56
57 const magXCenter = magnifier.offsetWidth / 2;
58 const magYCenter = magnifier.offsetHeight / 2;
59 let { x, y } = this.getRelativeCursorPos(e, magContainerBounds);
60
61 // Calculate our screen-space bounds
62 const relImageLeft = this.magnifierBounds.left - magContainerBounds.left;
63 const relImageTop = this.magnifierBounds.top - magContainerBounds.top;
64 const relImageRight = this.magnifierBounds.right - magContainerBounds.left;
65 const relImageBottom = this.magnifierBounds.bottom - magContainerBounds.top;
66
67 // Ensure the magnifier never leaves the bounds of the image
68 if (x < relImageLeft) {
69 x = relImageLeft;
70 }
71 if (y < relImageTop) {
72 y = relImageTop;
73 }
74 if (x > relImageRight) {
75 x = relImageRight;
76 }
77 if (y > relImageBottom) {
78 y = relImageBottom;
79 }
80
81 // Center the magnifier over the cursor
82 magnifier.style.left = (x - magXCenter) + "px";
83 magnifier.style.top = (y - magYCenter) + "px";
84
85 // Calculate the offset for the magnified image
86 const imageX = x - relImageLeft - (magXCenter / this.magnifierZoom);
87 const imageY = y - relImageTop - (magYCenter / this.magnifierZoom);
88
89 // Convert the offset to valid CSS values
90 const actualX = imageX < 0
91 ? `${(imageX * this.magnifierZoom * -1)}px`
92 : `-${(imageX * this.magnifierZoom)}px`
93
94 const actualY = imageY < 0
95 ? `${(imageY * this.magnifierZoom * -1)}px`
96 : `-${(imageY * this.magnifierZoom)}px`
97
98 magnifier.style.backgroundPosition = `${actualX} ${actualY}`
99 },
100 /**
101 * Gets the position of the cursor relative to a container.
102 * @param {MouseEvent} e The mouse event.
103 * @param {DOMRect} bounds The bounding container.
104 * @returns {{x: Number, y: Number}} The relative X and Y coordinate of the cursor.
105 */
106 getRelativeCursorPos(e, bounds) {
107 let x = e.pageX - bounds.x;
108 let y = e.pageY - bounds.y;
109
110 // Consider any page scrolling
111 x = x - window.scrollX;
112 y = y - window.scrollY;
113
114 return {
115 x: x,
116 y: y
117 };
118 }
119 },
120 watch: {
121 imageUrl() {
122 setTimeout(() => {
123 this.updateMagnifier(this.$refs.imageRef);
124 }, 100);
125 },
126 magnifierZoom() {
127 this.updateMagnifier(this.$refs.imageRef);
128 }
129 },
130 mounted() {
131 setTimeout(() => {
132 this.updateMagnifier(this.$refs.imageRef);
133 }, 100);
134 }
135}
136</script>
137
138<template>
139<div ref="magContainer" class="image-container">
140 <div ref="magnifier" class="magnifier" @mousemove="moveMagnifier" :style="{ 'visibility': enableMagnifier ? 'visible' : 'collapse' }" />
141 <img v-if="!showThresholdedImage" ref="imageRef" class="image" :src="imageUrl" />
142</div>
143</template>
144
145<style lang="scss" scoped>
146.image-container {
147 position: relative;
148
149 &:hover {
150 .control-tip {
151 opacity: 1;
152 }
153
154 .controls {
155 opacity: 1;
156 }
157
158 .magnifier {
159 opacity: 1;
160 }
161 }
162}
163
164.image {
165 border: 1px solid #444;
166 max-width: 100%;
167 max-height: 80vh;
168 object-fit: contain;
169 grid-row: 1;
170 grid-column: 1;
171 user-select: none;
172}
173
174.magnifier {
175 position: absolute;
176 width: 175px;
177 height: 175px;
178
179 background-color: white;
180 background-repeat: no-repeat;
181 border: 2px solid #000;
182 cursor: none;
183 border-radius: 40%;
184
185 transition: opacity var(--transition-duration);
186 opacity: 0.2;
187}
188</style>
Note: See TracBrowser for help on using the repository browser.