-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathWotwMap.vue
157 lines (145 loc) · 4.26 KB
/
WotwMap.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
<template>
<div ref="container" class="relative">
<v-snackbar :value="loading" transition="scroll-y-transition" bottom left>
<template v-if="!!customLoadingText">
{{ customLoadingText }}
</template>
<template v-else>
Loading map: {{ Math.round(loadingProgressValue / loadingProgressMax * 100.0) }}%...
</template>
</v-snackbar>
<div ref="timelineStageContainer" class="stage-container fill-height">
<k-stage ref="stage" :config="stageConfig" @wheel="onStageWheel" @mouseMove="e => emitMousePosition('mousemove', e)" @click="e => emitMousePosition('click', e)">
<k-layer ref="layer">
<k-image v-for="(tile, index) in mapTiles" :key="index" :config="tile" />
</k-layer>
<slot />
</k-stage>
</div>
</div>
</template>
<script>
export default {
name: 'WotwMap',
props: {
renderFn: {
type: Function,
default: null,
},
},
data: () => ({
stageConfig: {
width: 1,
height: 1,
x: 1000,
y: -2880,
scale: {
x: 0.75,
y: -0.75,
},
draggable: true,
},
mapTiles: [],
loading: false,
loadingProgressValue: 0,
loadingProgressMax: 1,
customLoadingText: null,
isDestroyed: false,
}),
computed: {
constants: () => ({
tilesX: 36,
tilesY: 9,
tileSize: 512,
// Calculated with S C I E N C E
tileScale: 3023.460435 / 12359.664154,
mapOffsetX: -2015.2614140037902,
mapOffsetY: -3513.5714250429464,
}),
stage() {
return this.$refs.stage.getStage()
},
},
mounted() {
const observer = new ResizeObserver(() => this.updateStageSize())
observer.observe(this.$refs.timelineStageContainer)
for (let x = 0; x < this.constants.tilesX; x++) {
for (let y = 0; y < this.constants.tilesY; y++) {
this.loadImage(x, y)
}
}
},
beforeDestroy() {
this.isDestroyed = true
},
methods: {
emitMousePosition(eventName, event) {
const layer = this.stage.children[0]
this.$emit(eventName, event, layer.getRelativePointerPosition())
},
async loadImage(x, y) {
try {
const image = await this.getImage(x, y)
this.mapTiles.push({
image,
x: 512 * this.constants.tileScale * x + this.constants.mapOffsetX,
y: -512 * this.constants.tileScale * y + this.constants.mapOffsetY,
scale: {
x: this.constants.tileScale,
y: -this.constants.tileScale,
},
})
} catch (e) {}
},
async getImage(x, y) {
const image = new Image()
image.src = (await import(`@/assets/images/map/tile-${x}_${y}.png`)).default
return await new Promise((resolve) => {
image.onload = () => {
resolve(image)
}
})
},
updateStageSize() {
if (!this.$refs.timelineStageContainer) {
return
}
this.stageConfig.width = this.$refs.timelineStageContainer.clientWidth
this.stageConfig.height = this.$refs.timelineStageContainer.clientHeight
},
onStageWheel(e) {
const scaleBy = 0.96
const stage = this.stage
const oldScale = stage.scaleX()
const pointer = stage.getPointerPosition()
const mousePointTo = {
x: (pointer.x - stage.x()) / oldScale,
y: (pointer.y - stage.y()) / oldScale,
}
// how to scale? Zoom in? Or zoom out?
let direction = e.evt.deltaY > 0 ? 1 : -1
// when we zoom on trackpad, e.evt.ctrlKey is true
// in that case lets revert direction
if (e.evt.ctrlKey) {
direction = -direction
}
const newScale = direction > 0 ? oldScale * scaleBy : oldScale / scaleBy
stage.scale({ x: newScale, y: -newScale })
const newPos = {
x: pointer.x - mousePointTo.x * newScale,
y: pointer.y - mousePointTo.y * newScale,
}
stage.position(newPos)
},
},
}
</script>
<style lang="scss" scoped>
.relative {
position: relative;
}
.fill-canvas {
min-height: 0;
display: block;
}
</style>