@qispace/vue3-player 0.0.5

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 (160) hide show
  1. package/README.md +18 -0
  2. package/dist/components/apartmentChooser/QiApartmentChooser.vue.d.ts +4 -0
  3. package/dist/components/apartmentChooser/QiApartmentChooser.vue.d.ts.map +1 -0
  4. package/dist/components/apartmentChooser/QiApartmentChooserNavigation.vue.d.ts +4 -0
  5. package/dist/components/apartmentChooser/QiApartmentChooserNavigation.vue.d.ts.map +1 -0
  6. package/dist/components/apartmentChooser/QiApartmentChooserRaster.vue.d.ts +4 -0
  7. package/dist/components/apartmentChooser/QiApartmentChooserRaster.vue.d.ts.map +1 -0
  8. package/dist/components/apartmentChooser/QiApartmentChooserRotator.vue.d.ts +4 -0
  9. package/dist/components/apartmentChooser/QiApartmentChooserRotator.vue.d.ts.map +1 -0
  10. package/dist/components/apartmentChooser/QiApartmentChooserSvg.vue.d.ts +4 -0
  11. package/dist/components/apartmentChooser/QiApartmentChooserSvg.vue.d.ts.map +1 -0
  12. package/dist/components/buildingPicker/QiBuildingChooser.vue.d.ts +3 -0
  13. package/dist/components/buildingPicker/QiBuildingChooser.vue.d.ts.map +1 -0
  14. package/dist/components/buildingPicker/QiBuildingChooserRaster.vue.d.ts +4 -0
  15. package/dist/components/buildingPicker/QiBuildingChooserRaster.vue.d.ts.map +1 -0
  16. package/dist/components/buildingPicker/QiBuildingChooserRasterOverlay.vue.d.ts +4 -0
  17. package/dist/components/buildingPicker/QiBuildingChooserRasterOverlay.vue.d.ts.map +1 -0
  18. package/dist/components/buildingPicker/QiBuildingChooserSceneImageStack.vue.d.ts +4 -0
  19. package/dist/components/buildingPicker/QiBuildingChooserSceneImageStack.vue.d.ts.map +1 -0
  20. package/dist/components/buildingPicker/QiBuildingChooserSunSimControl.vue.d.ts +5 -0
  21. package/dist/components/buildingPicker/QiBuildingChooserSunSimControl.vue.d.ts.map +1 -0
  22. package/dist/components/buildingPicker/QiBuildingChooserSvg.vue.d.ts +4 -0
  23. package/dist/components/buildingPicker/QiBuildingChooserSvg.vue.d.ts.map +1 -0
  24. package/dist/components/index.d.ts +4 -0
  25. package/dist/components/shared/QiFloatingCard.vue.d.ts +4 -0
  26. package/dist/components/shared/QiFloatingCard.vue.d.ts.map +1 -0
  27. package/dist/components/shared/QiHoverProbe.vue.d.ts +4 -0
  28. package/dist/components/shared/QiHoverProbe.vue.d.ts.map +1 -0
  29. package/dist/components/shared/QiNorthDirection.vue.d.ts +3 -0
  30. package/dist/components/shared/QiNorthDirection.vue.d.ts.map +1 -0
  31. package/dist/components/shared/QiZoomBox.vue.d.ts +4 -0
  32. package/dist/components/shared/QiZoomBox.vue.d.ts.map +1 -0
  33. package/dist/components/virtualTourV2/Disclaimer.vue.d.ts +4 -0
  34. package/dist/components/virtualTourV2/Disclaimer.vue.d.ts.map +1 -0
  35. package/dist/components/virtualTourV2/FullScreenButton.vue.d.ts +3 -0
  36. package/dist/components/virtualTourV2/FullScreenButton.vue.d.ts.map +1 -0
  37. package/dist/components/virtualTourV2/ManualSlideShow.vue.d.ts +4 -0
  38. package/dist/components/virtualTourV2/ManualSlideShow.vue.d.ts.map +1 -0
  39. package/dist/components/virtualTourV2/ManualSlideShowPlayer.vue.d.ts +4 -0
  40. package/dist/components/virtualTourV2/ManualSlideShowPlayer.vue.d.ts.map +1 -0
  41. package/dist/components/virtualTourV2/SingleImage.vue.d.ts +4 -0
  42. package/dist/components/virtualTourV2/SingleImage.vue.d.ts.map +1 -0
  43. package/dist/components/virtualTourV2/SocialShare.vue.d.ts +4 -0
  44. package/dist/components/virtualTourV2/SocialShare.vue.d.ts.map +1 -0
  45. package/dist/components/virtualTourV2/WaypointCarousel.vue.d.ts +4 -0
  46. package/dist/components/virtualTourV2/WaypointCarousel.vue.d.ts.map +1 -0
  47. package/dist/lib/virtualTour__/ApartmentConfig.d.ts +27 -0
  48. package/dist/lib/virtualTour__/ApartmentFiles.d.ts +7 -0
  49. package/dist/lib/virtualTour__/MinimapConfig.d.ts +23 -0
  50. package/dist/lib/virtualTour__/impl/HttpApartmentFiles.d.ts +19 -0
  51. package/dist/vue3-player.cjs.js +2 -0
  52. package/dist/vue3-player.esm.js +2992 -0
  53. package/dist/vue3-player.iife.js +3 -0
  54. package/dist/vue3-player.umd.js +3 -0
  55. package/package.json +65 -0
  56. package/src/components/apartmentChooser/QiApartmentChooser.vue +319 -0
  57. package/src/components/apartmentChooser/QiApartmentChooserNavigation.vue +88 -0
  58. package/src/components/apartmentChooser/QiApartmentChooserRaster.vue +408 -0
  59. package/src/components/apartmentChooser/QiApartmentChooserRotator.vue +202 -0
  60. package/src/components/apartmentChooser/QiApartmentChooserSvg.vue +257 -0
  61. package/src/components/apartmentChooser/index.js +5 -0
  62. package/src/components/buildingPicker/QiBuildingChooser.vue +61 -0
  63. package/src/components/buildingPicker/QiBuildingChooserRaster.vue +312 -0
  64. package/src/components/buildingPicker/QiBuildingChooserRasterOverlay.vue +96 -0
  65. package/src/components/buildingPicker/QiBuildingChooserSceneImageStack.vue +89 -0
  66. package/src/components/buildingPicker/QiBuildingChooserSunSimControl.vue +257 -0
  67. package/src/components/buildingPicker/QiBuildingChooserSvg.vue +414 -0
  68. package/src/components/buildingPicker/index.js +6 -0
  69. package/src/components/index.ts +4 -0
  70. package/src/components/shared/QiFloatingCard.vue +76 -0
  71. package/src/components/shared/QiHoverProbe.vue +22 -0
  72. package/src/components/shared/QiNorthDirection.vue +27 -0
  73. package/src/components/shared/QiZoomBox.vue +322 -0
  74. package/src/components/shared/index.js +4 -0
  75. package/src/components/virtualTourV2/Compass.vue +37 -0
  76. package/src/components/virtualTourV2/Disclaimer.vue +41 -0
  77. package/src/components/virtualTourV2/FloorLevel.vue +73 -0
  78. package/src/components/virtualTourV2/FullScreenButton.vue +23 -0
  79. package/src/components/virtualTourV2/InteriorSelector.vue +90 -0
  80. package/src/components/virtualTourV2/ManualSlideShow.vue +198 -0
  81. package/src/components/virtualTourV2/ManualSlideShowPlayer.vue +159 -0
  82. package/src/components/virtualTourV2/PlayerV2.vue +300 -0
  83. package/src/components/virtualTourV2/ScrollHelper.vue +74 -0
  84. package/src/components/virtualTourV2/SettingsUI.vue +355 -0
  85. package/src/components/virtualTourV2/SingleImage.vue +36 -0
  86. package/src/components/virtualTourV2/SocialShare.vue +163 -0
  87. package/src/components/virtualTourV2/TimeOfDay.vue +50 -0
  88. package/src/components/virtualTourV2/Tutorial.vue +690 -0
  89. package/src/components/virtualTourV2/ViewModeToggle.vue +24 -0
  90. package/src/components/virtualTourV2/VirtualTourV2.vue +876 -0
  91. package/src/components/virtualTourV2/WaypointCarousel.vue +162 -0
  92. package/src/components/virtualTourV2/index.js +1 -0
  93. package/src/components/virtualTourV2/minimapv2/MiniMapMap.vue +262 -0
  94. package/src/components/virtualTourV2/minimapv2/MiniMapV2.vue +110 -0
  95. package/src/components/virtualTourV2/minimapv2/MinimapCompass.vue +39 -0
  96. package/src/components/virtualTourV2/minimapv2/Moveable.vue +208 -0
  97. package/src/components/virtualTourV2/minimapv2/RotationMarkerV2.vue +79 -0
  98. package/src/components/virtualTourV2/minimapv2/SunsimulationSlider.vue +203 -0
  99. package/src/components/virtualTourV2/minimapv2/index.js +1 -0
  100. package/src/entry.esm.js +17 -0
  101. package/src/entry.js +13 -0
  102. package/src/entry.ts_ +17 -0
  103. package/src/lib/apartmentChooser/BuildingViewerModel.js +60 -0
  104. package/src/lib/apartmentChooser/CircularSlideshow.js +66 -0
  105. package/src/lib/apartmentChooser/RotationStep.js +31 -0
  106. package/src/lib/apartmentChooser/SceneRotator.js +25 -0
  107. package/src/lib/apartmentChooser/index.js +3 -0
  108. package/src/lib/apartmentChooser/throttle.js +15 -0
  109. package/src/lib/buildingPicker/BuildingMap.js +24 -0
  110. package/src/lib/buildingPicker/BuildingPickerResourceProvider.js +97 -0
  111. package/src/lib/buildingPicker/CanvasRaster.js +29 -0
  112. package/src/lib/buildingPicker/DayOfYearSelector.js +36 -0
  113. package/src/lib/buildingPicker/SampleRaster.js +14 -0
  114. package/src/lib/buildingPicker/index.js +5 -0
  115. package/src/lib/index.js +4 -0
  116. package/src/lib/shared/BatchLoadTracker.js +52 -0
  117. package/src/lib/shared/I18N.js +65 -0
  118. package/src/lib/shared/ResourceLoader.js +33 -0
  119. package/src/lib/shared/index.js +3 -0
  120. package/src/lib/virtualTour/CameraSnapshot.js +42 -0
  121. package/src/lib/virtualTour/FullscreenModel.js +69 -0
  122. package/src/lib/virtualTour/textures/arrow.png +0 -0
  123. package/src/lib/virtualTour/textures/compass-bg.png +0 -0
  124. package/src/lib/virtualTour/textures/compass-needle.png +0 -0
  125. package/src/lib/virtualTour/textures/compass-north.png +0 -0
  126. package/src/lib/virtualTour/textures/floor-1.svg +4 -0
  127. package/src/lib/virtualTour/textures/floor-2.svg +4 -0
  128. package/src/lib/virtualTour/textures/marker.png +0 -0
  129. package/src/lib/virtualTour/textures/tod-sun.png +0 -0
  130. package/src/lib/virtualTour__/ApartmentConfig.ts +80 -0
  131. package/src/lib/virtualTour__/ApartmentFiles.ts +8 -0
  132. package/src/lib/virtualTour__/CameraNavigator.js_ +74 -0
  133. package/src/lib/virtualTour__/CameraSnapshot.js +42 -0
  134. package/src/lib/virtualTour__/CoordConversions.js +43 -0
  135. package/src/lib/virtualTour__/FullscreenModel.js +69 -0
  136. package/src/lib/virtualTour__/MinimapConfig.ts +46 -0
  137. package/src/lib/virtualTour__/PlayerViewModel.js +423 -0
  138. package/src/lib/virtualTour__/config/ApartmentConfig.js +92 -0
  139. package/src/lib/virtualTour__/config/CameraConfig.js +97 -0
  140. package/src/lib/virtualTour__/config/Interaction.js +393 -0
  141. package/src/lib/virtualTour__/config/Panorama.js +78 -0
  142. package/src/lib/virtualTour__/config/PlayerConfig.js +812 -0
  143. package/src/lib/virtualTour__/config/rawinflate.export.js +833 -0
  144. package/src/lib/virtualTour__/config/shaders.js +24 -0
  145. package/src/lib/virtualTour__/impl/HttpApartmentFiles.ts +57 -0
  146. package/src/lib/virtualTour__/index.js +1 -0
  147. package/src/lib/virtualTour__/textures/arrow.png +0 -0
  148. package/src/lib/virtualTour__/textures/compass-bg.png +0 -0
  149. package/src/lib/virtualTour__/textures/compass-needle.png +0 -0
  150. package/src/lib/virtualTour__/textures/compass-north.png +0 -0
  151. package/src/lib/virtualTour__/textures/floor-1.svg +4 -0
  152. package/src/lib/virtualTour__/textures/floor-2.svg +4 -0
  153. package/src/lib/virtualTour__/textures/marker.png +0 -0
  154. package/src/lib/virtualTour__/textures/tod-sun.png +0 -0
  155. package/src/main.ts_ +24 -0
  156. package/src/shims-png.d.ts +4 -0
  157. package/src/shims-tsx.d.ts +11 -0
  158. package/src/shims-vue.d.ts +4 -0
  159. package/src/style.css +0 -0
  160. package/src/vite-env.d.ts +5 -0
@@ -0,0 +1,257 @@
1
+ <template>
2
+ <div id="container" ref="container">
3
+ <div
4
+ v-show="!viewModel.isRotating"
5
+ class="svg-container"
6
+ :style="{
7
+ opacity: !currentSvgImage || !currentSvgImage.svgObjects ? 0 : 1,
8
+ }"
9
+ >
10
+ <svg
11
+ v-if="currentSvgImage && currentSvgImage.svgObjects"
12
+ :viewBox="currentSvgImage.viewBox"
13
+ >
14
+ <g
15
+ v-for="{ index, svg } of currentSvgImage.svgObjects"
16
+ :key="index"
17
+ v-html="svg"
18
+ :style="getGClass(index)"
19
+ @mouseenter="mouseEnter(index)"
20
+ @mouseleave="mouseLeave(index)"
21
+ @click="click(index)"
22
+ />
23
+ </svg>
24
+ </div>
25
+ <qi-hover-probe ref="hoverProbe" />
26
+ </div>
27
+ </template>
28
+
29
+ <script>
30
+ import { ResourceLoader } from "@/lib/shared/ResourceLoader";
31
+ import QiHoverProbe from "@/components/shared/QiHoverProbe.vue";
32
+ import throttle from "@/lib/apartmentChooser/throttle";
33
+
34
+ export default {
35
+ name: "QiApartmentChooserSvg",
36
+ components: { QiHoverProbe },
37
+ mixins: [throttle],
38
+ props: {
39
+ cdnFileResolver: { type: Function, required: true },
40
+ viewModel: { type: Object, required: true },
41
+ selectedUnitId: { type: String, default: null },
42
+ unitCallback: { type: Function, default: (aptId) => aptId },
43
+ canGoToUnitCallback: { type: Function, default: () => true },
44
+ sceneData: { type: Array, default: [] },
45
+ colorCallback: {
46
+ type: Function,
47
+ default: () => ({ r: 108, g: 0, b: 108, a: 144, s: 2 }),
48
+ },
49
+ drawObserver: {
50
+ type: [Object, Array, String, Number, Boolean],
51
+ default: null,
52
+ },
53
+ },
54
+ emits: ["select-unit", "go-to-unit", "hover-over", "hover-out"],
55
+ data() {
56
+ return {
57
+ pos: { x: 0, y: 0, width: 0, height: 0 },
58
+ hoverApartmentIndex: -1,
59
+ svgImages: {},
60
+ currentSvgImage: null,
61
+ resourceLoader: new ResourceLoader(),
62
+ };
63
+ },
64
+ computed: {
65
+ isHoverable() {
66
+ return this.$refs.hoverProbe.isHoverEnabled;
67
+ },
68
+ },
69
+ watch: {
70
+ "viewModel.currentScene": {
71
+ handler() {
72
+ if (this.viewModel.currentScene in this.svgImages) {
73
+ this.currentSvgImage = this.svgImages[this.viewModel.currentScene];
74
+ }
75
+ },
76
+ immediate: true,
77
+ },
78
+ svgImages: {
79
+ handler() {
80
+ if (this.viewModel.currentScene in this.svgImages) {
81
+ this.currentSvgImage = this.svgImages[this.viewModel.currentScene];
82
+ }
83
+ },
84
+ immediate: true,
85
+ },
86
+ pos() {
87
+ this.$emit("pos", this.pos);
88
+ },
89
+ hoverApartmentIndex(current, prev) {
90
+ if (current >= 0) {
91
+ const hoverApartment = this.getApartmentByIndex(current);
92
+ this.$emit("hover-over", {
93
+ hoverApartment,
94
+ closeHandler: () => {
95
+ this.hoverApartmentIndex = -1;
96
+ },
97
+ });
98
+ } else {
99
+ const hoverApartment = this.getApartmentByIndex(prev);
100
+ this.$emit("hover-out", {
101
+ hoverApartment,
102
+ closeHandler: () => {
103
+ this.hoverApartmentIndex = -1;
104
+ },
105
+ });
106
+ }
107
+ },
108
+ },
109
+ mounted() {
110
+ for (let i = 1; i <= 4; i++) {
111
+ const url = this.cdnFileResolver(`/scenedata${i}.svg`);
112
+ const svgImage = {};
113
+ this.resourceLoader.loadJson(url).then((markup) => {
114
+ svgImage.markup = markup;
115
+ const parser = new DOMParser();
116
+ const dom = parser
117
+ .parseFromString(markup, "image/svg+xml")
118
+ .getElementsByTagName("svg")[0];
119
+ svgImage.viewBox = dom.getAttribute("viewBox");
120
+ svgImage.svgObjects = Array.from(
121
+ dom.querySelectorAll("*[data-index]")
122
+ ).map((dom) => ({
123
+ index: parseInt(dom.getAttribute("data-index")),
124
+ svg: dom.innerHTML,
125
+ }));
126
+ this.svgImages = { ...this.svgImages, [i]: svgImage };
127
+ });
128
+ }
129
+ this.$refs.container.addEventListener(
130
+ "mousemove",
131
+ this.throttle(this.handleMouseMove, 50)
132
+ );
133
+ },
134
+ methods: {
135
+ handleMouseMove(e) {
136
+ this.pos = {
137
+ x: e.offsetX,
138
+ y: e.offsetY,
139
+ width: this.$el.offsetWidth,
140
+ height: this.$el.offsetHeight,
141
+ };
142
+ },
143
+ getApartmentIdByIndex(index) {
144
+ return this.sceneData.find((x) => x.index === parseInt(index))
145
+ ?.apartmentId;
146
+ },
147
+ getApartmentByIndex(index) {
148
+ const apartmentId = this.getApartmentIdByIndex(index);
149
+ return this.unitCallback ? this.unitCallback(apartmentId) : apartmentId;
150
+ },
151
+ mouseEnter(index) {
152
+ if (this.isHoverable) {
153
+ this.hoverApartmentIndex = index;
154
+ }
155
+ },
156
+ mouseLeave() {
157
+ if (this.isHoverable) {
158
+ this.hoverApartmentIndex = -1;
159
+ }
160
+ },
161
+ click(index) {
162
+ const clickApartment = this.getApartmentByIndex(index);
163
+ this.$emit("select-unit", clickApartment);
164
+ if (
165
+ this.isHoverable &&
166
+ clickApartment &&
167
+ this.canGoToUnitCallback(clickApartment)
168
+ ) {
169
+ this.$emit("go-to-unit", clickApartment);
170
+ } else {
171
+ this.hoverApartmentIndex = index;
172
+ }
173
+ },
174
+ getGClass(index) {
175
+ const apartmentId = this.getApartmentIdByIndex(index);
176
+ const apartment = this.getApartmentByIndex(index);
177
+ let type = "idle";
178
+ if (
179
+ this.hoverApartmentIndex < 0 &&
180
+ this.selectedUnitId &&
181
+ apartmentId === this.selectedUnitId
182
+ ) {
183
+ type = "selected";
184
+ } else if (
185
+ this.hoverApartmentIndex >= 0 &&
186
+ this.hoverApartmentIndex === index
187
+ ) {
188
+ type = "hover";
189
+ }
190
+ const color = this.colorCallback(apartment, type);
191
+ if (color) {
192
+ const { r, g, b, a, s } = color;
193
+ return {
194
+ strokeWidth: s !== undefined ? s : 2,
195
+ stroke: `rgb(${r}, ${g}, ${b})`,
196
+ strokeOpacity: 1,
197
+ fill: `rgb(${r}, ${g}, ${b})`,
198
+ fillOpacity: a / 255,
199
+ cursor: `${
200
+ this.canGoToUnitCallback(apartment) ? "cursor" : "default"
201
+ } !important`,
202
+ };
203
+ } else {
204
+ return {
205
+ strokeWidth: 0,
206
+ stroke: "white",
207
+ strokeOpacity: 0,
208
+ fill: "white",
209
+ fillOpacity: 0,
210
+ cursor: "default !important",
211
+ };
212
+ }
213
+ },
214
+ },
215
+ };
216
+ </script>
217
+
218
+ <style scoped>
219
+ #container {
220
+ position: absolute;
221
+ top: 0;
222
+ width: 100%;
223
+ user-select: none;
224
+ touch-action: none;
225
+ padding-top: 56.25%;
226
+ }
227
+ .svg-container {
228
+ transition-property: opacity;
229
+ transition-duration: 250ms;
230
+ transition-timing-function: ease-in-out;
231
+ }
232
+ .svg-container,
233
+ svg {
234
+ position: absolute;
235
+ left: 0;
236
+ top: 0;
237
+ width: 100%;
238
+ height: 100%;
239
+ z-index: 10000;
240
+ }
241
+ svg g :deep(path) {
242
+ cursor: pointer;
243
+ stroke-width: inherit !important;
244
+ stroke-opacity: inherit !important;
245
+ stroke: inherit !important;
246
+ fill: inherit !important;
247
+ fill-opacity: inherit !important;
248
+ transition-property: fill-opacity, fill;
249
+ transition-duration: 250ms;
250
+ transition-timing-function: ease-in-out;
251
+ }
252
+ @media only screen and (min-width: 1280px) {
253
+ #canvas {
254
+ border-radius: 10px;
255
+ }
256
+ }
257
+ </style>
@@ -0,0 +1,5 @@
1
+ export { default as QiApartmentChooser } from './QiApartmentChooser.vue'
2
+ export { default as QiApartmentChooserNavigation } from './QiApartmentChooserNavigation.vue'
3
+ export { default as QiApartmentChooserSvg } from './QiApartmentChooserSvg.vue'
4
+ export { default as QiApartmentChooserRaster } from './QiApartmentChooserRaster.vue'
5
+ export { default as QiApartmentChooserRotator } from './QiApartmentChooserRotator.vue'
@@ -0,0 +1,61 @@
1
+ <template>
2
+ <component
3
+ :is="component"
4
+ :show-cards="showCards"
5
+ :color-callback="colorCallback"
6
+ :building-callback="buildingCallback"
7
+ :cdn-file-resolver="cdnFileResolver"
8
+ :i18n="i18n"
9
+ :interactive-desktop="interactiveDesktop"
10
+ :interactive-mobile="interactiveMobile"
11
+ :building-chooser-time-of-day="buildingChooserTimeOfDay"
12
+ :can-go-to-building-callback="canGoToBuildingCallback"
13
+ :custom-overlay-content="customOverlayContent"
14
+ @go-to-building="$emit('go-to-building', $event)"
15
+ @hover-over-building="$emit('hover-over-building', $event)"
16
+ @hover-out-building="$emit('hover-out-building', $event)"
17
+ >
18
+ <template
19
+ v-for="slot in Object.keys($slots)"
20
+ :key="slot"
21
+ v-slot:[slot]="scope"
22
+ >
23
+ <slot :name="slot" v-bind="scope" />
24
+ </template>
25
+ </component>
26
+ </template>
27
+
28
+ <script>
29
+ import QiBuildingChooserSvg from "./QiBuildingChooserSvg.vue";
30
+ import QiBuildingChooserRaster from "./QiBuildingChooserRaster.vue";
31
+
32
+ export default {
33
+ name: "QiBuildingChooser",
34
+ components: {
35
+ QiBuildingChooserSvg,
36
+ QiBuildingChooserRaster,
37
+ },
38
+ emits: ["go-to-building", "hover-over-building", "hover-out-building"],
39
+ props: {
40
+ showCards: { type: Boolean, default: () => true },
41
+ colorCallback: {
42
+ type: Function,
43
+ default: () => ({ r: 108, g: 0, b: 108, a: 144, s: 2 }),
44
+ },
45
+ buildingCallback: { type: Function, default: (buildingId) => buildingId },
46
+ cdnFileResolver: { type: Function, required: true },
47
+ i18n: { type: Object, default: undefined },
48
+ interactiveDesktop: { type: Boolean, default: true },
49
+ interactiveMobile: { type: Boolean, default: true },
50
+ useSvg: { type: Boolean, default: false },
51
+ buildingChooserTimeOfDay: { type: String, default: null },
52
+ canGoToBuildingCallback: { type: Function, default: () => true },
53
+ customOverlayContent: { type: String, default: "" },
54
+ },
55
+ computed: {
56
+ component() {
57
+ return this.useSvg ? QiBuildingChooserSvg : QiBuildingChooserRaster;
58
+ },
59
+ },
60
+ };
61
+ </script>
@@ -0,0 +1,312 @@
1
+ <template>
2
+ <div
3
+ v-if="error"
4
+ style="height: 50vh"
5
+ class="d-flex justify-center align-center blue-grey lighten-5"
6
+ >
7
+ <v-icon x-large>mdi-alert-circle-outline</v-icon>
8
+ </div>
9
+ <div
10
+ v-else-if="pickerData"
11
+ :style="`cursor: ${selectedBuildingId ? 'pointer' : 'auto'};height: ${
12
+ canvasDimensions.height
13
+ }px; position: relative`"
14
+ class="image-container"
15
+ >
16
+ <qi-zoom-box
17
+ :desktop="interactiveDesktop"
18
+ :mobile="interactiveMobile"
19
+ @update="updateView"
20
+ >
21
+ <div class="image-instances">
22
+ <qi-building-chooser-scene-image-stack
23
+ :image-urls="daysOfYear.imageUrls"
24
+ :active-index="halfHourMark"
25
+ @resize="resizeScene"
26
+ @preloaded="sceneState.preloaded = true"
27
+ @ready="sceneState.ready = true"
28
+ @reset="sceneReset"
29
+ />
30
+ <div v-show="sceneState.preloaded">
31
+ <qi-building-chooser-raster-overlay
32
+ v-for="overlay of pickerData.buildingOverlays"
33
+ ref="overlays"
34
+ :key="overlay.id"
35
+ :image-url="overlay.url"
36
+ :dimensions="canvasDimensions"
37
+ :color="getColor(overlay.id)"
38
+ />
39
+ </div>
40
+ </div>
41
+ <canvas
42
+ v-bind="canvasDimensions"
43
+ ref="canvas"
44
+ :class="{ hover: !!selectedBuildingId }"
45
+ @click="onClick"
46
+ @mousemove="onMove"
47
+ />
48
+ <div
49
+ v-show="sceneState.preloaded"
50
+ class="custom-overlay-layer"
51
+ v-html="customOverlayContent"
52
+ ></div>
53
+ </qi-zoom-box>
54
+ <div v-show="sceneState.preloaded">
55
+ <qi-north-direction
56
+ v-if="pickerData.sceneLongitude"
57
+ class="north-indicator"
58
+ :longitude="pickerData.sceneLongitude"
59
+ />
60
+ <qi-building-chooser-sun-sim-control
61
+ v-model="halfHourMark"
62
+ :date-selector="daysOfYear"
63
+ :is-loading="!sceneState.ready"
64
+ />
65
+ </div>
66
+ <template v-if="showCards && selectedBuilding">
67
+ <qi-floating-card
68
+ :is-hoverable="isHoverable"
69
+ :pos="pos"
70
+ @outside="() => (selectedBuildingId = undefined)"
71
+ >
72
+ <slot
73
+ :building="selectedBuilding"
74
+ :can-go-to-building="canGoToBuildingCallback(selectedBuilding)"
75
+ :go-to-building="
76
+ () =>
77
+ canGoToBuildingCallback(selectedBuilding) &&
78
+ $emit('go-to-building', selectedBuilding)
79
+ "
80
+ :pos="pos"
81
+ :is-hoverable="isHoverable"
82
+ :close="() => (selectedBuildingId = undefined)"
83
+ />
84
+ </qi-floating-card>
85
+ </template>
86
+ <qi-hover-probe ref="hoverProbe" />
87
+ </div>
88
+ </template>
89
+
90
+ <script>
91
+ import QiNorthDirection from "@/components/shared/QiNorthDirection.vue";
92
+ import QiBuildingChooserRasterOverlay from "./QiBuildingChooserRasterOverlay.vue";
93
+ import QiBuildingChooserSunSimControl from "./QiBuildingChooserSunSimControl.vue";
94
+ import QiBuildingChooserSceneImageStack from "./QiBuildingChooserSceneImageStack.vue";
95
+ import QiFloatingCard from "@/components/shared/QiFloatingCard.vue";
96
+ import QiHoverProbe from "@/components/shared/QiHoverProbe.vue";
97
+ import QiZoomBox from "@/components/shared/QiZoomBox.vue";
98
+ import { BuildingPickerResourceProvider } from "@/lib/buildingPicker/BuildingPickerResourceProvider.js";
99
+ import { BuildingMap } from "@/lib/buildingPicker/BuildingMap";
100
+ import { CanvasRaster } from "@/lib/buildingPicker/CanvasRaster";
101
+ import { DayOfYearSelector } from "@/lib/buildingPicker/DayOfYearSelector.js";
102
+ import { defineComponent, ref } from "vue";
103
+ import { I18N } from "@/lib/shared/I18N";
104
+
105
+ export default defineComponent({
106
+ setup() {
107
+ const daysOfYear = ref({});
108
+ },
109
+ name: "QiBuildingChooserRaster",
110
+ components: {
111
+ QiBuildingChooserRasterOverlay,
112
+ QiBuildingChooserSunSimControl,
113
+ QiBuildingChooserSceneImageStack,
114
+ QiNorthDirection,
115
+ QiFloatingCard,
116
+ QiHoverProbe,
117
+ QiZoomBox,
118
+ },
119
+ emits: ["go-to-building", "hover-over-building", "hover-out-building"],
120
+ provide() {
121
+ const localizer = this.i18n
122
+ ? new I18N(this.i18n.labels, this.i18n.language)
123
+ : new I18N();
124
+ return {
125
+ i18n: localizer,
126
+ };
127
+ },
128
+ props: {
129
+ showCards: { type: Boolean, default: () => true },
130
+ colorCallback: {
131
+ type: Function,
132
+ default: () => ({ r: 108, g: 0, b: 108, a: 144, s: 2 }),
133
+ },
134
+ buildingCallback: { type: Function, default: (buildingId) => buildingId },
135
+ canGoToBuildingCallback: { type: Function, default: () => true },
136
+ buildingChooserTimeOfDay: { type: String, default: null },
137
+ cdnFileResolver: { type: Function, required: true },
138
+ i18n: { type: Object, default: undefined },
139
+ interactiveDesktop: { type: Boolean, default: true },
140
+ interactiveMobile: { type: Boolean, default: true },
141
+ customOverlayContent: { type: String, default: "" },
142
+ },
143
+ data() {
144
+ return {
145
+ sceneState: { preloaded: false, ready: false },
146
+ canvasDimensions: { width: 0, height: 0 },
147
+ pos: { x: 0, y: 0, width: 0, height: 0 },
148
+ pickerData: undefined,
149
+ buildingMap: BuildingMap.empty(),
150
+ canvasRaster: CanvasRaster.empty(),
151
+ selectedBuildingId: undefined,
152
+ halfHourMark:
153
+ new Date().getHours() * 2 + (new Date().getMinutes() < 30 ? 0 : 1),
154
+ viewResolver: (x, y) => ({ x, y }),
155
+ error: false,
156
+ };
157
+ },
158
+ computed: {
159
+ isHoverable() {
160
+ return this.$refs.hoverProbe.isHoverEnabled;
161
+ },
162
+ selectedBuilding() {
163
+ return this.getBuildingData(this.selectedBuildingId);
164
+ },
165
+ },
166
+ watch: {
167
+ buildingChooserTimeOfDay: {
168
+ handler() {
169
+ if (this.buildingChooserTimeOfDay) {
170
+ const [_, hour, minutes] = /^(\d\d):(\d\d)$/.exec(
171
+ this.buildingChooserTimeOfDay
172
+ );
173
+ const ms = parseInt(minutes);
174
+ this.halfHourMark =
175
+ parseInt(hour) * 2 + (ms < 15 ? 0 : ms > 45 ? 2 : 1);
176
+ }
177
+ },
178
+ immediate: true,
179
+ },
180
+ selectedBuildingId(current, prev) {
181
+ if (current) {
182
+ this.$emit("hover-over-building", this.getBuildingData(current));
183
+ } else {
184
+ this.$emit("hover-out-building", this.getBuildingData(prev));
185
+ }
186
+ },
187
+ },
188
+ async mounted() {
189
+ const resourceProvider = new BuildingPickerResourceProvider(
190
+ this.cdnFileResolver
191
+ );
192
+ const configData = await resourceProvider.loadBuildingChooser();
193
+ if (configData) {
194
+ this.canvasRaster = new CanvasRaster(configData.mapImage);
195
+ this.buildingMap = new BuildingMap(configData.buildingIds);
196
+ this.daysOfYear = new DayOfYearSelector(configData.backgrounds);
197
+ this.pickerData = {
198
+ buildingOverlays: configData.buildingOverlays,
199
+ sceneLongitude: configData.viewLongitude,
200
+ };
201
+ this.resizeImages();
202
+ } else {
203
+ this.error = true;
204
+ }
205
+ },
206
+ methods: {
207
+ resizeScene({ width, height }) {
208
+ this.canvasDimensions.width = width;
209
+ this.canvasDimensions.height = height;
210
+ this.$nextTick(this.resizeImages);
211
+ },
212
+ sceneReset() {
213
+ this.sceneState = { preloaded: false, ready: false };
214
+ },
215
+ getBuildingData(buildingId) {
216
+ return this.buildingCallback
217
+ ? this.buildingCallback(buildingId)
218
+ : buildingId;
219
+ },
220
+ getBuildingId(event) {
221
+ const { x, y } = this.viewResolver(event.layerX, event.layerY);
222
+ return this.buildingMap.getBuildingId(this.canvasRaster, x, y);
223
+ },
224
+ resizeImages() {
225
+ if (!this.$refs.canvas) {
226
+ return;
227
+ }
228
+
229
+ this.canvasRaster.update(this.$refs.canvas);
230
+ if (this.$refs.overlays) {
231
+ for (const overlay of this.$refs.overlays) {
232
+ overlay.setupDraw();
233
+ }
234
+ }
235
+ },
236
+ onMove(evt) {
237
+ if (this.isHoverable) {
238
+ this.selectedBuildingId = this.getBuildingId(evt);
239
+
240
+ if (this.selectedBuildingId !== undefined) {
241
+ this.pos = {
242
+ x: evt.layerX,
243
+ y: evt.layerY,
244
+ width: this.canvasDimensions.width,
245
+ height: this.canvasDimensions.height,
246
+ };
247
+ }
248
+ }
249
+ },
250
+ onClick(evt) {
251
+ this.selectedBuildingId = this.getBuildingId(evt);
252
+ if (this.isHoverable && this.selectedBuilding) {
253
+ this.$emit("go-to-building", this.selectedBuilding);
254
+ }
255
+ },
256
+ getColor(buildingId) {
257
+ return this.colorCallback(
258
+ this.getBuildingData(buildingId),
259
+ buildingId === this.selectedBuildingId ? "hover" : "idle"
260
+ );
261
+ },
262
+ updateView({ resolve }) {
263
+ this.viewResolver = resolve;
264
+ },
265
+ },
266
+ });
267
+ </script>
268
+
269
+ <style scoped>
270
+ .north-indicator {
271
+ position: absolute;
272
+ right: 0;
273
+ top: 0;
274
+ height: 42px;
275
+ width: 42px;
276
+ margin-top: 12px;
277
+ margin-right: 8px;
278
+ }
279
+ .image-container {
280
+ position: relative;
281
+ }
282
+ .image-instances {
283
+ position: relative;
284
+ width: 100%;
285
+ height: 100%;
286
+ }
287
+ canvas {
288
+ position: absolute;
289
+ left: 0;
290
+ top: 0;
291
+ z-index: 2;
292
+ opacity: 0;
293
+ }
294
+ .fill-parent {
295
+ position: absolute;
296
+ top: 0;
297
+ bottom: 0;
298
+ left: 0;
299
+ right: 0;
300
+ }
301
+ @media only screen and (max-width: 980px) and (min-width: 500px) and (orientation: landscape) {
302
+ div.image-container {
303
+ margin-top: 44px;
304
+ }
305
+ }
306
+ @media only screen and (max-width: 500px) {
307
+ div.image-container {
308
+ margin-top: 44px;
309
+ margin-bottom: 44px;
310
+ }
311
+ }
312
+ </style>