game scenarios
This commit is contained in:
@@ -1,40 +1,93 @@
|
||||
<template>
|
||||
<div class="container my-3">
|
||||
<v-btn-toggle variant="tonal" density="compact" class="mx-auto" v-model="listMode" color="blue">
|
||||
<v-btn class="text-none" value="scene"><v-icon>mdi-panorama-outline</v-icon><span>{{ $l.addScene }}</span></v-btn>
|
||||
<v-btn class="text-none" value="object"><v-icon>mdi-bird</v-icon><span>{{ $l.addScene }}</span></v-btn>
|
||||
<v-btn class="text-none" value="task"><v-icon>mdi-checkbox-marked-circle-plus-outline</v-icon><span>{{ $l.addScene }}</span></v-btn>
|
||||
<v-btn-toggle variant="tonal" density="compact" class="mx-auto" v-model="mode" color="blue">
|
||||
<v-btn size="small" class="text-none" value="default" prepend-icon="mdi-cursor-default-click">Pointer</v-btn>
|
||||
<v-btn size="small" class="text-none" value="select" prepend-icon="mdi-select-multiple">Select</v-btn>
|
||||
<v-btn size="small" class="text-none" value="move" prepend-icon="mdi-cursor-move">Move</v-btn>
|
||||
<v-btn size="small" class="text-none" value="pan" prepend-icon="mdi-hand-back-right-outline">Pan</v-btn>
|
||||
|
||||
<v-btn size="small" class="text-none" value="scene" prepend-icon="mdi-panorama-outline">{{ $l.addScene }}</v-btn>
|
||||
<v-btn size="small" class="text-none" value="object" prepend-icon="mdi-bird">{{ $l.addScene }}</v-btn>
|
||||
<v-btn size="small" class="text-none" value="task" prepend-icon="mdi-checkbox-marked-circle-plus-outline">{{ $l.addScene }}</v-btn>
|
||||
</v-btn-toggle>
|
||||
<div @wheel="onWheel" @mousedown="onMouseDown" @mouseup="onMouseUp" @mousemove="onDrag"
|
||||
:class="`svg-container ${mode}`" ref="svgContainer">
|
||||
<svg>
|
||||
|
||||
<svg class="scene-designer" @resize="resize" :width="viewBox.w" :height="viewBox.h" :viewBox="`${vb.x} ${vb.y} ${vb.w} ${vb.h}`" x="0" y="0"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<SvgRectangle v-model="selector" class="selector"></SvgRectangle>
|
||||
</svg>
|
||||
</div>
|
||||
<v-navigation-drawer location="right">
|
||||
<svg-scene></svg-scene>
|
||||
<template v-for="(item, i) in flatItems" :key="i">
|
||||
<component :is="components[item.__type]" :ref="'svg-'+item.id"
|
||||
:vd="item.vd" v-model="item.data" @target="setTarget($event, item)"
|
||||
:visible="item.visible" :cid="item.id"
|
||||
:parent="item.__parent" :selected="selectedItem.includes(item)">
|
||||
</component>
|
||||
</template>
|
||||
</v-navigation-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SvgScene from './SvgScene.vue';
|
||||
import GameObject from './GameObject.vue';
|
||||
import Scene from './Scene.vue';
|
||||
import SvgRectangle from './SvgRectangle.vue';
|
||||
import Utils from '@/lib/utils';
|
||||
|
||||
const components = {
|
||||
SvgScene
|
||||
Scene, GameObject
|
||||
}
|
||||
|
||||
export default {
|
||||
components: { SvgScene },
|
||||
props:{
|
||||
modelValue: Object
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
listMode: 'select'
|
||||
mode: 'default',
|
||||
selectedItem: [],
|
||||
viewBox: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 1000,
|
||||
h: 800
|
||||
},
|
||||
scale: 1,
|
||||
offset:{
|
||||
x: 0,
|
||||
y: 0
|
||||
},
|
||||
selector:{
|
||||
x1:0, x2:0, y1:0, y2:0
|
||||
},
|
||||
modeStep: 0,
|
||||
mousedown: false,
|
||||
target: null,
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
window.addEventListener('resize', this.resize);
|
||||
this.resize();
|
||||
},
|
||||
unmounted(){
|
||||
window,removeEventListener('resize', this.resize);
|
||||
},
|
||||
computed:{
|
||||
object:()=>this.modelValue,
|
||||
vb(){
|
||||
return {
|
||||
x: (1-this.scale)*100 + this.offset.x,
|
||||
y: (1-this.scale)*100 + this.offset.y,
|
||||
w: this.viewBox.w * this.scale,
|
||||
h: this.viewBox.h * this.scale
|
||||
}
|
||||
},
|
||||
object(){
|
||||
return this.modelValue;
|
||||
},
|
||||
items(){
|
||||
return this.object.scenes;
|
||||
},
|
||||
zoom:{
|
||||
get(){
|
||||
return 1 / this.scale;
|
||||
@@ -43,12 +96,22 @@ export default {
|
||||
this.rescale(1 / v);
|
||||
}
|
||||
},
|
||||
mode(){
|
||||
return this.listMode[0];
|
||||
},
|
||||
components(){
|
||||
return components;
|
||||
},
|
||||
flatItems(){
|
||||
let fi = [];
|
||||
this.items.forEach(i=>{
|
||||
i.__type = 'Scene';
|
||||
fi.push(i);
|
||||
i.data?.gameObjects?.forEach(go=>{
|
||||
fi.push(go);
|
||||
go.__parent = i;
|
||||
go.__type = 'GameObject';
|
||||
})
|
||||
})
|
||||
return fi.reverse();
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
rescale(scale, e){
|
||||
@@ -111,6 +174,7 @@ export default {
|
||||
})
|
||||
},
|
||||
onWheel(e){
|
||||
e.preventDefault();
|
||||
this.rescale(null, e);
|
||||
},
|
||||
onDrag(e){
|
||||
@@ -121,11 +185,11 @@ export default {
|
||||
y: Utils.round(e.movementY*this.scale, 0)
|
||||
}
|
||||
this.selectedItem.forEach(i=>{
|
||||
let mf = components[i.name].modifiers;
|
||||
mf.filter(m=>m.match(/^x[0-9]+$/)).forEach(x=>i.data[x]+= p.x);
|
||||
mf.filter(m=>m.match(/^y[0-9]+$/)).forEach(y=>i.data[y]+= p.y);
|
||||
let mf = components[i.__type].modifiers;
|
||||
mf.filter(m=>m.match(/^x[0-9]+$/)).forEach(x=>i.vd[x]+= p.x);
|
||||
mf.filter(m=>m.match(/^y[0-9]+$/)).forEach(y=>i.vd[y]+= p.y);
|
||||
})
|
||||
}else if (this.target) {
|
||||
}else if (this.target && this.mode != 'pan') {
|
||||
this.retarget(e)
|
||||
}
|
||||
}
|
||||
@@ -164,7 +228,7 @@ export default {
|
||||
let id, nid = 1;
|
||||
do {
|
||||
id = `${this.components[this.mode].name}-${nid++}`
|
||||
}while (this.items.find(i=>i.id == id))
|
||||
}while (this.flatItems.find(i=>i.id == id))
|
||||
this.items.push({
|
||||
name: this.mode,
|
||||
data: this.target.target,
|
||||
@@ -211,59 +275,56 @@ export default {
|
||||
this.target = t;
|
||||
this.selectedItem = [item]
|
||||
},
|
||||
async save(){
|
||||
let imageData = await Utils.blobToBase64(await (await fetch(this.imageTarget)).blob());
|
||||
//console.log(imageData)
|
||||
let jsonData = {
|
||||
items: this.items,
|
||||
viewBox: this.viewBox,
|
||||
offset: this.offset,
|
||||
zoom: this.zoom,
|
||||
rotation: this.imgRotation,
|
||||
page: this.page,
|
||||
data: imageData
|
||||
};
|
||||
const link = document.createElement('a')
|
||||
const blob = new Blob([JSON.stringify(jsonData)], {type: 'application/json'});
|
||||
link.href = URL.createObjectURL(blob)
|
||||
link.download = "document.json"
|
||||
link.click();
|
||||
},
|
||||
select(){
|
||||
let r = Utils.adjustMinMax(this.selector);
|
||||
this.selectedItem = this.items.filter(i=>this.$refs['svg-'+i.id][0].intersect(r));
|
||||
this.selectedItem = this.flatItems.filter(i=>this.$refs['svg-'+i.id][0].intersect(r));
|
||||
},
|
||||
|
||||
async processImage(){
|
||||
this.processingImage = true;
|
||||
await this.$nextTick();
|
||||
let processor = new ImageProcessor(this.$refs.imageCanvas, this.img)
|
||||
let lines = processor.identifyLines(), iw = this.img.naturalWidth;
|
||||
lines.forEach((l, i)=>{
|
||||
this.items.push({
|
||||
name: 'SvgHorizontalLine', id: `aline-${i}`, visible: true, title: `ALine-${i}`,
|
||||
data: { x1:iw*.1, y1: l, x2: iw*.9}
|
||||
})
|
||||
})
|
||||
this.processingImage = false;
|
||||
resize(){
|
||||
let r = this.$refs.svgContainer;
|
||||
this.viewBox.w = r.clientWidth;
|
||||
this.viewBox.h = r.clientHeight;
|
||||
//this.zoom = Math.min(r.clientWidth / this.viewBox.w, r.clientHeight / this.viewBox.h);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
:root {
|
||||
--svg-scale: 1;
|
||||
}
|
||||
|
||||
.svg-container{
|
||||
svg{
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
image{
|
||||
clip-path: circle(50% at 50% 50%);
|
||||
}
|
||||
circle {
|
||||
stroke: rgb(var(--v-theme-primary));
|
||||
fill:rgba(255,255,255,.5);
|
||||
stroke-width: 2px;
|
||||
}
|
||||
g{
|
||||
&.selected circle{
|
||||
fill: rgba(var(--v-theme-secondary), .9);
|
||||
}
|
||||
}
|
||||
line, path{
|
||||
stroke: #19c;
|
||||
stroke-width: calc( 2px * var(--svg-scale) );
|
||||
}
|
||||
g.selector {
|
||||
line {
|
||||
stroke-dasharray: 0 calc(8 * var(--svg-scale)) 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
image{
|
||||
clip-path: circle(50% at 50% 50%);
|
||||
}
|
||||
circle {
|
||||
stroke: rgb(var(--v-theme-primary));
|
||||
fill:rgba(255,255,255,.5);
|
||||
stroke-width: 2px;
|
||||
overflow: hidden;
|
||||
max-width: 100vw;
|
||||
max-height: 95vh;
|
||||
&.pan {
|
||||
cursor: grab;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user