@file-viewer/renderer-image 2.1.13 → 2.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/image.js +53 -22
  2. package/package.json +2 -2
package/dist/image.js CHANGED
@@ -68,15 +68,8 @@ const resolveImageUrl = async (buffer, type) => {
68
68
  }
69
69
  return readBlobDataUrl(new Blob([buffer], { type: getImageBlobType(normalizedType) }));
70
70
  };
71
- const clampZoom = (value) => {
72
- return Math.min(5, Math.max(0.1, Number(value.toFixed(2))));
73
- };
74
- const applyImageZoom = (image, viewportHeight, zoom) => {
75
- if (viewportHeight > 0) {
76
- image.style.height = `${Math.max(1, Math.round(viewportHeight * zoom))}px`;
77
- return;
78
- }
79
- image.style.height = `${zoom * 100}%`;
71
+ const roundImageScale = (value) => {
72
+ return Number(value.toFixed(3));
80
73
  };
81
74
  const createLightbox = (src, t) => {
82
75
  const lightbox = document.createElement('div');
@@ -117,7 +110,9 @@ const createLightbox = (src, t) => {
117
110
  export default async function renderImage(buffer, target, type, context) {
118
111
  const t = createFileViewerTranslator(context === null || context === void 0 ? void 0 : context.options);
119
112
  const src = await resolveImageUrl(buffer, type);
120
- let zoom = 1;
113
+ let userZoom = 1;
114
+ let fitScale = 1;
115
+ let currentScale = 1;
121
116
  let viewportHeight = 0;
122
117
  const zoomEmitter = createZoomChangeEmitter();
123
118
  const root = document.createElement('div');
@@ -134,32 +129,67 @@ export default async function renderImage(buffer, target, type, context) {
134
129
  const openLightbox = () => lightbox.open();
135
130
  image.addEventListener('click', openLightbox);
136
131
  document.body.append(lightbox.element);
132
+ const getMinScale = () => Math.min(0.1, fitScale || 0.1);
133
+ const clampScale = (value) => {
134
+ const minScale = getMinScale();
135
+ return Math.min(5, Math.max(minScale, roundImageScale(value)));
136
+ };
137
+ const computeFitScale = () => {
138
+ const naturalWidth = image.naturalWidth || 0;
139
+ const naturalHeight = image.naturalHeight || 0;
140
+ if (!naturalWidth || !naturalHeight) {
141
+ return 1;
142
+ }
143
+ const availableWidth = Math.max((root.clientWidth || 0) - 48, 1);
144
+ const availableHeight = Math.max((root.clientHeight || viewportHeight || 0) - 48, 1);
145
+ return Math.min(1, availableWidth / naturalWidth, availableHeight / naturalHeight);
146
+ };
147
+ const applyImageZoom = () => {
148
+ fitScale = computeFitScale();
149
+ currentScale = clampScale(fitScale * userZoom);
150
+ if (image.naturalWidth && image.naturalHeight) {
151
+ image.style.width = `${Math.max(1, Math.round(image.naturalWidth * currentScale))}px`;
152
+ image.style.height = `${Math.max(1, Math.round(image.naturalHeight * currentScale))}px`;
153
+ return;
154
+ }
155
+ image.style.width = 'auto';
156
+ image.style.height = viewportHeight > 0
157
+ ? `${Math.max(1, Math.round(viewportHeight * userZoom))}px`
158
+ : `${userZoom * 100}%`;
159
+ };
137
160
  const updateViewportSize = () => {
138
161
  viewportHeight = root.clientHeight || 0;
139
- applyImageZoom(image, viewportHeight, zoom);
162
+ applyImageZoom();
140
163
  zoomEmitter.emit();
141
164
  };
142
165
  const resizeObserver = new ResizeObserver(updateViewportSize);
143
166
  resizeObserver.observe(root);
167
+ image.addEventListener('load', updateViewportSize);
144
168
  const getZoomState = () => ({
145
- scale: zoom,
146
- label: `${Math.round(zoom * 100)}%`,
147
- canZoomIn: zoom < 5,
148
- canZoomOut: zoom > 0.1,
149
- canReset: zoom !== 1,
150
- minScale: 0.1,
169
+ scale: currentScale,
170
+ label: `${Math.round(currentScale * 100)}%`,
171
+ canZoomIn: currentScale < 5,
172
+ canZoomOut: currentScale > getMinScale(),
173
+ canReset: Math.abs(userZoom - 1) > 0.001,
174
+ minScale: getMinScale(),
151
175
  maxScale: 5,
152
176
  });
153
177
  const setZoom = (scale) => {
154
- zoom = clampZoom(scale);
155
- applyImageZoom(image, viewportHeight, zoom);
178
+ const nextScale = clampScale(scale);
179
+ userZoom = nextScale / Math.max(fitScale, 0.001);
180
+ applyImageZoom();
156
181
  zoomEmitter.emit();
157
182
  return getZoomState();
158
183
  };
159
184
  registerFileViewerZoomProvider(root, {
160
- zoomIn: () => setZoom(zoom + 0.15),
161
- zoomOut: () => setZoom(zoom - 0.15),
162
- resetZoom: () => setZoom(1),
185
+ zoomIn: () => setZoom(currentScale + 0.15),
186
+ zoomOut: () => setZoom(currentScale - 0.15),
187
+ resetZoom: () => {
188
+ userZoom = 1;
189
+ applyImageZoom();
190
+ zoomEmitter.emit();
191
+ return getZoomState();
192
+ },
163
193
  setZoom,
164
194
  getState: getZoomState,
165
195
  subscribe: zoomEmitter.subscribe,
@@ -171,6 +201,7 @@ export default async function renderImage(buffer, target, type, context) {
171
201
  unmount() {
172
202
  unregisterFileViewerZoomProvider(root);
173
203
  resizeObserver.disconnect();
204
+ image.removeEventListener('load', updateViewportSize);
174
205
  image.removeEventListener('click', openLightbox);
175
206
  lightbox.destroy();
176
207
  target.replaceChildren();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@file-viewer/renderer-image",
3
- "version": "2.1.13",
3
+ "version": "2.1.15",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Standalone image renderer plugin for Flyfish File Viewer with native image preview, HEIC/HEIF conversion, lightbox, and unified zoom support.",
@@ -54,7 +54,7 @@
54
54
  "LICENSE"
55
55
  ],
56
56
  "dependencies": {
57
- "@file-viewer/core": "^2.1.13",
57
+ "@file-viewer/core": "^2.1.15",
58
58
  "heic2any": "^0.0.4"
59
59
  },
60
60
  "devDependencies": {