This tutorial shows how to implement UI for editing marker properties based on marker type in marker.js 3.
marker.js supports different types of markers (shapes, text, arrows etc.) through specialized marker editors. Each marker type has its own set of editable properties - for example:
The marker editor's is() method lets us check the type of the current marker editor to show the appropriate property panel. For example:
// Check if current editor is a text marker
if (editor.is(TextMarkerEditor)) {
// Show text properties panel
}
This pattern allows us to:
The following sections demonstrate implementing this UI pattern with marker.js.
First, create property panels for different marker types:
<div id="shapePropertyPanel" style="display: none;">
<label>Stroke color: <input type="color" id="strokeColor"></label>
<label>Fill color: <input type="color" id="fillColor"></label>
<label>Stroke width: <input type="number" id="strokeWidth"></label>
<label>Stroke dash array: <input type="text" id="strokeDasharray"></label>
</div>
<div id="textPropertyPanel" style="display: none;">
<label>Text color: <input type="color" id="textColor"></label>
<label>Font family: <input type="text" id="fontFamily"></label>
<button id="decreaseFontSize">-</button>
<button id="increaseFontSize">+</button>
</div>
<div id="arrowPropertyPanel" style="display: none;">
<label>Arrow type:
<select id="arrowType">
<option value="none">—</button>
<option value="start"><—</button>
<option value="end">—></button>
<option value="both"><—></button>
</select>
</label>
</div>
Add event listeners to MarkerArea instance to show/hide appropriate property panels when markers are selected/deselected:
markerArea.addEventListener('markerselect', (e) => {
// Show text marker properties
if (e.detail.markerEditor.is(TextMarkerEditor)) {
const panel = document.getElementById('textPropertyPanel');
if (panel) {
panel.style.display = '';
}
setTextPropertyValues(e);
}
// Show shape marker properties
const panel = document.getElementById('shapePropertyPanel');
if (panel) {
panel.style.display = '';
}
// Show arrow marker properties
if (e.detail.markerEditor.is(ArrowMarkerEditor)) {
const arrowPanel = document.getElementById('arrowPropertyPanel');
if (arrowPanel) {
arrowPanel.style.display = '';
}
}
setPropertyValues(e);
});
markerArea.addEventListener('markerdeselect', () => {
// Hide all property panels
const panels = [
'shapePropertyPanel',
'textPropertyPanel',
'arrowPropertyPanel',
];
panels.forEach((id) => {
const panel = document.getElementById(id);
if (panel) {
panel.style.display = 'none';
}
});
});
When a marker is selected, populate the property panels with current values:
function setPropertyValues(e: CustomEvent<MarkerEditorEventData>) {
if (
e.detail.markerEditor.is(ShapeOutlineMarkerEditor) ||
e.detail.markerEditor.is(LinearMarkerEditor) ||
e.detail.markerEditor.is(FreehandMarkerEditor) ||
e.detail.markerEditor.is(PolygonMarkerEditor)
) {
const editor = e.detail.markerEditor;
document.getElementById('strokeColor').value = editor.strokeColor;
document.getElementById('fillColor').value = editor.fillColor;
document.getElementById('strokeWidth').value =
editor.strokeWidth.toString();
document.getElementById('strokeDasharray').value = editor.strokeDasharray;
}
if (e.detail.markerEditor.is(ArrowMarkerEditor)) {
document.getElementById('arrowType').value =
e.detail.markerEditor.arrowType;
}
}
function setTextPropertyValues(e: CustomEvent<MarkerEditorEventData>) {
if (e.detail.markerEditor.is(TextMarkerEditor)) {
document.getElementById('textColor').value =
e.detail.markerEditor.marker.color;
document.getElementById('fontFamily').value =
e.detail.markerEditor.marker.fontFamily;
}
}
Add event listeners to property inputs to update marker properties when changed:
const strokeColorInput = document.getElementById('strokeColor');
strokeColorInput.addEventListener('change', (ev) => {
const editor = markerArea.currentMarkerEditor;
if (
editor &&
(editor.is(ShapeOutlineMarkerEditor) ||
editor.is(LinearMarkerEditor) ||
editor.is(FreehandMarkerEditor) ||
editor.is(PolygonMarkerEditor))
) {
editor.strokeColor = ev.target.value;
}
});
const fontFamilyInput = document.getElementById('fontFamily');
fontFamilyInput.addEventListener('change', (ev) => {
const editor = markerArea.currentMarkerEditor;
if (editor && editor.is(TextMarkerEditor)) {
editor.fontFamily = ev.target.value;
}
});
const arrowTypeSelect = document.getElementById('arrowType');
arrowTypeSelect.addEventListener('change', (ev) => {
const editor = markerArea.currentMarkerEditor;
if (editor && editor.is(ArrowMarkerEditor)) {
editor.arrowType = ev.target.value;
}
});
Add event listeners for marker creation and changes to keep property panels in sync:
markerArea.addEventListener('markercreate', (e) => {
setPropertyValues(e);
});
markerArea.addEventListener('markerchange', (e) => {
setPropertyValues(e);
});