CalendarImage -> DoorMapEditor

This commit is contained in:
Jörn-Michael Miehe 2023-01-17 18:25:56 +00:00
parent eae3eed29b
commit 10751cb798
6 changed files with 195 additions and 150 deletions

View file

@ -9,7 +9,7 @@
</h1>
<h2 class="subtitle has-text-centered">Der Gelöt</h2>
<CalendarImage />
<DoorMapEditor />
<CalendarDoor
v-for="(_, index) in 24"
@ -31,14 +31,14 @@
import { Options, Vue } from "vue-class-component";
import CalendarDoor from "./components/CalendarDoor.vue";
import CalendarImage from "./components/CalendarImage.vue";
import DoorMapEditor from "./components/DoorMapEditor.vue";
import ImageModal from "./components/ImageModal.vue";
import LoginModal from "./components/LoginModal.vue";
@Options({
components: {
CalendarDoor,
CalendarImage,
DoorMapEditor,
ImageModal,
LoginModal,
},

View file

@ -1,147 +0,0 @@
<template>
<div id="container" ref="container">
<img id="background" ref="background" src="@/assets/adventskalender.png" />
<svg
id="drawpad"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1000 1000"
preserveAspectRatio="none"
@pointerdown="on_pointerdown"
@pointermove="on_pointermove"
@pointerup="on_pointerup"
>
<rect
v-if="preview_state.visible"
class="focus"
:x="preview_rectangle.left"
:y="preview_rectangle.top"
:width="preview_rectangle.width"
:height="preview_rectangle.height"
/>
<rect
v-for="(rect, index) in rectangles"
:key="'rect' + index"
:x="rect.left"
:y="rect.top"
:width="rect.width"
:height="rect.height"
/>
</svg>
</div>
</template>
<script lang="ts">
import { Vue } from "vue-class-component";
import { Vector2D, Rectangle } from "./rectangles";
function get_event_thous(event: MouseEvent): Vector2D {
if (event.currentTarget === null) {
return new Vector2D();
}
let target = event.currentTarget as Element;
return new Vector2D(
Math.round((event.offsetX / target.clientWidth) * 1000),
Math.round((event.offsetY / target.clientHeight) * 1000)
);
}
export default class CalendarImage extends Vue {
// "preview" rectangle on click-drag
private preview_state = {
visible: false,
corner1: new Vector2D(),
corner2: new Vector2D(),
};
private readonly min_rect_area = 4;
private rectangles: Rectangle[] = [];
private on_pointerdown(event: MouseEvent) {
this.preview_state.visible = true;
this.preview_state.corner1 = get_event_thous(event);
this.preview_state.corner2 = get_event_thous(event);
}
private on_pointermove(event: MouseEvent) {
this.preview_state.corner2 = get_event_thous(event);
}
private on_pointerup() {
this.preview_state.visible = false;
if (this.preview_rectangle.area >= this.min_rect_area) {
this.rectangles.push(this.preview_rectangle);
}
}
private get preview_rectangle(): Rectangle {
return new Rectangle(
this.preview_state.corner1,
this.preview_state.corner2
).normalize();
}
// Hook "resize" events
private resize_observer?: ResizeObserver;
declare $refs: {
container: HTMLDivElement;
background: HTMLImageElement;
};
private on_resize() {
this.$refs.container.style.setProperty(
"height",
this.$refs.background.offsetHeight + "px"
);
}
public mounted() {
this.resize_observer = new ResizeObserver(this.on_resize);
this.resize_observer.observe(this.$refs.background);
}
public unmounted() {
if (this.resize_observer instanceof ResizeObserver) {
this.resize_observer.disconnect();
delete this.resize_observer;
}
}
}
</script>
<style lang="scss" scoped>
div#container {
position: relative;
user-select: none;
img#background {
position: absolute;
z-index: 1;
width: 100%;
}
svg#drawpad {
cursor: crosshair;
position: absolute;
z-index: 2;
height: 100%;
width: 100%;
rect {
fill: lightgreen;
stroke: green;
fill-opacity: 0.2;
stroke-opacity: 0.9;
stroke-width: 1;
&.focus {
fill: gold;
stroke: yellow;
}
}
}
}
</style>

View file

@ -0,0 +1,64 @@
<template>
<div ref="container">
<img ref="background" src="@/assets/adventskalender.png" />
<RectPad id="rectpad" />
</div>
</template>
<script lang="ts">
import { Vue, Options } from "vue-class-component";
import RectPad from "./rects/RectPad.vue";
@Options({
components: {
RectPad,
},
})
export default class CalendarImage extends Vue {
private resize_observer?: ResizeObserver;
declare $refs: {
container: HTMLDivElement;
background: HTMLImageElement;
};
private on_resize() {
this.$refs.container.style.setProperty(
"height",
this.$refs.background.offsetHeight + "px"
);
}
public mounted() {
this.resize_observer = new ResizeObserver(this.on_resize);
this.resize_observer.observe(this.$refs.background);
}
public unmounted() {
if (this.resize_observer instanceof ResizeObserver) {
this.resize_observer.disconnect();
delete this.resize_observer;
}
}
}
</script>
<style lang="scss" scoped>
div {
position: relative;
user-select: none;
img {
position: absolute;
z-index: 1;
width: 100%;
}
#rectpad {
position: absolute;
z-index: 2;
height: 100%;
width: 100%;
}
}
</style>

View file

@ -0,0 +1,43 @@
<template>
<rect
:class="focused ? 'focused' : ''"
:x="rectangle.left"
:y="rectangle.top"
:width="rectangle.width"
:height="rectangle.height"
/>
</template>
<script lang="ts">
import { Vue, Options } from "vue-class-component";
import { Rectangle } from "./rectangles";
@Options({
props: {
focused: {
type: Boolean,
default: false,
},
rectangle: Rectangle,
},
})
export default class Rect extends Vue {
private focused!: boolean;
private rectangle!: Rectangle;
}
</script>
<style lang="scss" scoped>
rect {
fill: lightgreen;
stroke: green;
fill-opacity: 0.2;
stroke-opacity: 0.9;
stroke-width: 1;
&.focused {
fill: gold;
stroke: yellow;
}
}
</style>

View file

@ -0,0 +1,85 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1000 1000"
preserveAspectRatio="none"
@pointerdown="on_pointerdown"
@pointermove="on_pointermove"
@pointerup="on_pointerup"
>
<Rect
v-if="preview_state.visible"
:focused="true"
:rectangle="preview_rectangle"
/>
<Rect
v-for="(rect, index) in rectangles"
:key="'rect' + index"
:rectangle="rect"
/>
</svg>
</template>
<script lang="ts">
import { Vue, Options } from "vue-class-component";
import { Vector2D, Rectangle } from "./rectangles";
import Rect from "./Rect.vue";
function get_event_thous(event: MouseEvent): Vector2D {
if (event.currentTarget === null) {
return new Vector2D();
}
let target = event.currentTarget as Element;
return new Vector2D(
Math.round((event.offsetX / target.clientWidth) * 1000),
Math.round((event.offsetY / target.clientHeight) * 1000)
);
}
@Options({
components: {
Rect,
},
})
export default class RectPad extends Vue {
private preview_state = {
visible: false,
corner1: new Vector2D(),
corner2: new Vector2D(),
};
private readonly min_rect_area = 4;
private rectangles: Rectangle[] = [];
private on_pointerdown(event: MouseEvent) {
this.preview_state.visible = true;
this.preview_state.corner1 = get_event_thous(event);
this.preview_state.corner2 = get_event_thous(event);
}
private on_pointermove(event: MouseEvent) {
this.preview_state.corner2 = get_event_thous(event);
}
private on_pointerup() {
this.preview_state.visible = false;
if (this.preview_rectangle.area >= this.min_rect_area) {
this.rectangles.push(this.preview_rectangle);
}
}
private get preview_rectangle(): Rectangle {
return new Rectangle(
this.preview_state.corner1,
this.preview_state.corner2
).normalize();
}
}
</script>
<style scoped>
svg {
cursor: crosshair;
}
</style>