Image Occlusion Card Template with autoscroll & larger vertical images

Hey!

I made a modified image occlusion note type that displays vertical images larger (with scroll bar) and auto-scrolls to the current occlusion:

anki-demo-ezgif.com-video-to-gif-converter

Code for the frontside:

{{#Header}}<div>{{Header}}</div>{{/Header}}
<div style="display: none">{{cloze:Occlusion}}</div>
<div id="err"></div>
<div id="image-occlusion-container">
    {{Image}}
    <canvas id="image-occlusion-canvas"></canvas>
</div>
<script>
function scrollToActiveOcclusion() {
  const canvas = document.getElementById('image-occlusion-canvas');
  // Frontside: .cloze, Backside: .cloze-highlight
  const shapes = [...document.querySelectorAll('.cloze[data-shape], .cloze-highlight[data-shape]')];
  if (!canvas || !shapes.length) return;

  const top = Math.min(...shapes.map(s => +s.dataset.top));
  const bottom = Math.max(...shapes.map(s => +s.dataset.top + +s.dataset.height));
  const center = (top + bottom) / 2;

  const box = canvas.getBoundingClientRect();
  const y = box.top + window.scrollY + center * box.height;

  const maxScroll = document.documentElement.scrollHeight - window.innerHeight;
  const target = y - window.innerHeight / 2;

  const toBottom = maxScroll - target < window.innerHeight / 4;
  window.scrollTo({ top: toBottom ? maxScroll : Math.max(0, target) });
}

try {
  const container = document.getElementById('image-occlusion-container');
  const img = container.querySelector('img');

  const imgReady = img.complete
    ? Promise.resolve()
    : new Promise(resolve => {
        img.addEventListener('load', resolve, { once: true });
        img.addEventListener('error', resolve, { once: true });
      });

  imgReady.then(() => {
    container.classList.toggle('tall', img.naturalHeight > img.naturalWidth);

    anki.imageOcclusion.setup();
    
    setTimeout(function(){
    requestAnimationFrame(scrollToActiveOcclusion);
    }, 10);
  });
} catch (exc) {
  document.getElementById("err").innerHTML = `Failed to load image occlusion. Are you using the latest Anki version?<br><br>${exc}`;
}
</script>

Code for the backside:

{{#Header}}<div>{{Header}}</div>{{/Header}}
<div style="display: none">{{cloze:Occlusion}}</div>
<div id="err"></div>
<div id="image-occlusion-container">
    {{Image}}
    <canvas id="image-occlusion-canvas"></canvas>
</div>
<script>
function scrollToActiveOcclusion() {
  const canvas = document.getElementById('image-occlusion-canvas');
  const shapes = [...document.querySelectorAll('.cloze[data-shape], .cloze-highlight[data-shape]')];
  if (!canvas || !shapes.length) return;

  const top = Math.min(...shapes.map(s => +s.dataset.top));
  const bottom = Math.max(...shapes.map(s => +s.dataset.top + +s.dataset.height));
  const center = (top + bottom) / 2;

  const box = canvas.getBoundingClientRect();
  const y = box.top + window.scrollY + center * box.height;

  const maxScroll = document.documentElement.scrollHeight - window.innerHeight;
  const target = y - window.innerHeight / 2;

  const toBottom = maxScroll - target < window.innerHeight / 4;
  window.scrollTo({ top: toBottom ? maxScroll : Math.max(0, target) });
}

try {
  const container = document.getElementById('image-occlusion-container');
  const img = container.querySelector('img');

  const imgReady = img.complete
    ? Promise.resolve()
    : new Promise(resolve => {
        img.addEventListener('load', resolve, { once: true });
        img.addEventListener('error', resolve, { once: true });
      });

  imgReady.then(() => {
    container.classList.toggle('tall', img.naturalHeight > img.naturalWidth);

    anki.imageOcclusion.setup();
    
    setTimeout(function(){
    requestAnimationFrame(scrollToActiveOcclusion);
    }, 10);
  });
} catch (exc) {
  document.getElementById("err").innerHTML = `Failed to load image occlusion. Are you using the latest Anki version?<br><br>${exc}`;
}
</script>

<div><button id="toggle">Occlusions on/off</button></div>

Code for style:

#image-occlusion-canvas {
    --inactive-shape-color: #ffeba2;
    --active-shape-color: #ff8e8e;
    --inactive-shape-border: 1px #212121;
    --active-shape-border: 1px #212121;
}

.card {
    font-family: arial;
    font-size: 20px;
    text-align: center;
    color: black;
    background-color: white;
    margin-bottom: 75px;
}
img{
max-height: none !important;
max-width: none !important;
width: 100%;
height: auto;
}
#image-occlusion-container.tall{ 
		max-height: 150vh !important;
}

Note: You may need to adjust the field names on the front and back side (e.g. {{Bild}} instead of {{Image}} for the german Anki version).

Best
anghenfil

1 Like