Anki: Bildverdeckung mit automatischem Scrollen
Wie viele andere Student*innen auch nutze ich die Open-Source Karteikartensoftware Anki, um effektiver auswendig zu lernen. Gerade für Gebiete wie Anatomie ist der Bildverdeckungs-Kartentyp sehr nützlich.
Doch es gibt ein Problem: Bei großen Bildern wird alles extrem klein. Deshalb habe ich den Kartentyp so angepasst, dass hochformatige Bilder breiter (–> größer) anzeigt werden. Dadurch entsteht dann ein vertikaler Scrollbalken, doch durch eine weitere Anpassung wird die gerade aktive Bildverdeckung automatisch in die Mitte des Bildschirms gescrollt:
Selber Ausprobieren
Probiert es doch gerne selber einmal aus! Entweder ihr ladet euch einfach dieses Stapelpaket mit dem Notiztypen herunter, oder ihr passt euren Bildverdeckungstyp selber an.
via Stapelpaket
- Ladet euch einfach dieses Stapelpaket herunter und öffnet es in Anki.
- Nun müsst ihr euren bestehenden Bildverdeckungskarten den neuen Typ zuordnen: Öffnet die Kartenverwaltung
- In der Kartenverwaltung wählt ihr links Notiztypen –> Bildverdeckung
- Jetzt markiert ihr alle angezeigten Notizen (Strg + A), macht einen Rechtsklick –> Notiztyp ändern
- Dort wählt ihr dann den gerade importierten Notiztypen, er müsste Bildverdeckung-[irgendwas] heißen.
Manuell
- Öffnet Anki
- Ruft Extras –> Notiztypen verwalten auf
- Wählt “Bildverdeckung” aus und geht auf “Karten ...”
- Ersetzt den kompletten Code bei “Vorlage für Vorderseite” mit:
{{#Kopfzeile}}<div>{{Kopfzeile}}</div>{{/Kopfzeile}}
<small>{{Deck}}</small>
<div style="display: none">{{cloze:Verdeckung}}</div>
<div id="err"></div>
<div id="image-occlusion-container">
{{Bild}}
<canvas id="image-occlusion-canvas"></canvas>
</div>
<script>
function scrollToActiveOcclusion() {
const canvas = document.getElementById('image-occlusion-canvas');
// Vorderseite: .cloze, Rückseite: .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;
// Zentrieren lohnt sich nicht, wenn darunter kaum noch Platz ist -> ganz nach unten
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(() => {
// 1. Klasse anhand der echten Bildmaße
container.classList.toggle('tall', img.naturalHeight > img.naturalWidth);
anki.imageOcclusion.setup();
setTimeout(function(){
requestAnimationFrame(scrollToActiveOcclusion);
}, 10);
});
} catch (exc) {
document.getElementById("err").innerHTML = `Fehler beim Laden der Bildverdeckung. Ist Ihre Anki-Version aktuell?<br><br>${exc}`;
}
</script>
- Ersetzt den kompletten Code bei “Vorlage für Rückseite” mit:
{{#Kopfzeile}}<div>{{Kopfzeile}}</div>{{/Kopfzeile}}
<small>{{Deck}}</small>
<div style="display: none">{{cloze:Verdeckung}}</div>
<div id="err"></div>
<div id="image-occlusion-container">
{{Bild}}
<canvas id="image-occlusion-canvas"></canvas>
</div>
<script>
function scrollToActiveOcclusion() {
const canvas = document.getElementById('image-occlusion-canvas');
// Vorderseite: .cloze, Rückseite: .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;
// Zentrieren lohnt sich nicht, wenn darunter kaum noch Platz ist -> ganz nach unten
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(() => {
// 1. Klasse anhand der echten Bildmaße
container.classList.toggle('tall', img.naturalHeight > img.naturalWidth);
anki.imageOcclusion.setup();
setTimeout(function(){
requestAnimationFrame(scrollToActiveOcclusion);
}, 10);
});
} catch (exc) {
document.getElementById("err").innerHTML = `Fehler beim Laden der Bildverdeckung. Ist Ihre Anki-Version aktuell?<br><br>${exc}`;
}
</script>
<div><button id="toggle">Verdeckungen ein/aus</button></div>
- Ersetzt den kompletten Code bei “Stil” mit:
#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;
}
Fragen oder Probleme? Schreibt mir an blog@anghenfil.de!