You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

97 lines
2.9 KiB

export default {
template: `
<div style="position:relative">
<img ref="img" :src="computed_src" style="width:100%; height:100%;" v-on="onEvents" draggable="false" />
<svg style="position:absolute;top:0;left:0;pointer-events:none" :viewBox="viewBox">
<g v-if="cross" :style="{ display: cssDisplay }">
<line :x1="x" y1="0" :x2="x" y2="100%" stroke="black" />
<line x1="0" :y1="y" x2="100%" :y2="y" stroke="black" />
</g>
<g v-html="content"></g>
</svg>
<slot></slot>
</div>
`,
data() {
return {
viewBox: "0 0 0 0",
x: 100,
y: 100,
cssDisplay: "none",
computed_src: undefined,
waiting_source: undefined,
loading: false,
};
},
mounted() {
setTimeout(() => this.compute_src(), 0); // NOTE: wait for window.path_prefix to be set in app.mounted()
const handle_completion = () => {
if (this.waiting_source) {
this.computed_src = this.waiting_source;
this.waiting_source = undefined;
} else {
this.loading = false;
}
};
this.$refs.img.addEventListener("load", handle_completion);
this.$refs.img.addEventListener("error", handle_completion);
},
updated() {
this.compute_src();
},
methods: {
compute_src() {
const new_src = (this.src.startsWith("/") ? window.path_prefix : "") + this.src;
if (new_src == this.computed_src) {
return;
}
if (this.loading) {
this.waiting_source = new_src;
} else {
this.computed_src = new_src;
this.loading = true;
}
},
updateCrossHair(e) {
this.x = (e.offsetX * e.target.naturalWidth) / e.target.clientWidth;
this.y = (e.offsetY * e.target.naturalHeight) / e.target.clientHeight;
},
onImageLoaded(e) {
this.viewBox = `0 0 ${e.target.naturalWidth} ${e.target.naturalHeight}`;
},
onMouseEvent(type, e) {
this.$emit("mouse", {
mouse_event_type: type,
image_x: (e.offsetX * e.target.naturalWidth) / e.target.clientWidth,
image_y: (e.offsetY * e.target.naturalHeight) / e.target.clientHeight,
button: e.button,
buttons: e.buttons,
altKey: e.altKey,
ctrlKey: e.ctrlKey,
metaKey: e.metaKey,
shiftKey: e.shiftKey,
});
},
},
computed: {
onEvents() {
const allEvents = {};
for (const type of this.events) {
allEvents[type] = (event) => this.onMouseEvent(type, event);
}
if (this.cross) {
allEvents["mouseenter"] = () => (this.cssDisplay = "block");
allEvents["mouseleave"] = () => (this.cssDisplay = "none");
allEvents["mousemove"] = (event) => this.updateCrossHair(event);
}
allEvents["load"] = (event) => this.onImageLoaded(event);
return allEvents;
},
},
props: {
src: String,
content: String,
events: Array,
cross: Boolean,
},
};