Using marker.js Live with Vue.js
In this walkthrough we will create a simple Vue.js app (using Vue CLI), add an image to it, and then add marker.js Live to show annotations created with marker.js 2 dynamically.
Follow along or just lookup the sections you are interested in below.
Create a Vue App
Let’s create a simple Vue.js app as the basis for our project. We will use Vue CLI for this.
vue create my-vue-mjslive-app
We will go with the "Manually select features" option in the menu. Check "Choose Vue version", "Babel", and "TypeScript" in the next step. We can uncheck everything else for this walkthrough. Press Enter to continue.
Here are the choices I'm making in the next steps:
- Vue version - 3.x
- Use class-style component syntax? - yes
- Use Babel alongside TypeScript - yes
- Where do you prefer placing config for ... - In dedicated config files
Now our app is scafolded for us and we can start our project.
Basic app setup
Let's remove the Vue logo from App.vue
and change the message for the HelloWorld
component so our App.vue
looks like this:
<template>
<HelloWorld msg="marker.js Live Vue Demo."/>
</template>
<script lang="ts">
import { Options, Vue } from 'vue-class-component';
import HelloWorld from './components/HelloWorld.vue';
@Options({
components: {
HelloWorld,
},
})
export default class App extends Vue {}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
Now, add the image below to the src/assets
folder
And let's add it to our component in HelloWorld.vue
.
For this, remove everything in the template
except for the message and add an image tag to display our sample image. At this point HelloWorld.vue
should look like this:
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<img ref="imgRef"
src="./../assets/desk.jpg"
style="max-width: 50%;"
/>
</div>
</template>
<script lang="ts">
import { Options, Vue } from 'vue-class-component';
@Options({
props: {
msg: String
}
})
export default class HelloWorld extends Vue {
msg!: string
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
Note that we've added a ref
attribute to our img
tag so we can later reference it in our code.
With all of this in place we are ready to add marker.js Live.
Add marker.js Live
Now that we have our base project setup, it is time to add marker.js Live.
Run:
npm install markerjs-live
or
yarn add markerjs-live
After that we can import marker.js Live into our component and then wire it up.
import * as mjslive from 'markerjs-live';
Before we move on to adding marker.js Live code, we need an annotation data (config) for the annotations. You create it with marker.js 2 and here's the config we'll use in this walkthrough:
config: mjslive.MarkerAreaState = {
width: 798,
height: 775,
markers: [
{
fillColor: "transparent",
strokeColor: "#FFFF00",
strokeWidth: 3,
strokeDasharray: "3",
opacity: 1,
left: 435,
top: 738,
width: 63,
height: 34,
rotationAngle: 0,
visualTransformMatrix: { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 },
containerTransformMatrix: { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 },
typeName: "EllipseFrameMarker",
notes: "https://en.wikipedia.org/wiki/Logo",
state: "select"
} as mjslive.MarkerBaseState,
{
bgColor: "#FFFF00",
tipPosition: { x: -71.17532348632812, y: 66.67831420898438 },
color: "#7C3AED",
fontFamily: 'Courier, "Courier New", monospace',
padding: 5,
text: "can't have logos",
left: 567.4873962402344,
top: 698.9164733886719,
width: 151.55291748046875,
height: 35.87835693359375,
rotationAngle: 9.475395686551806,
visualTransformMatrix: { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 },
containerTransformMatrix: {
a: 0.9863563850257626,
b: 0.164624061785966,
c: -0.164624061785966,
d: 0.9863563850257626,
e: 123.49474533415031,
f: -93.85420974513181
},
typeName: "CalloutMarker",
notes: "https://en.wikipedia.org/wiki/Logo",
state: "select"
} as mjslive.MarkerBaseState,
{
strokeColor: "#EF4444",
strokeWidth: 3,
strokeDasharray: "",
x1: 212,
y1: 474,
x2: 211,
y2: 535,
typeName: "MeasurementMarker",
notes: "https://en.wikipedia.org/wiki/Parallel_(geometry)",
state: "select"
} as mjslive.MarkerBaseState,
{
strokeColor: "#EF4444",
strokeWidth: 3,
strokeDasharray: "",
x1: 734,
y1: 470,
x2: 733,
y2: 553,
typeName: "MeasurementMarker",
notes: "https://en.wikipedia.org/wiki/Parallel_(geometry)",
state: "select"
} as mjslive.MarkerBaseState,
{
color: "#FFFFFF",
fontFamily: "Helvetica, Arial, sans-serif",
padding: 5,
text: "these should be parallel",
left: 382,
top: 491,
width: 208,
height: 30,
rotationAngle: 0,
visualTransformMatrix: { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 },
containerTransformMatrix: { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 },
typeName: "TextMarker",
notes: "https://en.wikipedia.org/wiki/Parallel_(geometry)",
state: "select"
} as mjslive.MarkerBaseState,
{
fillColor: "#2563EB",
strokeColor: "transparent",
strokeWidth: 0,
strokeDasharray: "",
opacity: 0.5,
left: -38,
top: 483,
width: 195,
height: 267,
rotationAngle: 0,
visualTransformMatrix: { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 },
containerTransformMatrix: { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 },
typeName: "HighlightMarker",
notes: "https://en.wikipedia.org/wiki/Photograph_manipulation#%22Photoshop%22_as_a_verb",
state: "select"
} as mjslive.MarkerBaseState,
{
strokeColor: "#EF4444",
strokeWidth: 3,
strokeDasharray: "",
x1: 352,
y1: 474,
x2: 354,
y2: 538,
typeName: "MeasurementMarker",
notes: "https://en.wikipedia.org/wiki/Parallel_(geometry)",
state: "select"
} as mjslive.MarkerBaseState,
{
strokeColor: "#EF4444",
strokeWidth: 3,
strokeDasharray: "",
x1: 613,
y1: 470,
x2: 615,
y2: 546,
typeName: "MeasurementMarker",
notes: "https://en.wikipedia.org/wiki/Parallel_(geometry)",
state: "select"
} as mjslive.MarkerBaseState
]
};
Let's create a showMarkers()
method that we will then call to show the annotation:
showMarkers() {
const markerView = new mjslive.MarkerView(this.$refs.imgRef as HTMLImageElement);
markerView.show(this.config);
}
The only thing left now is to wire this method up so that when the image loads we can load the annotations as well.
<img ref="imgRef"
src="./../assets/desk.jpg"
style="max-width: 50%;"
@load="showMarkers" />
And that's all there is. Your final HelloWorld.vue
should look something like this:
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<img
ref="imgRef"
src="./../assets/desk.jpg"
style="max-width: 50%"
@load="showMarkers"
/>
</div>
</template>
<script lang="ts">
import { Options, Vue } from "vue-class-component";
import * as mjslive from "markerjs-live";
@Options({
props: {
msg: String,
},
})
export default class HelloWorld extends Vue {
msg!: string;
config: mjslive.MarkerAreaState = {
width: 798,
height: 775,
markers: [
{
fillColor: "transparent",
strokeColor: "#FFFF00",
strokeWidth: 3,
strokeDasharray: "3",
opacity: 1,
left: 435,
top: 738,
width: 63,
height: 34,
rotationAngle: 0,
visualTransformMatrix: { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 },
containerTransformMatrix: { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 },
typeName: "EllipseFrameMarker",
notes: "https://en.wikipedia.org/wiki/Logo",
state: "select",
} as mjslive.MarkerBaseState,
{
bgColor: "#FFFF00",
tipPosition: { x: -71.17532348632812, y: 66.67831420898438 },
color: "#7C3AED",
fontFamily: 'Courier, "Courier New", monospace',
padding: 5,
text: "can't have logos",
left: 567.4873962402344,
top: 698.9164733886719,
width: 151.55291748046875,
height: 35.87835693359375,
rotationAngle: 9.475395686551806,
visualTransformMatrix: { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 },
containerTransformMatrix: {
a: 0.9863563850257626,
b: 0.164624061785966,
c: -0.164624061785966,
d: 0.9863563850257626,
e: 123.49474533415031,
f: -93.85420974513181,
},
typeName: "CalloutMarker",
notes: "https://en.wikipedia.org/wiki/Logo",
state: "select",
} as mjslive.MarkerBaseState,
{
strokeColor: "#EF4444",
strokeWidth: 3,
strokeDasharray: "",
x1: 212,
y1: 474,
x2: 211,
y2: 535,
typeName: "MeasurementMarker",
notes: "https://en.wikipedia.org/wiki/Parallel_(geometry)",
state: "select",
} as mjslive.MarkerBaseState,
{
strokeColor: "#EF4444",
strokeWidth: 3,
strokeDasharray: "",
x1: 734,
y1: 470,
x2: 733,
y2: 553,
typeName: "MeasurementMarker",
notes: "https://en.wikipedia.org/wiki/Parallel_(geometry)",
state: "select",
} as mjslive.MarkerBaseState,
{
color: "#FFFFFF",
fontFamily: "Helvetica, Arial, sans-serif",
padding: 5,
text: "these should be parallel",
left: 382,
top: 491,
width: 208,
height: 30,
rotationAngle: 0,
visualTransformMatrix: { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 },
containerTransformMatrix: { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 },
typeName: "TextMarker",
notes: "https://en.wikipedia.org/wiki/Parallel_(geometry)",
state: "select",
} as mjslive.MarkerBaseState,
{
fillColor: "#2563EB",
strokeColor: "transparent",
strokeWidth: 0,
strokeDasharray: "",
opacity: 0.5,
left: -38,
top: 483,
width: 195,
height: 267,
rotationAngle: 0,
visualTransformMatrix: { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 },
containerTransformMatrix: { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 },
typeName: "HighlightMarker",
notes:
"https://en.wikipedia.org/wiki/Photograph_manipulation#%22Photoshop%22_as_a_verb",
state: "select",
} as mjslive.MarkerBaseState,
{
strokeColor: "#EF4444",
strokeWidth: 3,
strokeDasharray: "",
x1: 352,
y1: 474,
x2: 354,
y2: 538,
typeName: "MeasurementMarker",
notes: "https://en.wikipedia.org/wiki/Parallel_(geometry)",
state: "select",
} as mjslive.MarkerBaseState,
{
strokeColor: "#EF4444",
strokeWidth: 3,
strokeDasharray: "",
x1: 613,
y1: 470,
x2: 615,
y2: 546,
typeName: "MeasurementMarker",
notes: "https://en.wikipedia.org/wiki/Parallel_(geometry)",
state: "select",
} as mjslive.MarkerBaseState,
],
};
showMarkers() {
const markerView = new mjslive.MarkerView(
this.$refs.imgRef as HTMLImageElement
);
markerView.show(this.config);
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
JavaScript version of this demo in CodeSandbox
Check out the JavaScript version of this demo embeded below, or click here to open it in CodeSandbox.
See also
Check out other documentation topics and demos for more advanced uses of marker.js Live that can be easily applied to your Vue apps.