Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

loading state for stage/panel/runner #473

Merged
merged 7 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion spx-gui/src/components/editor/panels/common/PanelItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@
<div class="remove" @click.stop="emit('remove')">
<UIIcon class="icon" type="trash" />
</div>
<UILoading v-if="loading" class="loading" />
</li>
</template>

<script setup lang="ts">
import { UIIcon } from '@/components/ui'
import { UIIcon, UILoading } from '@/components/ui'

const props = defineProps<{
active: boolean
name: string
loading?: boolean
}>()

const emit = defineEmits<{
Expand All @@ -24,6 +26,10 @@ const emit = defineEmits<{
</script>

<style lang="scss" scoped>
.loading {
position: absolute;
nighca marked this conversation as resolved.
Show resolved Hide resolved
}

.panel-item {
display: flex;
flex-direction: column;
Expand Down
8 changes: 7 additions & 1 deletion spx-gui/src/components/editor/panels/sound/SoundItem.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
<template>
<PanelItem class="sound-item" :active="active" :name="props.sound.name" @remove="emit('remove')">
<PanelItem
class="sound-item"
:active="active"
:name="props.sound.name"
:loading="!audioSrc"
@remove="emit('remove')"
>
<div class="content">
<SoundPlayer :src="audioSrc" :color="uiVariables.color.sound" />
</div>
Expand Down
2 changes: 1 addition & 1 deletion spx-gui/src/components/editor/panels/sprite/SpriteItem.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<PanelItem :active="active" :name="props.sprite.name" @remove="emit('remove')">
<PanelItem :active="active" :name="props.sprite.name" :loading="!imgSrc" @remove="emit('remove')">
<div class="img" :style="imgStyle"></div>
</PanelItem>
</template>
Expand Down
19 changes: 12 additions & 7 deletions spx-gui/src/components/editor/preview/stage-viewer/SpriteItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ import type { Sprite } from '@/models/sprite'
import type { Size } from '@/models/common'
import { useImgFile } from '@/utils/file'
import { useEditorCtx } from '../../EditorContextProvider.vue'
import { useSpritesReady } from './common'
import { round } from '@/utils/utils'

const props = defineProps<{
sprite: Sprite
mapSize: Size
spritesReadyMap: Map<string, boolean>
}>()

const nodeRef = ref<any>()
Expand All @@ -36,19 +36,24 @@ const costume = computed(() => props.sprite.costume)
const bitmapResolution = computed(() => costume.value?.bitmapResolution ?? 1)
const image = useImgFile(() => costume.value?.img)

const spritesReady = useSpritesReady()
watchEffect((onCleanup) => {
const spriteName = props.sprite.name
onCleanup(() => {
props.spritesReadyMap.delete(spriteName)
})
})

watchEffect((onCleanup) => {
// We need to notify event ready for SpriteTransformer (to get correct node size)
const name = props.sprite.name
const spriteName = props.sprite.name

const img = image.value
onCleanup(() => spritesReady.delete(props.sprite.name))
if (img == null) {
spritesReady.set(props.sprite.name, false)
props.spritesReadyMap.set(spriteName, false)
return
}
function handleImageLoad() {
spritesReady.set(name, true)
// We need to notify event ready for SpriteTransformer (to get correct node size)
props.spritesReadyMap.set(spriteName, true)
}
img.addEventListener('load', handleImageLoad)
onCleanup(() => img.removeEventListener('load', handleImageLoad))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ import { effect, ref } from 'vue'
import type { Transformer } from 'konva/lib/shapes/Transformer'
import type { Node } from 'konva/lib/Node'
import { useEditorCtx } from '../../EditorContextProvider.vue'
import { useSpritesReady } from './common'
import type { Sprite } from '@/models/sprite'

const props = defineProps<{
spritesReady: (sprite: Sprite) => boolean
}>()

const transformer = ref<any>()
const editorCtx = useEditorCtx()
const spritesReady = useSpritesReady()

effect(async () => {
if (transformer.value == null) return
Expand All @@ -22,7 +25,7 @@ effect(async () => {
return
}
// Wait for sprite ready, so that Konva can get correct node size
if (!spritesReady.get(sprite.name)) return
if (!props.spritesReady(sprite)) return
const stage = transformerNode.getStage()
if (stage == null) throw new Error('no stage')
const selectedNode = stage.findOne((node: Node) => node.getAttr('spriteName') === sprite.name)
Expand Down
27 changes: 21 additions & 6 deletions spx-gui/src/components/editor/preview/stage-viewer/StageViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@
:key="sprite.name"
:sprite="sprite"
:map-size="mapSize!"
:sprites-ready-map="spritesReadyMap"
/>
</v-layer>
<v-layer>
<SpriteTransformer />
<SpriteTransformer :sprites-ready="(sprite) => !!spritesReadyMap.get(sprite.name)" />
</v-layer>
</v-stage>
<UIDropdown trigger="manual" :visible="menuVisible" :pos="menuPos" placement="bottom-start">
Expand All @@ -38,24 +39,22 @@
}}</UIMenuItem>
</UIMenu>
</UIDropdown>
<UILoading v-if="spritesAndBackdropLoading" class="loading" />
</div>
</template>

<script setup lang="ts">
import { computed, ref } from 'vue'
import { computed, reactive, ref } from 'vue'
import type { KonvaEventObject } from 'konva/lib/Node'
import type { Stage } from 'konva/lib/Stage'
import { UIDropdown, UIMenu, UIMenuItem } from '@/components/ui'
import { UIDropdown, UILoading, UIMenu, UIMenuItem } from '@/components/ui'
import { useContentSize } from '@/utils/dom'
import { useAsyncComputed } from '@/utils/utils'
import type { Sprite } from '@/models/sprite'
import { useImgFile } from '@/utils/file'
import { useEditorCtx } from '../../EditorContextProvider.vue'
import SpriteTransformer from './SpriteTransformer.vue'
import SpriteItem from './SpriteItem.vue'
import { provideSpritesReady } from './common'

provideSpritesReady()

const editorCtx = useEditorCtx()
const conatiner = ref<HTMLElement | null>(null)
Expand All @@ -64,6 +63,8 @@ const containerSize = useContentSize(conatiner)
const stageRef = ref<any>()
const mapSize = useAsyncComputed(() => editorCtx.project.stage.getMapSize())

const spritesReadyMap = reactive(new Map<string, boolean>())

/** containerSize / mapSize */
const scale = computed(() => {
if (containerSize.width.value == null || containerSize.height.value == null) return null
Expand All @@ -90,6 +91,11 @@ const stageConfig = computed(() => {

const backdropImg = useImgFile(() => editorCtx.project.stage.backdrop?.img)

const spritesAndBackdropLoading = computed(() => {
if (backdropImg.value == null) return true
return editorCtx.project.sprites.some((s) => !spritesReadyMap.get(s.name))
})

const visibleSprites = computed(() => {
const { zorder, sprites } = editorCtx.project
return zorder.map((name) => sprites.find((s) => s.name === name)).filter(Boolean) as Sprite[]
Expand Down Expand Up @@ -145,5 +151,14 @@ function moveSprite(direction: 'up' | 'down' | 'top' | 'bottom') {
background-position: center;
background-repeat: repeat;
background-size: contain;
position: relative;
}

.loading {
position: absolute;
z-index: 100;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.5);
}
</style>
14 changes: 0 additions & 14 deletions spx-gui/src/components/editor/preview/stage-viewer/common.ts

This file was deleted.

27 changes: 26 additions & 1 deletion spx-gui/src/components/project/runner/IframeDisplay.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<template>
<iframe ref="iframe" class="runner" frameborder="0" src="about:blank" />
<div class="iframe-container">
<iframe ref="iframe" class="iframe" frameborder="0" src="about:blank" />
<UILoading v-if="loading" class="loading" />
</div>
</template>
<script setup lang="ts">
const emit = defineEmits<{
Expand All @@ -16,11 +19,14 @@ import rawRunnerHtml from '@/assets/ispx/runner.html?raw'
import wasmExecUrl from '@/assets/wasm_exec.js?url'
import wasmUrl from '@/assets/ispx/main.wasm?url'
import { watch } from 'vue'
import { UILoading } from '@/components/ui'

const { zipData } = defineProps<{ zipData: ArrayBuffer | Uint8Array }>()

const iframe = ref<HTMLIFrameElement>()

const loading = ref(true)

watch(iframe, () => {
const iframeWindow = iframe.value?.contentWindow as IframeWindow | null | undefined
if (!iframeWindow) {
Expand All @@ -37,6 +43,7 @@ watch(iframe, () => {
const canvas = iframeWindow.document.querySelector('canvas')
if (canvas == null) throw new Error('canvas expected in iframe')
canvas.focus() // focus to canvas by default, so the user can interact with the game immediately
loading.value = false
ComfyFluffy marked this conversation as resolved.
Show resolved Hide resolved
})
iframeWindow.console.log = function (...args: unknown[]) {
// eslint-disable-next-line no-console
Expand All @@ -49,3 +56,21 @@ watch(iframe, () => {
}
})
</script>

<style scoped lang="scss">
.iframe-container {
position: relative;
display: flex;
justify-content: center;
align-items: center;
}

.iframe {
width: 100%;
height: 100%;
}

.loading {
position: absolute;
}
</style>
8 changes: 8 additions & 0 deletions spx-gui/src/components/share/SharePage.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
<template>
<RunnerContainer v-if="project" mode="share" :project="project" />
<UILoading v-else class="loading" />
</template>
<script setup lang="ts">
import { watch } from 'vue'
import { useRoute } from 'vue-router'
import { ref } from 'vue'
import { Project } from '@/models/project'
import RunnerContainer from '../project/runner/RunnerContainer.vue'
import { UILoading } from '../ui'

const route = useRoute()
const project = ref<Project>()
Expand All @@ -26,3 +28,9 @@ watch(
{ deep: true, immediate: true }
)
</script>
<style lang="scss" scoped>
.loading {
height: 100%;
width: 100%;
}
</style>
Loading