A Bookreader viewer, with facing pages
This example builds on the minimal viewer by adding support for 2-up views - facing pages.
One way of doing that would be to use two Canvas Panel instances, next to each other. But the expected behaviour of a 2-up viewer is that there is still only one viewport, the two canvases live in the same "zoom-space".
A generalised way of hoisting the viewport is to use <layout-container/>
. This demo uses Sequence panel which has some extra features to deal with paged sequences.
You can see it running at /demos/bookreader.html Bookreader 2-up viewer.
Bookreader 2-up viewer
<html><head> <title>Bookreader</title> <style> #manifest { width: 50vw; } #th { width:240px; border-right: 1px solid #999; margin: 5px; float:left; height:90%; overflow-y:scroll; } #main { width:72vw; margin-top:30px; margin-left:10px; float:left; height:90%;} .tc { display: inline-block; padding:5px; cursor: pointer; min-width: 100; } #viewportContainer { height:80%; background-color: black; display: flex } sequence-panel { display: flex; flex-direction: column; flex: 1; min-width: 0; /* Required for downsizing */ --atlas-container-flex: 1 1 0px; --atlas-background: #000; } /* #cp { height:100% } */ </style> <script src="https://pkg.csb.dev/digirati-co-uk/iiif-canvas-panel/commit/b239c900/@digirati/canvas-panel-web-components/dist/bundle.js"></script> <script src="https://cdn.jsdelivr.net/npm/@iiif/vault@latest/dist/index.umd.js"></script> <script src="https://cdn.jsdelivr.net/npm/@iiif/vault-helpers@latest/dist/index.umd.js"></script></head><body> <h1>A bookreader using Canvas Panel and Layout Container</h1> <h2 id="manifestLabel"></h2> <div> <input id="manifest" type="text" value="https://iiif.wellcomecollection.org/presentation/b18035723" /> <input id="go" type="button" value="Go" /> </div> <div> <div id="th"></div> <div id="main"> <div id="viewportContainer"> </div> <p id="cvLabel"></p> </div> </div> <script> function $(id) { return document.getElementById(id); } const vault = new IIIFVault.Vault(); const thumbHelper = VaultHelpers.createThumbnailHelper(vault); let manifest; async function loadManifest(){ const manifestUri = $("manifest").value; if(manifestUri){ manifest = await vault.loadManifest(manifestUri); const viewportContainer = $("viewportContainer"); viewportContainer.innerHTML = ''; // New component. const sequencePanel = document.createElement('sequence-panel'); sequencePanel.vault = vault; sequencePanel.setAttribute('manifest-id', manifestUri); sequencePanel.setAttribute('margin', '20'); viewportContainer.appendChild(sequencePanel); $("manifestLabel").innerText = VaultHelpers.getValue(manifest.label); let thumbsHtml = ""; let counter = 1; // for brevity we will assume that the manifest is "paged", the viewingDirection is left-to-right. thumbsHtml += '<div class="tc"></div>'; for(const canvasRef of vault.get(manifest.items)){ const canvas = vault.get(canvasRef); const label = VaultHelpers.getValue(canvas.label); thumbsHtml += `<div class="tc">${label}<br/>`; thumbsHtml += `<img data-uri="${canvas.id}" data-label="${label}" src="${canvas.thumbnail[0].id}" data-index="${counter}" />`; thumbsHtml += '</div>'; counter++; } $('th').innerHTML = thumbsHtml; const thumbs = document.querySelectorAll('#th img'); for(const thumb of thumbs){ thumb.addEventListener('click', () => { if (sequencePanel.sequence) { sequencePanel.sequence.setCurrentCanvasId(thumb.getAttribute('data-uri')); } }); } sequencePanel.addEventListener("sequence-change", () => { // Update the page labels for both visible canvases const sequence = sequencePanel.sequence; const itemIndexes = sequence.sequence[sequence.currentSequenceIndex]; const canvasRefs = itemIndexes.map(idx => sequence.items[idx]); const canvases = vault.get(canvasRefs); const labels = canvases.map(canvas => VaultHelpers.getValue(canvas.label)); $("cvLabel").innerText = labels.join(" | "); }); thumbs[0].click(); } } $("go").addEventListener('click', loadManifest); loadManifest(); </script></body></html>