Canvas Panel Developer Narrative - Version 1

Intro | Version 1 | Version 2 | Viewer 1 | Collaboration

First, a Design principle for Canvas Panel:

Internally, work with the latest IIIF specification (Presentation 3) and normalise incoming resources to this specification, rather than accommodating different versions of the spec throughout the codebase.

Canvas Panel is a component that renders canvases. So, perhaps Canvas Panel is to a IIIF Canvas what OpenSeadragon (OSD) is to a deep zoom tile source. But this analogy doesn't really stretch very far; you can pass multiple tile sources to OSD, but CP's job is to render one IIIF Canvas. That Canvas might have other canvases on it, as content. It might be VERY complex. And, your viewer could use multiple CP instances to render multiple canvases. But by definition, no matter how complex the canvas is, one CP instance renders one single canvas.

A better analogy is an img tag; this only takes one value for src. It has block level layout. But this doesn't quite work either. What's the block on the page that Canvas Panel occupies? Is it an exact correspondence with the canvas it is rendering, and therefore of the same proportions? No, I don't think so; even though we only give it one canvas to render, the UI we want is a viewport on that canvas: the block level element on the page is a viewport, in which we might zoom in to look at just part of the canvas. That seems to be the most aligned with how I would put together a viewer; managing a viewport independent of its content is difficult, you want the black box of CP taking care of that.

Having said that, you could use CP in locked mode; where it doesn't support pan and zoom; you set a height and width for the element proportional (or even the same as) the Canvas dimensions. This would be better for video, for example. It's not incompatible with the notion of a viewport; it's just a locked viewport. So I think the Viewport model is the right level for the CP component, the maximum usefulness. CP is a block element that renders a canvas, but there is navigational freedom over the canvas inside that block.

So, another Design principle for Canvas Panel:

CP is a block level element that renders a viewport on the canvas. In many scenarios this viewport may be fixed.

For later consideration: full screen. Sometimes you might want to defer full screen rendering to CP, but other times you might want to carry some of your other UI to full screen mode; i.e., it's the viewer going full screen, not CP. Developers have a similar consideration when using OSD.

I want CP to behave like a div, so I can treat it like other divs in my application, style it, apply predictable CSS to it, etc. As a web component, this feels like a Customized built-in element, rather than an Autonomous custom element (following the definitions given at MDN).

My first thought is to just feed it canvases, that probably live in manifests:

  <div is="canvas-panel-1" id="cp1" class="canvaspanel"    
    data-canvas-uri="https://wellcomelibrary.org/iiif/b18035723/canvas/c8"       
    data-partof-uri="https://tomcrane.github.io/webcomponents/wunder.json">   
  </div>

It's a div tag, extended with the canvas-panel-1 behaviour.

In this type of usage, CP is entirely declarative. It needs to go off and load things under the hood. In this case it will need to load the manifest, then find the canvas in it, then render the canvas. To be useful for people who want to do this, CP needs to have all the normalisation to P3 built in. The resources at the other end of those attribute values could be Presentation 2 manifests and canvases; used like this, CP will have to manage the conversion to Presentation 3 itself.

Maybe I don't know what's on this canvas. I don't even know if it has an image, image with tilesource, multiple images, Choice, Audio, Video, text. As my viewer gets more sophisticated, I want to deal with more potential IIIF content. If I want a component like this to insert that content into a page, I don't want different components for different types of content. This declarative component would be good for that.

As established in the viewport design principle, the thing you are setting the size of in the linked CSS isn't a canvas. You probably don't know how big or what shape the canvas is at this point. The component is a viewport - the space in which canvas panel is free to render the canvas (or part of it).

<style>    
    .canvaspanel {        
        width: 600px;        
        height: 600px;        
        padding:2px;        
        border:1px solid green;        
        background-color:black;    
    } 
</style> 
    
<div is="canvas-panel-1" id="cp1" class="canvaspanel"
    data-canvas-uri="https://wellcomelibrary.org/iiif/b18035723/canvas/c8"
    data-partof-uri="https://tomcrane.github.io/webcomponents/wunder.json"> 
</div>

This is a 600 by 600 pixel div, rendering the canvas I have given it.

I haven't written any code here to respond to anything on that canvas that might need a user interaction to provide the full rendering - e.g., a Choice annotation. And I can't step in to deal with user interactions in the canvas, such as a click on a linking annotation.

Another example:

 <div is="canvas-panel-1" id="cp1" class="canvaspanel"
    data-canvas-uri="https://wellcomelibrary.org/iiif/b22047347/canvas/c24"
    data-partof-uri="https://tomcrane.github.io/webcomponents/wunder.json"> 
 </div>

The red border is part of this demo Canvas Panel's rendering. You'd probably control whether it was visible (usually not) but I'm showing it here to indicate the shape of the canvas that is being shown in this viewport.

While the component shown here would be very useful, I don't think this is Canvas Panel. This feels like a component that's a light wrapper around CP - taking the attributes and then loading the resources, like this:

 <div is="iiif-resource-loader" id="ir1" class="canvaspanel"
    data-canvas-uri="https://wellcomelibrary.org/iiif/b22047347/canvas/c24"
    data-partof-uri="https://tomcrane.github.io/webcomponents/wunder.json"> 
 </div>

or...

 <div is="iiif-resource-loader" id="ir1" class="canvaspanel"
    data-canvas-uri="https://wellcomelibrary.org/iiif/b22047347/canvas/c24"
    data-partof-uri="https://tomcrane.github.io/webcomponents/wunder.json" 
    data-xywh="100,500,300,1000">
 </div>

In that second example I want the viewport to show a particular region of the canvas.

The existing React implementation of Canvas Panel lets you do something just like this:

 <div id="app"></div>
 <div class="canvaspanel">
 <div id="viewer" 
     data-element="canvas-panel-viewer" 
     data-manifest="https://wellcomelibrary.org/iiif/b18035723/manifest"
     data-canvas="https://wellcomelibrary.org/iiif/b18035723/canvas/c8">
 </div>
 </div>
 <script src="//unpkg.com/@canvas-panel/viewer-element@0.0.9/umd/@canvas-panel/viewer-element.min.js"></script>

View in Sandbox

Or, you would initialise it with an annotation as discussed in the Import to viewers proposal.

The iiif-resource-loader web component isn't Canvas Panel, then, but a thin wrapper around it. A useful component in its own right, but something else.

If this isn't Canvas Panel, what is?

On to Version 2


View on GitHub