Arranging the Images

Arrange the images in a trim and proper way

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

This lesson preview is part of the OpenSeadragon Deep Dive course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to OpenSeadragon Deep Dive, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

Thumbnail for the \newline course OpenSeadragon Deep Dive
  • [00:00 - 00:12] In the previous lesson, you loaded your DZIs into the viewer and displayed them all in a row using OpenSeadragon's collection mode. The collection mode feature is nice for quick mock-ups, but if you want full control over the layout, you'll want to take care of yourself.

    [00:13 - 00:25] In this lesson, you'll learn how to move images around in the OpenSeadragon viewer and how to create your own custom arrangement. In order to take control of the arrangement, we need to turn off collection mode by removing collectionMode: true and collectionRows: 1.

    [00:26 - 00:36] If you do so and run it, you'll see that it leaves all the images stacked directly on top of each other, overlapping. This is because OpenSeadragon is expecting you to explicitly state where to put all the images.

    [00:37 - 00:49] One way to lay out the images is during the viewer creation. You can specify image positioning as part of the tileSources option. So far, we've just been giving an array of image URLs like so.

    [00:50 - 01:06] For starters, we'll do a simple arrangement that just has each image next to the previous in a big row. This is similar to what the collection mode does for us, but an important difference is that whereas the collection mode places the images in a uniform grid, we can allow the image shape to dictate how much width it takes up.

    [01:07 - 01:21] Instead of passing our tile sources in as simple URLs, we can pass in objects that give OpenSeadragon additional information about where we want them placed. By default, each image has a width of 1 in viewport coordinates, so this will put them right next to each other.

    [01:22 - 01:34] The height of the image is whatever matches the width with the appropriate aspect ratio. So, for instance, an image with a 16 by 9 aspect ratio and a width of 1 would have a height of 0.5625.

    [01:35 - 01:46] An image that is twice as tall as it is wide would have a height of 2. Sometimes it's nice to think in bigger numbers. You can change the scale of your coordinates simply by giving your images bigger sizes.

    [01:47 - 02:03] That will look exactly the same on the screen as the previous example, because OpenSeadragon automatically zooms to wherever the images are, regardless of their size. For our example, we'd like to have all the images be the same height as each other and just further apart for wide images and closer for narrow ones.

    [02:04 - 02:15] For this, we need to know the aspect ratio of each image, which is not something we have in the data. We could certainly include that in our pre-processing, but in this case, we're going to gather that data dynamically once the images have been loaded.

    [02:16 - 02:28] Opening the images is an asynchronous operation, so we need to wait for the viewer's open event to know when the images are ready, like so. Once the viewer is properly loaded up, we can access all of its images through its world property.

    [02:29 - 02:39] The world is how the viewer keeps track of its images, which is stored as a stack of items. If any of the images overlap, the images lower in the stack are obscured by images higher up.

    [02:40 - 02:49] You can find out how many images there are by using the world's getItemCount method. And you can get a specific image by passing its place in the stack into the world's getItemAt method.

    [02:50 - 03:02] Each item in the world is a TiledImage, with all sorts of functions for manipulating how they look and where they are positioned. In our case, we just want to use setPosition to place them somewhere and setHeight to make sure they are all the same height.

    [03:03 - 03:11] Once we've set each one's position and height, we can measure it with getBounds to know where to put the next one. To keep track, we can add an x variable above our loop.

    [03:12 - 03:21] If you give it a try, it should work, but you'll notice some funky things. For one thing, the viewport doesn't update for the new layout, so you're left zoomed into the upper left corner of the first image.

    [03:22 - 03:34] Remember that by default, all of the images were stacked in the range of 0 to 1 in viewport coordinates, and now we've told each image to be 1000 units wide. That means we're just looking at the leftmost 1000th of the image.

    [03:35 - 03:41] There's an easy fix, of course. After we loop through and set the positions of all the images, we can tell the viewport to go home.

    [03:42 - 03:50] This works because the viewport's idea of where home is gets updated every time you move the images around. Now you can see it zooms out automatically.

    [03:51 - 03:59] It's an animated zoom though, and in this case, we probably just want to start out there. We can accomplish this by passing true into the function, meaning do it immediately.

    [04:00 - 04:12] There are many places in OpenSeadragon that automatically animate, but you can turn off the animation by passing in a true flag. Now we start out zoomed out, but you can see the images themselves also animate to their new positions.

    [04:13 - 04:21] That could be fixed by adding true flags to the set position and set height calls. One subtle detail is that the new position is stored immediately, even if it's being animated.

    [04:22 - 04:36] That means if you call getBounds, it already tells you the new final location, not wherever along the animation path it may be at the moment. If you really want to know exactly where it is in its animation, you can call getBounds(true), and it'll give you the true current location.

    [04:37 - 04:42] Here's what your app should look like. Do you see the difference from how collection mode laid the images out?

    [04:43 - 04:48] Unlike collection mode, all of the images are now the same height. Also, the space between the images is now consistent.

    [04:49 - 05:03] Now that you have your own arrangement code, you can tweak those values as much as you'd like. Okay, your code should look like this. I've taken the liberty of putting the 1000 height and the true for the immediately flag into constants to make the code more readable.

    [05:04 - 05:11] You're now loading all your converted images into the viewer. In the next lesson, we'll get into the multi-image API so you can arrange them however you want.