@file-viewer/renderer-cad 3.0.0 → 3.0.1
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.
- package/dist/cad.js +226 -2
- package/package.json +2 -2
package/dist/cad.js
CHANGED
|
@@ -5,6 +5,8 @@ const CAD_WORKER_TIMEOUT = 120000;
|
|
|
5
5
|
const CAD_DEFAULT_FIT_PADDING = 0.92;
|
|
6
6
|
const CAD_BOUNDS_EPSILON = 1e-9;
|
|
7
7
|
const CAD_BEST_FIT_OUTLIER_RATIO = 8;
|
|
8
|
+
const CAD_NATIVE_MIN_ZOOM_RATIO = 0.05;
|
|
9
|
+
const CAD_NATIVE_MAX_ZOOM_RATIO = 64;
|
|
8
10
|
const cadStyle = `
|
|
9
11
|
.cad-shell{display:flex;height:100%;min-height:100%;flex-direction:column;background:#f5f7fb;color:#142335}
|
|
10
12
|
.cad-shell *{box-sizing:border-box}
|
|
@@ -198,6 +200,105 @@ const getCadDocumentBaseUrl = (target) => {
|
|
|
198
200
|
: 'file:///';
|
|
199
201
|
};
|
|
200
202
|
const CAD_DWG_WORKER_FILENAME = 'file-viewer-input.dwg';
|
|
203
|
+
const getCadPinchSnapshot = (points) => {
|
|
204
|
+
const [first, second] = Array.from(points.values());
|
|
205
|
+
if (!first || !second) {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
center: {
|
|
210
|
+
x: (first.x + second.x) / 2,
|
|
211
|
+
y: (first.y + second.y) / 2,
|
|
212
|
+
},
|
|
213
|
+
distance: Math.hypot(second.x - first.x, second.y - first.y),
|
|
214
|
+
};
|
|
215
|
+
};
|
|
216
|
+
/**
|
|
217
|
+
* The bundled CAD engines currently treat every touch pointer as the same
|
|
218
|
+
* drag. Coordinate two touch pointers before their target listeners run so a
|
|
219
|
+
* pinch becomes one anchored zoom operation instead of two competing pans.
|
|
220
|
+
*/
|
|
221
|
+
const registerCadPinchZoom = (surface, onStart, onZoom) => {
|
|
222
|
+
const points = new Map();
|
|
223
|
+
let previous = null;
|
|
224
|
+
let suppressDragUntilRelease = false;
|
|
225
|
+
const suppressPointerEvent = (event) => {
|
|
226
|
+
event.preventDefault();
|
|
227
|
+
event.stopImmediatePropagation();
|
|
228
|
+
};
|
|
229
|
+
const handlePointerDown = (event) => {
|
|
230
|
+
if (event.pointerType !== 'touch') {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
points.set(event.pointerId, { x: event.clientX, y: event.clientY });
|
|
234
|
+
try {
|
|
235
|
+
surface.setPointerCapture(event.pointerId);
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
// Synthetic browser harness events may not create an active pointer.
|
|
239
|
+
}
|
|
240
|
+
if (points.size < 2) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
suppressDragUntilRelease = true;
|
|
244
|
+
previous = getCadPinchSnapshot(points);
|
|
245
|
+
onStart();
|
|
246
|
+
suppressPointerEvent(event);
|
|
247
|
+
};
|
|
248
|
+
const handlePointerMove = (event) => {
|
|
249
|
+
if (event.pointerType !== 'touch' || !points.has(event.pointerId)) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
points.set(event.pointerId, { x: event.clientX, y: event.clientY });
|
|
253
|
+
if (!suppressDragUntilRelease) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (points.size >= 2) {
|
|
257
|
+
const current = getCadPinchSnapshot(points);
|
|
258
|
+
if (previous && current && previous.distance > 0 && current.distance > 0) {
|
|
259
|
+
onZoom({
|
|
260
|
+
center: current.center,
|
|
261
|
+
deltaX: current.center.x - previous.center.x,
|
|
262
|
+
deltaY: current.center.y - previous.center.y,
|
|
263
|
+
factor: Math.min(2, Math.max(0.5, current.distance / previous.distance)),
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
previous = current;
|
|
267
|
+
}
|
|
268
|
+
suppressPointerEvent(event);
|
|
269
|
+
};
|
|
270
|
+
const handlePointerEnd = (event) => {
|
|
271
|
+
if (event.pointerType !== 'touch' || !points.has(event.pointerId)) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
points.delete(event.pointerId);
|
|
275
|
+
previous = points.size >= 2 ? getCadPinchSnapshot(points) : null;
|
|
276
|
+
if (points.size === 0) {
|
|
277
|
+
suppressDragUntilRelease = false;
|
|
278
|
+
}
|
|
279
|
+
try {
|
|
280
|
+
if (surface.hasPointerCapture(event.pointerId)) {
|
|
281
|
+
surface.releasePointerCapture(event.pointerId);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
// The underlying engine may already have released this pointer.
|
|
286
|
+
}
|
|
287
|
+
// Do not stop pointerup/cancel: the engine must clear the single-pointer
|
|
288
|
+
// drag that began before the second finger started the pinch.
|
|
289
|
+
};
|
|
290
|
+
surface.addEventListener('pointerdown', handlePointerDown, { capture: true });
|
|
291
|
+
surface.addEventListener('pointermove', handlePointerMove, { capture: true });
|
|
292
|
+
surface.addEventListener('pointerup', handlePointerEnd, { capture: true });
|
|
293
|
+
surface.addEventListener('pointercancel', handlePointerEnd, { capture: true });
|
|
294
|
+
return () => {
|
|
295
|
+
surface.removeEventListener('pointerdown', handlePointerDown, { capture: true });
|
|
296
|
+
surface.removeEventListener('pointermove', handlePointerMove, { capture: true });
|
|
297
|
+
surface.removeEventListener('pointerup', handlePointerEnd, { capture: true });
|
|
298
|
+
surface.removeEventListener('pointercancel', handlePointerEnd, { capture: true });
|
|
299
|
+
points.clear();
|
|
300
|
+
};
|
|
301
|
+
};
|
|
201
302
|
const createEmptyCadBounds = () => ({
|
|
202
303
|
minX: Number.POSITIVE_INFINITY,
|
|
203
304
|
minY: Number.POSITIVE_INFINITY,
|
|
@@ -557,7 +658,10 @@ export default async function renderCad(buffer, target, type = 'dxf', context) {
|
|
|
557
658
|
let viewer = null;
|
|
558
659
|
let resizeObserver = null;
|
|
559
660
|
let abortController = null;
|
|
661
|
+
let disposeCadPinch = null;
|
|
662
|
+
let disposeNativeInteractions = null;
|
|
560
663
|
let fitViewActive = true;
|
|
664
|
+
let nativeZoomRatio = 1;
|
|
561
665
|
let disposed = false;
|
|
562
666
|
const style = createStyle();
|
|
563
667
|
const shell = createElement('div', 'cad-shell');
|
|
@@ -615,8 +719,11 @@ export default async function renderCad(buffer, target, type = 'dxf', context) {
|
|
|
615
719
|
};
|
|
616
720
|
const getWarnings = () => (loadResult === null || loadResult === void 0 ? void 0 : loadResult.warnings) || (loadResult === null || loadResult === void 0 ? void 0 : loadResult.document.warnings) || [];
|
|
617
721
|
const getZoomPercent = () => {
|
|
618
|
-
var _a, _b, _c;
|
|
619
|
-
|
|
722
|
+
var _a, _b, _c, _d;
|
|
723
|
+
if ((_a = viewer === null || viewer === void 0 ? void 0 : viewer.isNativeRendererActive) === null || _a === void 0 ? void 0 : _a.call(viewer)) {
|
|
724
|
+
return Math.round(nativeZoomRatio * 100);
|
|
725
|
+
}
|
|
726
|
+
const zoom = (_d = (_b = viewState === null || viewState === void 0 ? void 0 : viewState.zoomPercent) !== null && _b !== void 0 ? _b : (_c = viewer === null || viewer === void 0 ? void 0 : viewer.getZoomPercent) === null || _c === void 0 ? void 0 : _c.call(viewer)) !== null && _d !== void 0 ? _d : 100;
|
|
620
727
|
return Number.isFinite(zoom) ? Math.round(zoom) : 100;
|
|
621
728
|
};
|
|
622
729
|
const getCadZoomState = () => {
|
|
@@ -655,6 +762,98 @@ export default async function renderCad(buffer, target, type = 'dxf', context) {
|
|
|
655
762
|
}
|
|
656
763
|
return state;
|
|
657
764
|
};
|
|
765
|
+
const clampNativeZoomRatio = (ratio) => {
|
|
766
|
+
return Math.min(CAD_NATIVE_MAX_ZOOM_RATIO, Math.max(CAD_NATIVE_MIN_ZOOM_RATIO, ratio));
|
|
767
|
+
};
|
|
768
|
+
const notifyNativeZoomChange = (action) => {
|
|
769
|
+
cadZoomEmitter.emit();
|
|
770
|
+
syncState();
|
|
771
|
+
emitCadViewStateChange(action, 'user');
|
|
772
|
+
};
|
|
773
|
+
const bindNativeCadInteractions = () => {
|
|
774
|
+
disposeNativeInteractions === null || disposeNativeInteractions === void 0 ? void 0 : disposeNativeInteractions();
|
|
775
|
+
disposeNativeInteractions = null;
|
|
776
|
+
const surface = nativeHost.querySelector('.dwfv-overlay-canvas');
|
|
777
|
+
if (!surface) {
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
let notificationQueued = false;
|
|
781
|
+
const queueNativeZoomChange = (action) => {
|
|
782
|
+
if (notificationQueued) {
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
notificationQueued = true;
|
|
786
|
+
queueMicrotask(() => {
|
|
787
|
+
var _a;
|
|
788
|
+
notificationQueued = false;
|
|
789
|
+
if (!disposed && ((_a = viewer === null || viewer === void 0 ? void 0 : viewer.isNativeRendererActive) === null || _a === void 0 ? void 0 : _a.call(viewer))) {
|
|
790
|
+
notifyNativeZoomChange(action);
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
};
|
|
794
|
+
const applyNativeZoomFactor = (factor, action) => {
|
|
795
|
+
if (!Number.isFinite(factor) || factor <= 0) {
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
nativeZoomRatio = clampNativeZoomRatio(nativeZoomRatio * factor);
|
|
799
|
+
queueNativeZoomChange(action);
|
|
800
|
+
};
|
|
801
|
+
const handleNativeClick = (event) => {
|
|
802
|
+
const eventTarget = event.target;
|
|
803
|
+
const button = eventTarget instanceof Element ? eventTarget.closest('button') : null;
|
|
804
|
+
if (!button || !nativeHost.contains(button)) {
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
const label = (button.textContent || '').trim();
|
|
808
|
+
if (label === '+') {
|
|
809
|
+
applyNativeZoomFactor(1.25, 'zoom-in');
|
|
810
|
+
}
|
|
811
|
+
else if (label === '-' || label === '−') {
|
|
812
|
+
applyNativeZoomFactor(0.8, 'zoom-out');
|
|
813
|
+
}
|
|
814
|
+
else if (/适应|fit|reset/i.test(label)) {
|
|
815
|
+
nativeZoomRatio = 1;
|
|
816
|
+
queueNativeZoomChange('zoom-reset');
|
|
817
|
+
}
|
|
818
|
+
};
|
|
819
|
+
const handleNativeWheel = (event) => {
|
|
820
|
+
const rect = surface.getBoundingClientRect();
|
|
821
|
+
const delta = event.deltaMode === WheelEvent.DOM_DELTA_LINE
|
|
822
|
+
? event.deltaY * 16
|
|
823
|
+
: event.deltaMode === WheelEvent.DOM_DELTA_PAGE
|
|
824
|
+
? event.deltaY * Math.max(1, rect.height)
|
|
825
|
+
: event.deltaY;
|
|
826
|
+
applyNativeZoomFactor(Math.exp(-delta * 0.0015), 'cad-view-change');
|
|
827
|
+
};
|
|
828
|
+
const handleNativePageChange = (event) => {
|
|
829
|
+
if (event.target instanceof HTMLSelectElement && nativeHost.contains(event.target)) {
|
|
830
|
+
nativeZoomRatio = 1;
|
|
831
|
+
queueNativeZoomChange('zoom-reset');
|
|
832
|
+
}
|
|
833
|
+
};
|
|
834
|
+
const disposePinch = registerCadPinchZoom(surface, () => {
|
|
835
|
+
fitViewActive = false;
|
|
836
|
+
}, ({ center, factor }) => {
|
|
837
|
+
const deltaY = -Math.log(factor) / 0.0015;
|
|
838
|
+
surface.dispatchEvent(new WheelEvent('wheel', {
|
|
839
|
+
bubbles: true,
|
|
840
|
+
cancelable: true,
|
|
841
|
+
clientX: center.x,
|
|
842
|
+
clientY: center.y,
|
|
843
|
+
deltaMode: WheelEvent.DOM_DELTA_PIXEL,
|
|
844
|
+
deltaY,
|
|
845
|
+
}));
|
|
846
|
+
});
|
|
847
|
+
nativeHost.addEventListener('click', handleNativeClick);
|
|
848
|
+
nativeHost.addEventListener('change', handleNativePageChange);
|
|
849
|
+
surface.addEventListener('wheel', handleNativeWheel);
|
|
850
|
+
disposeNativeInteractions = () => {
|
|
851
|
+
disposePinch();
|
|
852
|
+
nativeHost.removeEventListener('click', handleNativeClick);
|
|
853
|
+
nativeHost.removeEventListener('change', handleNativePageChange);
|
|
854
|
+
surface.removeEventListener('wheel', handleNativeWheel);
|
|
855
|
+
};
|
|
856
|
+
};
|
|
658
857
|
const syncInspector = () => {
|
|
659
858
|
const summary = loadResult === null || loadResult === void 0 ? void 0 : loadResult.summary;
|
|
660
859
|
const rows = [
|
|
@@ -744,6 +943,7 @@ export default async function renderCad(buffer, target, type = 'dxf', context) {
|
|
|
744
943
|
return false;
|
|
745
944
|
}
|
|
746
945
|
if ((_a = viewer.isNativeRendererActive) === null || _a === void 0 ? void 0 : _a.call(viewer)) {
|
|
946
|
+
nativeZoomRatio = 1;
|
|
747
947
|
viewer.fit();
|
|
748
948
|
return true;
|
|
749
949
|
}
|
|
@@ -918,6 +1118,19 @@ export default async function renderCad(buffer, target, type = 'dxf', context) {
|
|
|
918
1118
|
syncState();
|
|
919
1119
|
},
|
|
920
1120
|
});
|
|
1121
|
+
disposeCadPinch === null || disposeCadPinch === void 0 ? void 0 : disposeCadPinch();
|
|
1122
|
+
const renderer = nextViewer.renderer;
|
|
1123
|
+
disposeCadPinch = registerCadPinchZoom(nextViewer.canvas, () => {
|
|
1124
|
+
fitViewActive = false;
|
|
1125
|
+
}, ({ center, deltaX, deltaY, factor }) => {
|
|
1126
|
+
var _a, _b;
|
|
1127
|
+
const rect = nextViewer.canvas.getBoundingClientRect();
|
|
1128
|
+
(_a = renderer.panByScreenDelta) === null || _a === void 0 ? void 0 : _a.call(renderer, deltaX, deltaY);
|
|
1129
|
+
(_b = renderer.zoom) === null || _b === void 0 ? void 0 : _b.call(renderer, factor, {
|
|
1130
|
+
x: center.x - rect.left,
|
|
1131
|
+
y: center.y - rect.top,
|
|
1132
|
+
});
|
|
1133
|
+
});
|
|
921
1134
|
if (options.preloadDwg !== false && normalizedType === 'dwg') {
|
|
922
1135
|
void nextViewer.preloadDwg({ wasmPath, workerUrl }).catch(() => {
|
|
923
1136
|
// 预热失败不阻断真实加载,loadBuffer 会返回完整错误上下文。
|
|
@@ -926,6 +1139,7 @@ export default async function renderCad(buffer, target, type = 'dxf', context) {
|
|
|
926
1139
|
return nextViewer;
|
|
927
1140
|
};
|
|
928
1141
|
const loadCad = async () => {
|
|
1142
|
+
var _a;
|
|
929
1143
|
status = 'loading';
|
|
930
1144
|
progressMessage = t('cad.state.parsing');
|
|
931
1145
|
errorMessage = '';
|
|
@@ -934,6 +1148,9 @@ export default async function renderCad(buffer, target, type = 'dxf', context) {
|
|
|
934
1148
|
viewState = null;
|
|
935
1149
|
layers = [];
|
|
936
1150
|
fitViewActive = true;
|
|
1151
|
+
nativeZoomRatio = 1;
|
|
1152
|
+
disposeNativeInteractions === null || disposeNativeInteractions === void 0 ? void 0 : disposeNativeInteractions();
|
|
1153
|
+
disposeNativeInteractions = null;
|
|
937
1154
|
syncUi();
|
|
938
1155
|
abortController === null || abortController === void 0 ? void 0 : abortController.abort();
|
|
939
1156
|
const controller = new AbortController();
|
|
@@ -956,6 +1173,9 @@ export default async function renderCad(buffer, target, type = 'dxf', context) {
|
|
|
956
1173
|
loadResult = restoreLoadResultSourceName(result);
|
|
957
1174
|
layers = collectLayers(result);
|
|
958
1175
|
status = 'ready';
|
|
1176
|
+
if ((_a = viewer.isNativeRendererActive) === null || _a === void 0 ? void 0 : _a.call(viewer)) {
|
|
1177
|
+
bindNativeCadInteractions();
|
|
1178
|
+
}
|
|
959
1179
|
syncUi();
|
|
960
1180
|
queueMicrotask(() => {
|
|
961
1181
|
applyCadFit();
|
|
@@ -999,6 +1219,10 @@ export default async function renderCad(buffer, target, type = 'dxf', context) {
|
|
|
999
1219
|
unregisterFileViewerZoomProvider(shell);
|
|
1000
1220
|
abortController === null || abortController === void 0 ? void 0 : abortController.abort();
|
|
1001
1221
|
abortController = null;
|
|
1222
|
+
disposeCadPinch === null || disposeCadPinch === void 0 ? void 0 : disposeCadPinch();
|
|
1223
|
+
disposeCadPinch = null;
|
|
1224
|
+
disposeNativeInteractions === null || disposeNativeInteractions === void 0 ? void 0 : disposeNativeInteractions();
|
|
1225
|
+
disposeNativeInteractions = null;
|
|
1002
1226
|
resizeObserver === null || resizeObserver === void 0 ? void 0 : resizeObserver.disconnect();
|
|
1003
1227
|
resizeObserver = null;
|
|
1004
1228
|
viewer === null || viewer === void 0 ? void 0 : viewer.destroy();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@file-viewer/renderer-cad",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Standalone CAD renderer plugin for File Viewer powered by @flyfish-dev/cad-viewer.",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"LICENSE"
|
|
57
57
|
],
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@file-viewer/core": "3.0.
|
|
59
|
+
"@file-viewer/core": "3.0.1",
|
|
60
60
|
"@flyfish-dev/cad-viewer": "0.8.0"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|