Using marker.js Live with React
In this walkthrough we will create a simple React app (using Create React App), add an image to it, and then add marker.js Live to display annotations made with marker.js 2 on top of that image.
Follow along or just lookup the sections you are interested in below.
Create React App
Let’s create a simple React app as the basis for our project. We will use Create React App for this.
npx create-react-app my-react-mjslive-app --template typescript
cd my-react-mjslive-app
NOTE:
We are using TypeScript in this walkthrough but feel free to remove the --template typescript
part from the command above and just omit type declarations in the code examples below.
We will use class components in this walkthrough. So, we will start with this barebone App.tsx (App.js, if you aren’t using TypeScript).:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
render() {
return (
<div className="App">
<h1>marker.js Live Demo.</h1>
</div>
);
}
}
export default App;
Add an image
Now, let's add an image to our app. Grab the image below and save it to the public
folder of the project. Let's call it desk.jpg
.
Next we will add the image to our App component and create a ref for it, so that we can pass it to marker.js Live.
At this stage, our App file looks like this:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
imgRef = React.createRef<HTMLImageElement>();
render() {
return (
<div className="App">
<h1>marker.js Live Demo.</h1>
<img ref={this.imgRef}
src="desk.jpg"
alt="sample"
style={{ maxWidth: '50%' }}
/>
</div>
);
}
}
export default App;
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
]
};
Now, let's create a showMarkers()
method that we will then call to show the markers from the config:
showMarkers(target: HTMLImageElement) {
const markerView = new mjslive.MarkerView(target);
markerView.show(this.config);
}
Now we just need to call this method when our image loads:
<img ref={this.imgRef}
src="desk.jpg"
alt="sample"
style={{ maxWidth: '50%' }}
onLoad={ ev => this.showMarkers(ev.target as HTMLImageElement)}
/>
And that's all it takes to integrate the basic marker.js Live functionality into a React app and dynamically display the annotations created with marker.js 2.
The complete code
Here's the complete final App.tsx
for this walkthrough:
import React, { Component } from 'react';
import * as mjslive from 'markerjs-live';
import './App.css';
class App extends Component {
imgRef = React.createRef<HTMLImageElement>();
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(target: HTMLImageElement) {
const markerView = new mjslive.MarkerView(target);
markerView.show(this.config);
}
render() {
return (
<div className="App">
<h1>marker.js Live Demo.</h1>
<img ref={this.imgRef}
src="desk.jpg"
alt="sample"
style={{ maxWidth: '50%' }}
onLoad={ ev => this.showMarkers(ev.target as HTMLImageElement)}
/>
</div>
);
}
}
export default App;
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 React apps.