forked from noxious/client
Added temp. offset logic for easier sprite management
This commit is contained in:
parent
15f9e9861e
commit
5dd9d1e7af
@ -48,12 +48,24 @@
|
|||||||
<input v-model.number="action.frameRate" class="input-field" type="number" step="any" name="frame-speed" placeholder="Frame rate" />
|
<input v-model.number="action.frameRate" class="input-field" type="number" step="any" name="frame-speed" placeholder="Frame rate" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-field-full">
|
<div class="form-field-full">
|
||||||
<SpriteActionsInput v-model="action.sprites" />
|
<SpriteActionsInput
|
||||||
|
v-model="action.sprites"
|
||||||
|
@tempOffsetChange="(index, offset) => handleTempOffsetChange(action, index, offset)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<SpritePreview v-if="selectedAction" :sprites="selectedAction.sprites" :frame-rate="selectedAction.frameRate" :is-modal-open="isModalOpen" @update:frame-rate="updateFrameRate" @update:is-modal-open="isModalOpen = $event" />
|
<SpritePreview
|
||||||
|
v-if="selectedAction"
|
||||||
|
:sprites="selectedAction.sprites"
|
||||||
|
:frame-rate="selectedAction.frameRate"
|
||||||
|
:is-modal-open="isModalOpen"
|
||||||
|
:temp-offset-index="tempOffsetData.index"
|
||||||
|
:temp-offset="tempOffsetData.offset"
|
||||||
|
@update:frame-rate="updateFrameRate"
|
||||||
|
@update:is-modal-open="isModalOpen = $event"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -187,6 +199,17 @@ function updateFrameRate(value: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tempOffsetData = ref<{ index: number | undefined; offset: { x: number; y: number } | undefined }>({
|
||||||
|
index: undefined,
|
||||||
|
offset: undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleTempOffsetChange(action: SpriteAction, index: number, offset: { x: number; y: number }) {
|
||||||
|
if (selectedAction.value === action) {
|
||||||
|
tempOffsetData.value = { index, offset }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
watch(selectedSprite, (sprite: Sprite | null) => {
|
watch(selectedSprite, (sprite: Sprite | null) => {
|
||||||
if (!sprite) return
|
if (!sprite) return
|
||||||
spriteName.value = sprite.name
|
spriteName.value = sprite.name
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-wrap gap-3">
|
<div class="flex flex-wrap gap-3">
|
||||||
<div v-for="(image, index) in modelValue" :key="index" class="h-20 w-20 p-4 bg-gray-300 bg-opacity-50 rounded text-center relative group cursor-move" draggable="true" @dragstart="dragStart($event, index)" @dragover.prevent @dragenter.prevent @drop="drop($event, index)">
|
<div v-for="(image, index) in modelValue" :key="index" class="h-20 w-20 p-4 bg-gray-300 bg-opacity-50 rounded text-center relative group cursor-move" draggable="true" @dragstart="dragStart($event, index)" @dragover.prevent @dragenter.prevent @drop="drop($event, index)">
|
||||||
<img :src="image.url" class="max-w-full max-h-full object-contain pointer-events-none" alt="Uploaded image" />
|
<img :src="image.url" class="max-w-full max-h-full object-contain pointer-events-none" alt="Uploaded image" @load="updateImageDimensions($event, index)" />
|
||||||
|
<div v-if="image.dimensions" class="absolute bottom-1 right-1 bg-black/50 text-white text-xs px-1 py-0.5 rounded transition-opacity font-default">
|
||||||
|
{{ image.dimensions.width }}x{{ image.dimensions.height }}
|
||||||
|
</div>
|
||||||
<div class="absolute top-1 left-1 flex-row space-y-1">
|
<div class="absolute top-1 left-1 flex-row space-y-1">
|
||||||
<button @click.stop="deleteImage(index)" class="bg-red-500 text-white rounded-full w-6 h-6 flex items-center justify-center cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity" aria-label="Delete image">
|
<button @click.stop="deleteImage(index)" class="bg-red-500 text-white rounded-full w-6 h-6 flex items-center justify-center cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity" aria-label="Delete image">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
@ -50,7 +53,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Modal from '@/components/utilities/Modal.vue'
|
import Modal from '@/components/utilities/Modal.vue'
|
||||||
import { ref } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
|
|
||||||
interface SpriteImage {
|
interface SpriteImage {
|
||||||
url: string
|
url: string
|
||||||
@ -58,6 +61,10 @@ interface SpriteImage {
|
|||||||
x: number
|
x: number
|
||||||
y: number
|
y: number
|
||||||
}
|
}
|
||||||
|
dimensions?: {
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -71,6 +78,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'update:modelValue', value: SpriteImage[]): void
|
(e: 'update:modelValue', value: SpriteImage[]): void
|
||||||
(e: 'close'): void
|
(e: 'close'): void
|
||||||
|
(e: 'tempOffsetChange', index: number, offset: { x: number, y: number }): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const fileInput = ref<HTMLInputElement | null>(null)
|
const fileInput = ref<HTMLInputElement | null>(null)
|
||||||
@ -162,4 +170,25 @@ const saveOffset = (index: number) => {
|
|||||||
updateImages(newImages)
|
updateImages(newImages)
|
||||||
closeOffsetModal()
|
closeOffsetModal()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onOffsetChange = () => {
|
||||||
|
if (selectedImageIndex.value !== null) {
|
||||||
|
emit('tempOffsetChange', selectedImageIndex.value, tempOffset.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(tempOffset, onOffsetChange, { deep: true })
|
||||||
|
|
||||||
|
const updateImageDimensions = (event: Event, index: number) => {
|
||||||
|
const img = event.target as HTMLImageElement
|
||||||
|
const newImages = [...props.modelValue]
|
||||||
|
newImages[index] = {
|
||||||
|
...newImages[index],
|
||||||
|
dimensions: {
|
||||||
|
width: img.naturalWidth,
|
||||||
|
height: img.naturalHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateImages(newImages)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
v-for="(sprite, index) in sprites"
|
v-for="(sprite, index) in spritesWithTempOffset"
|
||||||
:key="index"
|
:key="index"
|
||||||
:src="sprite.url"
|
:src="sprite.url"
|
||||||
alt="Sprite"
|
alt="Sprite"
|
||||||
@ -62,12 +62,14 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { SpriteImage } from '@/application/types'
|
import type { SpriteImage } from '@/application/types'
|
||||||
import Modal from '@/components/utilities/Modal.vue'
|
import Modal from '@/components/utilities/Modal.vue'
|
||||||
import { onMounted, onUnmounted, ref, watch } from 'vue'
|
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
sprites: SpriteImage[]
|
sprites: SpriteImage[]
|
||||||
frameRate: number
|
frameRate: number
|
||||||
isModalOpen?: boolean
|
isModalOpen?: boolean
|
||||||
|
tempOffsetIndex?: number
|
||||||
|
tempOffset?: { x: number, y: number }
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
@ -82,6 +84,15 @@ const localFrameRate = ref(props.frameRate)
|
|||||||
const zoomLevel = ref(100)
|
const zoomLevel = ref(100)
|
||||||
let animationInterval: number | null = null
|
let animationInterval: number | null = null
|
||||||
|
|
||||||
|
const spritesWithTempOffset = computed(() => {
|
||||||
|
return props.sprites.map((sprite, index) => {
|
||||||
|
if (index === props.tempOffsetIndex && props.tempOffset) {
|
||||||
|
return { ...sprite, offset: props.tempOffset }
|
||||||
|
}
|
||||||
|
return sprite
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
function updateContainerSize(event: Event) {
|
function updateContainerSize(event: Event) {
|
||||||
const img = event.target as HTMLImageElement
|
const img = event.target as HTMLImageElement
|
||||||
maxWidth.value = Math.max(maxWidth.value, img.naturalWidth)
|
maxWidth.value = Math.max(maxWidth.value, img.naturalWidth)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user