@miris-inc/components 0.0.3 → 0.0.6-02855f9b

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/stream.ts DELETED
@@ -1,217 +0,0 @@
1
- import {
2
- PerspectiveCamera as ThreeCamera,
3
- WebGLRenderer as ThreeRenderer,
4
- } from "three";
5
- import {
6
- MirisScene as ThreeScene,
7
- MirisStream as ThreeStream,
8
- MirisControls as ThreeControls,
9
- } from "@miris-inc/three";
10
- import Bus from "./bus";
11
- import Primitive from "./primitive";
12
- import type Scene from "./scene";
13
-
14
- export default class MirisStream extends Primitive {
15
- static observedAttributes = ["uuid", "key", "disabled", "controls"];
16
-
17
- #disabled = false;
18
- // prettier-ignore
19
- get disabled() { return this.#disabled }
20
- set disabled(disabled) {
21
- if (disabled === this.#disabled) return;
22
-
23
- this.#disabled = disabled;
24
-
25
- if (disabled) {
26
- this.setAttribute("disabled", "");
27
- this._disable();
28
- } else {
29
- this.removeAttribute("disabled");
30
- this._enable();
31
- }
32
- }
33
-
34
- #uuid: string | null = null;
35
- // prettier-ignore
36
- get uuid() { return this.#uuid }
37
- set uuid(uuid) {
38
- if (uuid === this.#uuid) return;
39
-
40
- if (this._threeObject) this._threeObject.clearBoxes();
41
- const oldUuid = this.#uuid;
42
-
43
- this.#uuid = uuid;
44
-
45
- if (uuid) {
46
- this.setAttribute("uuid", uuid);
47
- if (oldUuid) this._disable();
48
- this._enable();
49
- } else {
50
- this.removeAttribute("uuid");
51
- this._disable();
52
- }
53
- }
54
-
55
- #controls = false;
56
- // prettier-ignore
57
- get controls() { return this.#controls }
58
- set controls(controls) {
59
- if (controls === this.#controls) return;
60
-
61
- this.#controls = controls;
62
-
63
- if (controls) this.#control();
64
- else this.#uncontrol();
65
- }
66
-
67
- #threeControls: ThreeControls | null = null;
68
-
69
- #key: string | null = null;
70
- // prettier-ignore
71
- get key() { return this.#key }
72
- set key(key) {
73
- if (key === this.#key) return;
74
-
75
- this.#key = key;
76
-
77
- if (key) this.setAttribute("key", key);
78
- else this.removeAttribute("key");
79
- }
80
-
81
- protected override _threeObject: ThreeStream | null = null;
82
-
83
- #renderer: ThreeRenderer | null = null;
84
- #observer: ResizeObserver | null = null;
85
- #shadow: ShadowRoot | null = null;
86
- #scene: ThreeScene | null = null;
87
-
88
- constructor() {
89
- super();
90
-
91
- this.#key = this.getAttribute("key");
92
- }
93
-
94
- #enableScene() {
95
- if (this.#renderer) return;
96
-
97
- const renderer = new ThreeRenderer({ alpha: true });
98
- const scene = new ThreeScene(this.key);
99
- const camera = new ThreeCamera();
100
- const controls = new ThreeControls(null, camera, renderer.domElement);
101
- const observer = new ResizeObserver((entries: ResizeObserverEntry[]) => {
102
- const entry = entries[entries.length - 1]!;
103
- const width = entry.contentBoxSize[0]!.inlineSize;
104
- const height = entry.contentBoxSize[0]!.blockSize;
105
-
106
- renderer.setPixelRatio(devicePixelRatio);
107
- renderer.setSize(width, height, false);
108
-
109
- camera.aspect = width / height;
110
- camera.updateProjectionMatrix();
111
- });
112
-
113
- if (!this.#shadow) {
114
- this.#shadow = this.attachShadow({ mode: "closed" });
115
- const sheet = new CSSStyleSheet();
116
- sheet.insertRule(":host { display: block }");
117
- sheet.insertRule("canvas { display: block; width: 100%; height: 100% }");
118
- this.#shadow.adoptedStyleSheets.push(sheet);
119
- this.#shadow.append(renderer.domElement);
120
- }
121
-
122
- if (this._threeObject) scene.add(this._threeObject);
123
-
124
- observer.observe(this);
125
- renderer.setAnimationLoop(() => renderer.render(scene, camera));
126
- this.#renderer = renderer;
127
- this.#scene = scene;
128
- this.#threeControls = controls;
129
- this.#observer = observer;
130
- }
131
-
132
- override _enable() {
133
- if (this.disabled) return;
134
-
135
- if (!this._threeObject && this.uuid) {
136
- this._threeObject = new ThreeStream(this.uuid, this.key);
137
- this._threeObject.name = this.uuid;
138
- }
139
-
140
- super._enable();
141
-
142
- if (this.#scene && this._threeObject) this.#scene.add(this._threeObject);
143
- if (!this._parent && this.uuid) this.#enableScene();
144
-
145
- if (this.controls) this.#control();
146
- }
147
-
148
- #disableScene() {
149
- if (this.#renderer) {
150
- this.#renderer?.dispose();
151
- this.#renderer = null;
152
- }
153
-
154
- if (this.#observer) {
155
- this.#observer?.disconnect();
156
- this.#observer = null;
157
- }
158
- }
159
-
160
- override _disable() {
161
- // // Cannot implement until Aqua allows multiple scenes per page
162
- // if (!this._parent) this.#disableScene();
163
-
164
- if (this._threeObject) this.#scene?.remove(this._threeObject);
165
-
166
- if (this.controls) this.#uncontrol();
167
-
168
- super._disable();
169
- }
170
-
171
- #control() {
172
- if (!this.controls || !this._threeObject) return;
173
-
174
- const scene = this.closest("miris-scene") as Scene | null;
175
-
176
- if (scene) Bus.publish(scene, "control", this._threeObject);
177
- else this.#threeControls?.objects.add(this._threeObject);
178
- }
179
-
180
- #uncontrol() {
181
- if (this.controls || !this._threeObject) return;
182
-
183
- const scene = this.closest("miris-scene") as Scene | null;
184
-
185
- if (scene) Bus.publish(scene, "uncontrol", this._threeObject);
186
- else this.#threeControls?.objects.delete(this._threeObject);
187
- }
188
-
189
- attributeChangedCallback(
190
- name: string,
191
- _oldValue: string | null,
192
- newValue: string | null
193
- ) {
194
- switch (name) {
195
- case "controls":
196
- this.controls = newValue !== null;
197
- break;
198
- case "disabled":
199
- this.disabled = newValue !== null;
200
- break;
201
- case "key":
202
- this.#key = newValue;
203
- break;
204
- case "uuid":
205
- this.uuid = newValue;
206
- break;
207
- }
208
- }
209
-
210
- update() {
211
- if (this._threeObject) this._threeObject?.update();
212
- }
213
-
214
- toggleRenderBounds(shouldRender: boolean) {
215
- if (this._threeObject) this._threeObject?.toggleRenderBounds(shouldRender);
216
- }
217
- }
@@ -1,386 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>hero</title>
7
- <link rel="preconnect" href="https://fonts.googleapis.com" />
8
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
- <link
10
- href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,200..800;1,200..800&display=swap"
11
- rel="stylesheet"
12
- />
13
- <style>
14
- .hero-pagination-button {
15
- text-transform: uppercase;
16
- color: #9e9e9e;
17
- cursor: pointer;
18
- background-color: transparent;
19
- border-style: none;
20
- padding: 12px 16px;
21
- width: var(--button-width);
22
-
23
- &:hover {
24
- color: #fff;
25
- }
26
- }
27
-
28
- .hero-nav {
29
- background-color: transparent;
30
- position: absolute;
31
- top: 0;
32
- height: 100%;
33
- display: flex;
34
- align-items: center;
35
- fill: #fff;
36
- padding: 16px;
37
- border-style: none;
38
- cursor: pointer;
39
-
40
- &:hover {
41
- fill: #d7d8d2;
42
- }
43
-
44
- & svg {
45
- width: 40px;
46
- height: 40px;
47
- }
48
- }
49
-
50
- .hero-nav-prev {
51
- left: 0;
52
- }
53
-
54
- .hero-nav-next {
55
- right: 0;
56
- }
57
-
58
- .hero-display {
59
- --hero-display-margin: 20%;
60
-
61
- position: relative;
62
- place-items: center;
63
- height: 100%;
64
- }
65
-
66
- .hero-pagination-item {
67
- scroll-snap-align: center;
68
- display: block;
69
- }
70
-
71
- .hero-pagination-list {
72
- --button-width: 192px;
73
-
74
- position: absolute;
75
- inset: 0;
76
- bottom: auto;
77
- box-sizing: border-box;
78
- display: flex;
79
- justify-content: flex-start;
80
- align-items: flex-end;
81
- grid-column-start: 7;
82
- grid-column-end: 12;
83
- list-style-type: none;
84
- padding-inline-start: 0;
85
- margin-block: 0;
86
- width: 100%;
87
- scroll-snap-type: inline mandatory;
88
- overflow-x: auto;
89
- padding-inline: calc(50% - (var(--button-width) / 2));
90
- scrollbar-width: none;
91
- }
92
-
93
- .hero-mask {
94
- mask-image: linear-gradient(
95
- to right,
96
- transparent,
97
- black var(--hero-display-margin),
98
- black calc(100% - var(--hero-display-margin)),
99
- transparent
100
- );
101
- }
102
-
103
- .hero-scene {
104
- height: 100%;
105
- }
106
-
107
- body {
108
- margin: 0;
109
- background-color: black;
110
- font-family:
111
- Plus Jakarta Sans,
112
- sans-serif;
113
- }
114
-
115
- .hero {
116
- position: relative;
117
- height: 100%;
118
- }
119
-
120
- .hero-container {
121
- position: relative;
122
- display: grid;
123
- max-width: 1536px;
124
- margin-inline: auto;
125
- padding-inline: 64px;
126
- padding-block: 4rem;
127
- align-items: center;
128
-
129
- @media (min-width: 1280px) {
130
- grid-template-columns: repeat(12, minmax(0, 1fr));
131
- }
132
- }
133
-
134
- .hero-wrapper {
135
- height: 100%;
136
- @media (min-width: 1280px) {
137
- grid-column-start: 6;
138
- grid-column-end: 13;
139
- }
140
- }
141
-
142
- .hero-description {
143
- width: 100%;
144
- color: #9e9e9e;
145
- margin-block: 0;
146
- }
147
-
148
- .hero-heading {
149
- color: #fff;
150
- font-size: 72px;
151
- line-height: 94%;
152
- margin-block: 0;
153
- }
154
-
155
- .hero-links-anchor {
156
- display: block;
157
- text-transform: uppercase;
158
- letter-spacing: 2px;
159
- border-radius: 25px;
160
- text-align: center;
161
- padding: 16px 24px;
162
- text-decoration: none;
163
- font-size: 10px;
164
- border-width: 1px;
165
- border-style: solid;
166
- }
167
-
168
- .hero-links-anchor-primary {
169
- color: #151515;
170
- background-color: #fff;
171
- border-color: transparent;
172
- font-weight: 800;
173
-
174
- &:hover {
175
- background-color: #d7d8d2;
176
- }
177
- }
178
-
179
- .hero-links-anchor-secondary {
180
- border-color: #c2c3be;
181
- color: #fff;
182
- font-weight: 500;
183
-
184
- &:hover {
185
- background-color: #151515;
186
- }
187
- }
188
-
189
- .hero-links-list {
190
- display: flex;
191
- column-gap: 1rem;
192
- list-style-type: none;
193
- padding-inline-start: 0;
194
- margin-block: 0;
195
- }
196
-
197
- .hero-text {
198
- display: grid;
199
- row-gap: 24px;
200
-
201
- @media (min-width: 1280px) {
202
- grid-column-start: 1;
203
- grid-column-end: 6;
204
- grid-row-start: 1;
205
- grid-row-end: 3;
206
- }
207
- }
208
-
209
- .hero-video {
210
- position: absolute;
211
- width: 100%;
212
- height: 100%;
213
- object-fit: cover;
214
- }
215
-
216
- .container {
217
- height: 800px;
218
- max-width: 1536px;
219
- padding-block: 64px;
220
- margin-inline: auto;
221
- }
222
- </style>
223
- <script type="module" src="/index.ts"></script>
224
- <script type="module" lang="ts">
225
- const slots = 7;
226
-
227
- await customElements.whenDefined("miris-stream");
228
- for (const hero of document.getElementsByClassName("hero")) {
229
- const scene = hero.querySelector(".hero-scene");
230
- const group = hero.querySelector(".hero-group");
231
- const list = hero.querySelector(".hero-pagination-list");
232
- const assets = await scene.fetchAssets(["miris.com"]);
233
- const count = assets.length;
234
- const streams = [];
235
- const paginationButtons = [];
236
- const radius = 1 / (2 * Math.sin(Math.PI / slots));
237
-
238
- assets.forEach(({ uuid, name, pY = 0, zoom = 1 }, index) => {
239
- // calculate positions
240
- const angle = (index % slots) * ((2 * Math.PI) / slots);
241
- const pX = radius * Math.sin(angle);
242
- const pZ = radius * Math.cos(angle);
243
-
244
- const wrapper = document.createElement("miris-group");
245
- wrapper.position.set(pX, pY, pZ);
246
- group.append(wrapper);
247
-
248
- // create stream
249
- const stream = document.createElement("miris-stream");
250
- stream.disabled = true;
251
- stream.controls = true;
252
- stream.uuid = uuid;
253
- stream.zoom = zoom * 0.25;
254
- streams.push(stream);
255
- wrapper.append(stream);
256
-
257
- // create list item
258
- const item = document.createElement("li");
259
- item.className = "hero-pagination-item";
260
- list.append(item);
261
-
262
- // create button
263
- const button = document.createElement("button");
264
- button.className = "hero-pagination-button";
265
- button.innerText = name.replaceAll(/_+/g, " ");
266
- button.addEventListener("click", () =>
267
- button.scrollIntoView({ inline: "center", behavior: "smooth" })
268
- );
269
- paginationButtons.push(button);
270
- item.append(button);
271
- });
272
-
273
- let waiting = false;
274
- const scroll = () => {
275
- // take measurements
276
- const { clientWidth: cwL, scrollLeft: slL, scrollWidth: swL } = list;
277
- const cwB = swL / count;
278
-
279
- // calculate rotation
280
- const rMaxProgress = 1 - 1 / count;
281
- const rProgress = rMaxProgress * (slL / (swL - cwL));
282
- const rRadians = rProgress * 2 * Math.PI;
283
- const rY = rRadians * (count / slots);
284
-
285
- // determine active indices
286
- const iProgress = (slL + cwB / 2) / (swL - cwL + cwB);
287
- const iCurr = Math.floor(iProgress / (1 / count));
288
- const iPrev = iCurr - 1;
289
- const iNext = iCurr + 1;
290
-
291
- // update scene
292
- streams.forEach((stream, index) => {
293
- stream.disabled = ![iPrev, iCurr, iNext].includes(index);
294
- stream.parentElement.rotation.y = rY;
295
- });
296
- group.rotation.y = -rY;
297
-
298
- // update buttons
299
- paginationButtons.forEach((button, index) => {
300
- const className = "hero-pagination-button-active";
301
- const { classList } = button;
302
- if (index === iCurr) classList.add(className);
303
- else classList.remove(className);
304
- });
305
-
306
- // release hold
307
- waiting = false;
308
- };
309
-
310
- list.addEventListener("scroll", () => {
311
- if (!waiting) waiting = Boolean(requestAnimationFrame(scroll));
312
- });
313
- scroll();
314
-
315
- hero.querySelector(".hero-nav-prev")?.addEventListener("click", () => {
316
- const padding = parseFloat(getComputedStyle(list).paddingLeft);
317
- const prevButton = hero
318
- .querySelector(".hero-pagination-button-active")
319
- ?.parentElement?.previousElementSibling?.querySelector(
320
- ".hero-pagination-button"
321
- );
322
-
323
- if (prevButton) {
324
- list.scroll({
325
- left: prevButton.offsetLeft - padding,
326
- behavior: "smooth",
327
- });
328
- }
329
- });
330
-
331
- hero.querySelector(".hero-nav-next")?.addEventListener("click", () => {
332
- const padding = parseFloat(getComputedStyle(list).paddingLeft);
333
- const nextButton = hero
334
- .querySelector(".hero-pagination-button-active")
335
- ?.parentElement?.nextElementSibling?.querySelector(
336
- ".hero-pagination-button"
337
- );
338
-
339
- if (nextButton) {
340
- list.scroll({
341
- left: nextButton.offsetLeft - padding,
342
- behavior: "smooth",
343
- });
344
- }
345
- });
346
- }
347
- </script>
348
- </head>
349
- <body>
350
- <div class="container">
351
- <div class="hero">
352
- <div class="hero-wrapper">
353
- <div class="hero-display">
354
- <div class="hero-mask">
355
- <miris-scene
356
- key="l2LWVHS39ZhmXDOtefPoOmvPdAp55OxTPJfwVmNo7rY"
357
- class="hero-scene"
358
- >
359
- <miris-group position="0 0 -3" class="hero-group"></miris-group>
360
- </miris-scene>
361
- <ul class="hero-pagination-list"></ul>
362
- </div>
363
- <button class="hero-nav hero-nav-prev">
364
- <svg viewBox="0 0 24 24">
365
- <path
366
- fill-rule="evenodd"
367
- d="M7.72 12.53a.75.75 0 0 1 0-1.06l7.5-7.5a.75.75 0 1 1 1.06 1.06L9.31 12l6.97 6.97a.75.75 0 1 1-1.06 1.06l-7.5-7.5Z"
368
- clip-rule="evenodd"
369
- />
370
- </svg>
371
- </button>
372
- <button class="hero-nav hero-nav-next">
373
- <svg viewBox="0 0 24 24">
374
- <path
375
- fill-rule="evenodd"
376
- d="M16.28 11.47a.75.75 0 0 1 0 1.06l-7.5 7.5a.75.75 0 0 1-1.06-1.06L14.69 12 7.72 5.03a.75.75 0 0 1 1.06-1.06l7.5 7.5Z"
377
- clip-rule="evenodd"
378
- />
379
- </svg>
380
- </button>
381
- </div>
382
- </div>
383
- </div>
384
- </div>
385
- </body>
386
- </html>