Skip to content

Migrating from Micrio 3.x to 4.x Client 4.x

This guide only applies to upgrading existing custom JS and CSS implementations in your own website using Micrio 3.x and older to version 4.x.

If you don't know what that is, or if you are simply using Micrio out of the box without any customizations, this article is not for you.

If you do have a Micrio implementation inside your own website which makes use of the Micrio API, and/or has custom CSS, please read on.

Should I upgrade?

Each Micrio JavaScript major version has a stable working release:

This means that Micrio projects using those versions will always keep working as they are. Since Micrio only uses standard browser functions, older versions will not suddenly stop working at some point in time.

If you have a current running Micrio implementation in your own site, and it works well, there is no need to upgrade to Micrio 4.

We also still offer limited support on the previous major release, in this case Micrio 3.3. If you find any bugs, please contact us.

When should I upgrade?

There are a few reasons where upgrading makes sense:

  • You want to use newly added features, like petapixel viewing support or built-in swipeable galleries
  • You are using Micrio on many places inside your website (ie. a collection viewer), and you want to be sure you are running the latest and greatest Micrio version for performance or code cleanliness reasons

If either of these questions can be answered with "Yes", this article is for you.

What's changed?

Micrio 4.0 comes with quite some changes under the hood compared to Micrio 3.x, resulting in changed HTML/CSS, and a new JavaScript API.

1. HTML & CSS

Micrio 4.0 has a slightly different HTML DOM model compared to Micrio 3.x.

All changes to primary elements are below in this table. If an element is not listed here, it hasn't changed.

NOTE: & refers to the main Micrio element

HTML element3.x CSS selector4.0 CSS selector
All Micrio icon buttonsbutton.micrio-iconbutton.micrio-action
Main Micrio element.micrio-containermicr-io
Image marker& > micrio-markers > micrio-marker& > .micrio-markers > .marker
Custom marker classnames from the editor (ie red)& micrio-marker.class-red& .marker.red
Marker popup& > micrio-interface > micrio-marker-popup& > .marker-popup
Minimap& > micrio-interface > canvas.minimap& > canvas.minimap
Image info panel& > micrio-interface > header.micrio-image-info& > details
Control buttons& > micrio-interface > aside.controls& > aside.controls
Video tour controls& > micrio-interface > micrio-media& > div.tour
Marker tour controls& > micrio-interface > micrio-marker-tour& > div.tour
Full-window popover& > micrio-popover > figure.micrio-popover& > div.popover > section

As you can see, it's not all that different. If you are only using custom CSS for your Micrio implementations, upgrading can be done quickly by updating your CSS selectors.

Do mind to double check your changes: it's possible some underlying HTML of the main selected element has also changed, for instance the marker popup HTML.

2. JavaScript (& TypeScript)

Micrio 4.0 has been rewritten from scratch, for the following reasons:

  • Upgrading from pure JavaScript to pure TypeScript

    This allows for full typing and more optimized releases, making it much harder to introduce "stupid" bugs from our end.

    It might also be one step closer to making the Micrio client Open Source in the (definitely not near) future.

  • Incorporating the Svelte Store logic into Micrio

    This is something a lot of front-end UI frameworks like React, Vue, and Svelte use: Based on arbitrary JSON data, the interface is set to represent the data, instead of the developer reading the data, and then programatically saying "Ok, the user clicked a marker, now show the marker popup!".

    This makes Micrio reactive state oriented instead of imperative. These are complicated words to describe the API approach, which will be demonstrated below.

Below, all major changes are explained and documented.

2.1. No more new Micrio()

Since Micrio makes full use of the CustomElements browser API, it actually became a HTML Element, instead of added functionality to a standard element.

This means that the Micrio JavaScript constructor is now no longer directly callable.

If you want to create a Micrio instance in pure JS, this is however still possible. Let's say you already have the image ID, and original image width and height.

In 3.x, you would use this (putting it into the HTML <body>):

js
const micrio = new Micrio({
  id: 'abcde',
  width: 1000,
  height: 1000,
  container: document.body
});
const micrio = new Micrio({
  id: 'abcde',
  width: 1000,
  height: 1000,
  container: document.body
});

This would auto-load everything, and place the newly created <micr-io> element in the document.

In 4.x, you would use this:

js
// This already fully instances a new Micrio useable container
const micrio = document.createElement('micr-io');

// The .open() call accepts a simple ID, or a full JSON Micrio info model
micrio.open({
  id: 'abcde',
  width: 1000,
  height: 1000
});

// Put the element inside the main <body>
document.body.appendChild(micrio);
// This already fully instances a new Micrio useable container
const micrio = document.createElement('micr-io');

// The .open() call accepts a simple ID, or a full JSON Micrio info model
micrio.open({
  id: 'abcde',
  width: 1000,
  height: 1000
});

// Put the element inside the main <body>
document.body.appendChild(micrio);

Not that different!

2.1.1 Querying the Micrio instance

To access an already instantiated Micrio instance, for 3.x, this was a .micrio property on the main <micr-io> tag:

js
const micrio = document.querySelector('micr-io').micrio;
const micrio = document.querySelector('micr-io').micrio;

Following the above, for 4.0 this is the main <micr-io> tag itself:

js
const micrio = document.querySelector('micr-io');
const micrio = document.querySelector('micr-io');

2.1.2. Using the Typescript Declarations include

If your project uses TypeScript, be sure to include the Micrio declaration file into your project, so it will be seamlessly integrated in your IDE. Use it as such:

js
import type { HTMLMicrioElement } from 'Micrio';

// This gives you full type checking and any editor autocompletion
const micrio = document.querySelector('micr-io') as HTMLMicrioElement;
import type { HTMLMicrioElement } from 'Micrio';

// This gives you full type checking and any editor autocompletion
const micrio = document.querySelector('micr-io') as HTMLMicrioElement;

The following sections use the above Micrio JS reference as base micrio.

2.2. The virtual camera

Good news here: this is mostly unchanged 😃

The only change is that camera.setCoo() now takes 3 arguments (x, y and scale), instead of a single array [x,y,scale].

The rest of the camera API is still the same. See the API documentation pages for more information on how to use it.

Flying to a certain viewport is still done like so:

js
// This returns a promise which is resolved when the animation is done
// Or throws an error when an animation is interrupted
await micrio.camera.flyToView([.25,.25,.75,.75]);

console.log('ok, done!');
// This returns a promise which is resolved when the animation is done
// Or throws an error when an animation is interrupted
await micrio.camera.flyToView([.25,.25,.75,.75]);

console.log('ok, done!');

2.3. Marker and tour APIs

This is the largest change, API-wise.

If you are unfamiliar with stores, basically they are a subscribable variable, which you can get and set, but also hook to any changes to this variable.

For more technical documentation, please see the 4.0 API docs here:

In this document, we will focus on the practicalities of upgrading.

2.3.1. Markers

Where in a previous Micrio version you would open a marker like this:

js
// Markers are propagated to JS instances on image load
const marker = micrio.modules.markers.items[0];

// Let's open the marker
marker.open();

// And close it again after 5 seconds
setTimeout(() => { marker.close() }, 5000);
// Markers are propagated to JS instances on image load
const marker = micrio.modules.markers.items[0];

// Let's open the marker
marker.open();

// And close it again after 5 seconds
setTimeout(() => { marker.close() }, 5000);

This works well, and is very simple. On load, the Micrio JSON data containing the image markers is propagated to JavaScript classes, giving each marker its own JavaScript instance, having methods as .open() and .close().

However, this load operation is done only once. If you want to dynamically add or remove markers, things get dirty very quickly since the API to add or remove markers should also be made (and documented).

In 4.0, the entire interface is based on pure JSON data, and there are no such things as wrapped JS instances.

To do the above example in 4.0:

js
// The first marker of the current active shown MicrioImage
const marker = micrio.$current.$data.markers[0];

// Open the marker simply by setting its original JSON data as `state.marker`
micrio.state.marker.set(marker);

// Close it after 5 seconds
setTimeout(() => { micrio.state.marker.set(undefined) }, 5000);
// The first marker of the current active shown MicrioImage
const marker = micrio.$current.$data.markers[0];

// Open the marker simply by setting its original JSON data as `state.marker`
micrio.state.marker.set(marker);

// Close it after 5 seconds
setTimeout(() => { micrio.state.marker.set(undefined) }, 5000);

2.3.2. Marker Tours & Video Tours

For tours, use the micrio.state.tour store variable. Since you can only have 1 main active tour at the time (disregarding video tours embedded inside a marker), it makes sense to have a single store for both types.

Starting a tour in 3.x:

js
// A marker tour
micrio.modules.markerTours[0].start();

// The current marker tour:
const currentTour = micrio.modules.markers.currentTour;

// A video tour
micrio.modules.tours[0].start();

// The current video tour:
const currentVideoTour = micrio.modules.currentTour;
// A marker tour
micrio.modules.markerTours[0].start();

// The current marker tour:
const currentTour = micrio.modules.markers.currentTour;

// A video tour
micrio.modules.tours[0].start();

// The current video tour:
const currentVideoTour = micrio.modules.currentTour;

In 4.x:

js
// The current image JSON data
const data = micrio.$current.$data;

// Starting the first marker tour
micrio.state.tour.set(data.markerTours[0]);

// Starting the first video touir
micrio.state.tour.set(data.tours[0]);

// The current tour (JSON):
const currentTour = micrio.state.$tour;
// The current image JSON data
const data = micrio.$current.$data;

// Starting the first marker tour
micrio.state.tour.set(data.markerTours[0]);

// Starting the first video touir
micrio.state.tour.set(data.tours[0]);

// The current tour (JSON):
const currentTour = micrio.state.$tour;

As you can maybe already tell, this allows for a much cleaner setup, since only one tour can be active, and switching between different tours automatically gets correctly picked up by the UI.

2.3.3. Tour APIs

Another thing that has changed, is how to control the tours using the API.

Marker tours

Marker tours are now only known a JSON object: micrio.state.$tour.

However, this object has a special getter/setter .currentStep, which is the current step the user is on, and can be set to a value of your own:

js
// The current marker tour step
micrio.state.$tour.currentStep;

// Go to the next step
micrio.state.$tour.currentStep++;

// Go to the 4th step
micrio.state.$tour.currentStep = 4;
// The current marker tour step
micrio.state.$tour.currentStep;

// Go to the next step
micrio.state.$tour.currentStep++;

// Go to the 4th step
micrio.state.$tour.currentStep = 4;
Video tours

Video tours are a special case. Since they behave very much like <audio> and <video>, they also are implemented as such, sharing the most important methods and properties of HTMLMediaElement:

propertytypedescription
.pause()functionPauses a currently running tour
.play()async functionPlays a video tour
.durationnumberThe total tour duration in seconds
.currentTimenumberThe current playback time in seconds
.pausedbooleanThe current tour is paused

The currentTime property may also be set to a desired timestamp in seconds, which will set the video tour to that point in time.

An example:

js
// Let's start a video tour
micrio.state.tour.set(micrio.$current.$data.tours[0]);

// Get the media controller object
const tourMedia = micrio.state.media;

// Pause the tour
tourMedia.pause();

// Skip the tour to 4 seconds
tourMedia.currentTime = 4;

// Continue the tour
tourMedia.play();
// Let's start a video tour
micrio.state.tour.set(micrio.$current.$data.tours[0]);

// Get the media controller object
const tourMedia = micrio.state.media;

// Pause the tour
tourMedia.pause();

// Skip the tour to 4 seconds
tourMedia.currentTime = 4;

// Continue the tour
tourMedia.play();

2.4. Image navigating

Not a lot has changed here. If you want to manually navigate to a new Micrio image, here's how to do it:

3.x:

js
micrio.modules.navigator.goto('your-ID');
micrio.modules.navigator.goto('your-ID');

4.0:

js
micrio.open('your-ID');
micrio.open('your-ID');

The result is the same: a crossfade will take place to the requested image, once the required data has been loaded.

2.5. Events

2.5.1. Behavioral events (markers, tours, camera)

With Micrio 3.x, if you want to know when a user has opened a marker, or started a tour, or moved the camera, you would set an EventListener for this.

Micrio 4.0 still fully supports this, so you can still use this method to do any custom event handling. Please see the Micrio JS Events API article for more details about this.

The only change is that all different kinds of tour events are now bundled:

3.x4.0
markerTours-(start|stop|step)tour-(start|stop|step)
tours-(start|event|stop)tour-(start|event|stop)

And the event.detail argument will be the JSON objects of the corresponding tour.

2.5.2. Lifecycle events: Using store readers to get updates

With the added state possibilities, it is also possible to get updates through them. This is a little more robust than listening to arbitrary events.

Micrio 3.x has a pretty detailed event lifecycle.

Since all of these events fire on the main <micr-io> element, this quickly becomes complicated when you're working with multiple images (ie. a multi-image tour): the load event would fire for each image loaded, so as a developer you have to keep check on which image this is.

These lifecycle events have been reduced in Micrio 4.0 to be only load and show:

  • load only fires once, when data of the first image in the Micrio controller has been downloaded
  • show also fires once, when the first image frame has been succesfully drawn in the browser

For more fine grained data loading/updating control, see the next section.

2.5.3. Subscribing to image switching, data loading and changes

If you want to watch for image switches inside Micrio, you can watch micrio.current:

js
micrio.current.subscribe(image => {
	console.log('Opening image', image);
});
micrio.current.subscribe(image => {
	console.log('Opening image', image);
});

If you want to know when an individual image data has been loaded, you can use the .info and .data store variables of the individual MicrioImage.

An example, on opening an image and watching for load events:

js
// This returns a new MicrioImage, and also sets this as micrio.$current
const image = micrio.open('abcde');

// Subscribe to .info property: the image data (width, height, etc)
// This change will trigger once the needed JSON has been downloaded
image.info.subscribe(info => {
	// When setting the subscriber, it will immediately fire the current value
	// So it can also be undefined
	if(!info) return;

	// Log what we got
	console.log('Got image info!', info.width, info.height);
});

// Subscribe to .data: the CultureData (markers, tours, audio)  
// This also triggers once the JSON has been downloaded
image.data.subscribe(data => {
	console.log('Got image data!', data);
});
// This returns a new MicrioImage, and also sets this as micrio.$current
const image = micrio.open('abcde');

// Subscribe to .info property: the image data (width, height, etc)
// This change will trigger once the needed JSON has been downloaded
image.info.subscribe(info => {
	// When setting the subscriber, it will immediately fire the current value
	// So it can also be undefined
	if(!info) return;

	// Log what we got
	console.log('Got image info!', info.width, info.height);
});

// Subscribe to .data: the CultureData (markers, tours, audio)  
// This also triggers once the JSON has been downloaded
image.data.subscribe(data => {
	console.log('Got image data!', data);
});

The image.info property is a Readable, meaning that it's read-only.

However, the image.data is Writable, meaning that you can update the data yourself.

2.5.4. Changing or updating marker data yourself

To set your own custom data overwriting anything currently loaded, set the image.data store:

js
micrio.$current.data.set({
	"markers": [
		{
			"title": "This is a test marker!",
			"x": .5,
			"y": .5
		}
	]
})
micrio.$current.data.set({
	"markers": [
		{
			"title": "This is a test marker!",
			"x": .5,
			"y": .5
		}
	]
})

Or adding a marker to an existing loaded data object, use the update function:

js
micrio.$current.data.update(data => {
	data.markers.push({
		"title": "This is a newly added marker",
		"x": .5,
		"y": .5
	});
	return data;
})
micrio.$current.data.update(data => {
	data.markers.push({
		"title": "This is a newly added marker",
		"x": .5,
		"y": .5
	});
	return data;
})

This will immediately be visible!

Concluding

In this guide, we have glossed over all major changes in the Micrio 3.x vs 4.0 APIs. Upgrading existing projects should be doable using this guide and all other available documentation.

However, if you have any more specific questions about this, contact us!