@preference-sl/pref-viewer 2.4.0 → 2.5.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/package.json +2 -2
- package/src/index.js +31 -47
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@preference-sl/pref-viewer",
|
|
3
|
+
"version": "2.5.1",
|
|
3
4
|
"description": "Web Component to preview GLTF models with Babylon.js",
|
|
4
5
|
"author": "Alex Moreno Palacio <amoreno@preference.es>",
|
|
5
6
|
"scripts": {
|
|
@@ -35,6 +36,5 @@
|
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|
|
37
38
|
"standard-version": "^9.5.0"
|
|
38
|
-
}
|
|
39
|
-
"version": "2.4.0"
|
|
39
|
+
}
|
|
40
40
|
}
|
package/src/index.js
CHANGED
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
* 3. **Listen for loading events (optional)**
|
|
27
27
|
* const viewer = document.querySelector("pref-viewer");
|
|
28
28
|
* viewer.addEventListener("model-loaded", (evt) => {
|
|
29
|
-
*
|
|
29
|
+
* xx
|
|
30
30
|
* });
|
|
31
31
|
* viewer.addEventListener("model-error", (evt) => {
|
|
32
|
-
*
|
|
32
|
+
* xx
|
|
33
33
|
* });
|
|
34
34
|
*
|
|
35
35
|
* 4. **Change models at runtime**
|
|
@@ -39,7 +39,6 @@
|
|
|
39
39
|
* Implementation code below with updated preprocessUrl
|
|
40
40
|
* -----------------------------------------------------------------------------
|
|
41
41
|
*/
|
|
42
|
-
|
|
43
42
|
import {
|
|
44
43
|
Engine,
|
|
45
44
|
Scene,
|
|
@@ -59,7 +58,7 @@ class PrefViewer extends HTMLElement {
|
|
|
59
58
|
this._createCanvas();
|
|
60
59
|
this._wrapCanvas();
|
|
61
60
|
|
|
62
|
-
//
|
|
61
|
+
// Babylon.js core objects
|
|
63
62
|
this.engine = null;
|
|
64
63
|
this.scene = null;
|
|
65
64
|
this.camera = null;
|
|
@@ -67,10 +66,10 @@ class PrefViewer extends HTMLElement {
|
|
|
67
66
|
this.dirLight = null;
|
|
68
67
|
this._onWindowResize = null;
|
|
69
68
|
|
|
70
|
-
// modelUrl
|
|
69
|
+
// modelUrl will be provided via attribute
|
|
71
70
|
this.modelUrl = null;
|
|
72
71
|
this._hasInitialized = false;
|
|
73
|
-
this._pluginHookSetup = false;
|
|
72
|
+
this._pluginHookSetup = false;
|
|
74
73
|
}
|
|
75
74
|
|
|
76
75
|
static get observedAttributes() {
|
|
@@ -78,9 +77,8 @@ class PrefViewer extends HTMLElement {
|
|
|
78
77
|
}
|
|
79
78
|
|
|
80
79
|
attributeChangedCallback(name, _oldValue, newValue) {
|
|
81
|
-
if (name === "model"
|
|
80
|
+
if (name === "model") {
|
|
82
81
|
this.modelUrl = newValue;
|
|
83
|
-
// Only reload if initialization has already happened
|
|
84
82
|
if (this._hasInitialized) {
|
|
85
83
|
this._reloadModel();
|
|
86
84
|
}
|
|
@@ -88,27 +86,34 @@ class PrefViewer extends HTMLElement {
|
|
|
88
86
|
}
|
|
89
87
|
|
|
90
88
|
connectedCallback() {
|
|
91
|
-
|
|
92
|
-
// Set up the plugin hook ONCE globally before any other initialization
|
|
89
|
+
// Set up URL preprocessing once
|
|
93
90
|
if (!this._pluginHookSetup) {
|
|
94
91
|
this._setupUrlPreprocessing();
|
|
95
92
|
this._pluginHookSetup = true;
|
|
96
93
|
}
|
|
97
94
|
|
|
98
|
-
//
|
|
95
|
+
// Require a model attribute; fail early if missing
|
|
99
96
|
if (!this.hasAttribute("model")) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
this.
|
|
97
|
+
const errorMsg = 'PrefViewer: No "model" attribute provided.';
|
|
98
|
+
console.error(errorMsg);
|
|
99
|
+
this.dispatchEvent(
|
|
100
|
+
new CustomEvent("model-error", {
|
|
101
|
+
detail: { error: new Error(errorMsg) },
|
|
102
|
+
bubbles: true,
|
|
103
|
+
composed: true
|
|
104
|
+
})
|
|
105
|
+
);
|
|
106
|
+
return;
|
|
103
107
|
}
|
|
104
108
|
|
|
105
|
-
//
|
|
106
|
-
this.
|
|
109
|
+
// Initialize modelUrl from attribute
|
|
110
|
+
this.modelUrl = this.getAttribute("model");
|
|
107
111
|
|
|
108
|
-
//
|
|
112
|
+
// Initialize Babylon (engine + scene + camera + lights + hooks)
|
|
113
|
+
this._initializeBabylon();
|
|
109
114
|
this._hasInitialized = true;
|
|
110
115
|
|
|
111
|
-
//
|
|
116
|
+
// Load the specified model
|
|
112
117
|
this._reloadModel();
|
|
113
118
|
}
|
|
114
119
|
|
|
@@ -119,38 +124,29 @@ class PrefViewer extends HTMLElement {
|
|
|
119
124
|
}
|
|
120
125
|
}
|
|
121
126
|
|
|
122
|
-
// ====== URL Preprocessing
|
|
127
|
+
// ====== URL Preprocessing ======
|
|
123
128
|
_setupUrlPreprocessing() {
|
|
124
|
-
|
|
125
129
|
const transformUrl = (url) => {
|
|
126
|
-
// a) If the loader already prepended "blob:…", strip it out.
|
|
127
130
|
const stripped = url.replace(
|
|
128
131
|
/^blob:(?:http|https|file):\/\/[^\/]+\/(.+)/i,
|
|
129
132
|
"$1"
|
|
130
133
|
);
|
|
131
|
-
// b) Normalize backslashes "\" → forward slashes "/"
|
|
132
134
|
const fixedSlashes = stripped.replace(/\\/g, "/");
|
|
133
|
-
|
|
134
|
-
// c) If it now starts with "http://" or "https://", return it as an absolute URL:
|
|
135
135
|
if (/^https?:\/\//i.test(fixedSlashes)) {
|
|
136
136
|
return fixedSlashes;
|
|
137
137
|
}
|
|
138
|
-
// d) Otherwise, return the relative path
|
|
139
138
|
return fixedSlashes;
|
|
140
139
|
};
|
|
141
140
|
|
|
142
141
|
SceneLoader.OnPluginActivatedObservable.add((plugin) => {
|
|
143
142
|
if (plugin.name === "gltf" || plugin.name === "gltf2") {
|
|
144
|
-
// Support both sync and async preprocessing
|
|
145
143
|
plugin.preprocessUrl = transformUrl;
|
|
146
|
-
|
|
147
|
-
plugin.preprocessUrlAsync = (url) => Promise.resolve(transformUrl(url));
|
|
148
|
-
}
|
|
144
|
+
plugin.preprocessUrlAsync = (url) => Promise.resolve(transformUrl(url));
|
|
149
145
|
}
|
|
150
146
|
});
|
|
151
147
|
}
|
|
152
148
|
|
|
153
|
-
// ======
|
|
149
|
+
// ====== Setup Helpers ======
|
|
154
150
|
_createCanvas() {
|
|
155
151
|
this.canvas = document.createElement("canvas");
|
|
156
152
|
Object.assign(this.canvas.style, {
|
|
@@ -172,26 +168,20 @@ class PrefViewer extends HTMLElement {
|
|
|
172
168
|
}
|
|
173
169
|
|
|
174
170
|
_initializeBabylon() {
|
|
175
|
-
|
|
176
|
-
// 1) Create the Babylon engine & scene
|
|
177
171
|
this.engine = new Engine(this.canvas, true, { alpha: true });
|
|
178
172
|
this.scene = new Scene(this.engine);
|
|
179
173
|
this.scene.clearColor = new Color4(1, 1, 1, 1);
|
|
180
174
|
|
|
181
|
-
// 2) Create camera and lights
|
|
182
175
|
this._createCamera();
|
|
183
176
|
this._createLights();
|
|
184
|
-
|
|
185
|
-
// 3) Hook up input/event handlers (e.g. wheel-to-zoom)
|
|
186
177
|
this._setupEventListeners();
|
|
187
178
|
|
|
188
|
-
// 4) Start Babylon's render loop
|
|
189
179
|
this.engine.runRenderLoop(() => {
|
|
190
180
|
if (this.scene) {
|
|
191
181
|
this.scene.render();
|
|
192
182
|
}
|
|
193
183
|
});
|
|
194
|
-
|
|
184
|
+
|
|
195
185
|
this._onWindowResize = () => {
|
|
196
186
|
if (this.engine) {
|
|
197
187
|
this.engine.resize();
|
|
@@ -201,7 +191,6 @@ class PrefViewer extends HTMLElement {
|
|
|
201
191
|
}
|
|
202
192
|
|
|
203
193
|
_createCamera() {
|
|
204
|
-
// ArcRotateCamera that orbits around origin
|
|
205
194
|
this.camera = new ArcRotateCamera(
|
|
206
195
|
"camera",
|
|
207
196
|
Math.PI / 2,
|
|
@@ -231,7 +220,6 @@ class PrefViewer extends HTMLElement {
|
|
|
231
220
|
}
|
|
232
221
|
|
|
233
222
|
_setupEventListeners() {
|
|
234
|
-
// Zoom toward point-of-interest on wheel scroll
|
|
235
223
|
this.canvas.addEventListener("wheel", (evt) => {
|
|
236
224
|
if (!this.scene || !this.camera) return;
|
|
237
225
|
const pickResult = this.scene.pick(
|
|
@@ -248,14 +236,13 @@ class PrefViewer extends HTMLElement {
|
|
|
248
236
|
});
|
|
249
237
|
}
|
|
250
238
|
|
|
251
|
-
// ====== Model
|
|
239
|
+
// ====== Model Management ======
|
|
252
240
|
async _reloadModel() {
|
|
253
241
|
if (!this.scene || !this.modelUrl) {
|
|
254
242
|
console.warn("PrefViewer: _reloadModel aborted (scene or modelUrl missing)");
|
|
255
243
|
return;
|
|
256
244
|
}
|
|
257
245
|
|
|
258
|
-
// Dispose previous meshes so we don't accumulate them
|
|
259
246
|
this._disposePreviousMeshes();
|
|
260
247
|
|
|
261
248
|
try {
|
|
@@ -267,14 +254,11 @@ class PrefViewer extends HTMLElement {
|
|
|
267
254
|
undefined,
|
|
268
255
|
".gltf"
|
|
269
256
|
);
|
|
270
|
-
this.scene.createDefaultCameraOrLight(true, true, true);
|
|
271
257
|
|
|
258
|
+
this.scene.createDefaultCameraOrLight(true, true, true);
|
|
272
259
|
this.dispatchEvent(
|
|
273
260
|
new CustomEvent("model-loaded", {
|
|
274
|
-
detail: {
|
|
275
|
-
meshes: result.meshes,
|
|
276
|
-
particleSystems: result.particleSystems
|
|
277
|
-
},
|
|
261
|
+
detail: { meshes: result.meshes, particleSystems: result.particleSystems },
|
|
278
262
|
bubbles: true,
|
|
279
263
|
composed: true
|
|
280
264
|
})
|
|
@@ -313,4 +297,4 @@ class PrefViewer extends HTMLElement {
|
|
|
313
297
|
}
|
|
314
298
|
}
|
|
315
299
|
|
|
316
|
-
customElements.define("pref-viewer", PrefViewer);
|
|
300
|
+
customElements.define("pref-viewer", PrefViewer);
|