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

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

Refactor editor components to editor folder, fix thresholded image display

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