After many hours that were perhaps better spent actually studying, I have a working card template! It is tested on both Ankidroid and Anki PC. When you scroll/swipe an image stack, only that stack scrolls. When you scroll/swipe outside of all image stacks, only the page scrolls.
You can add up to 4 image stacks. If you need fewer than that, you can leave the associated fields blank.
Link to download: Scrollable Image Stack (CT_MRI)
Relevant code below. Note this is for the back. For the front, I had to change all the variables (i.e. add __Front) to get rid of some strange conflict that was causing the scroll behavior to be wonky.
<div id="img" hidden>{{Frame}}</div>
<div id="images" class="div-1">{{Sequence}}</div>
<div id="img2" hidden>{{Frame2}}</div>
<div id="images2" class="div-1">{{Sequence2}}</div>
<div id="img3" hidden>{{Frame3}}</div>
<div id="images3" class="div-1">{{Sequence3}}</div>
<div id="img4" hidden>{{Frame4}}</div>
<div id="images4" class="div-1">{{Sequence4}}</div>
{{Back}}
<hr id="ans">
<script>
(() => {
/***PC***/
let isMouseHover = false;
let scrollingImage = document.getElementById("images");
scrollingImage.addEventListener("mouseleave", function (event) {
isMouseHover = false;
document.body.style.overflow = "auto";
}, false);
scrollingImage.addEventListener("mouseover", function (event) {
isMouseHover = true;
document.body.style.overflow = "hidden";
}, false);
let isMouseHover2 = false;
let scrollingImage2 = document.getElementById("images2");
scrollingImage2.addEventListener("mouseleave", function (event) {
isMouseHover2 = false;
document.body.style.overflow = "auto";
}, false);
scrollingImage2.addEventListener("mouseover", function (event) {
isMouseHover2 = true;
document.body.style.overflow = "hidden";
}, false);
let isMouseHover3 = false;
let scrollingImage3 = document.getElementById("images3");
scrollingImage3.addEventListener("mouseleave", function (event) {
isMouseHover3 = false;
document.body.style.overflow = "auto";
}, false);
scrollingImage3.addEventListener("mouseover", function (event) {
isMouseHover3 = true;
document.body.style.overflow = "hidden";
}, false);
let isMouseHover4 = false;
let scrollingImage4 = document.getElementById("images4");
scrollingImage4.addEventListener("mouseleave", function (event) {
isMouseHover4 = false;
document.body.style.overflow = "auto";
}, false);
scrollingImage4.addEventListener("mouseover", function (event) {
isMouseHover4 = true;
document.body.style.overflow = "hidden";
}, false);
let activeImg;
qImg = document.querySelector("#img > img");
const images = document.querySelectorAll("#images img");
for (const img of images) {
const isActive = img.src === qImg.src;
img.classList.toggle("active", isActive);
if (isActive) {
activeImg = img;
}
}
let activeImg2;
qImg2 = document.querySelector("#img2 > img");
const images2 = document.querySelectorAll("#images2 img");
for (const img2 of images2) {
const isActive2 = img2.src === qImg2.src;
img2.classList.toggle("active", isActive2);
if (isActive2) {
activeImg2 = img2;
}
}
let activeImg3;
qImg3 = document.querySelector("#img3 > img");
const images3 = document.querySelectorAll("#images3 img");
for (const img3 of images3) {
const isActive3 = img3.src === qImg3.src;
img3.classList.toggle("active", isActive3);
if (isActive3) {
activeImg3 = img3;
}
}
let activeImg4;
qImg4 = document.querySelector("#img4 > img");
const images4 = document.querySelectorAll("#images4 img");
for (const img4 of images4) {
const isActive4 = img4.src === qImg4.src;
img4.classList.toggle("active", isActive4);
if (isActive4) {
activeImg4 = img4;
}
}
document.getElementById("qa").addEventListener("wheel", (e) => {
if (isMouseHover) {
const nextImg = activeImg[e.deltaY > 0 ? "nextElementSibling" : "previousElementSibling"];
if (!nextImg) {
return;
}
activeImg.classList.remove("active");
nextImg.classList.add("active");
activeImg = nextImg;
} else if (isMouseHover2) {
const nextImg2 = activeImg2[e.deltaY > 0 ? "nextElementSibling" : "previousElementSibling"];
if (!nextImg2) {
return;
}
activeImg2.classList.remove("active");
nextImg2.classList.add("active");
activeImg2 = nextImg2;
} else if (isMouseHover3) {
const nextImg3 = activeImg3[e.deltaY > 0 ? "nextElementSibling" : "previousElementSibling"];
if (!nextImg3) {
return;
}
activeImg3.classList.remove("active");
nextImg3.classList.add("active");
activeImg3 = nextImg3;
} else if (isMouseHover4) {
const nextImg4 = activeImg4[e.deltaY > 0 ? "nextElementSibling" : "previousElementSibling"];
if (!nextImg4) {
return;
}
activeImg4.classList.remove("active");
nextImg4.classList.add("active");
activeImg4 = nextImg4;
}
});
/***ANDROID***/
let touchstartX = 0;
let touchstartY = 0;
let touchmovinglastX = 0;
let touchmovinglastY = 0;
let touchmovingX = 0;
let touchmovingY = 0;
let touchendX = 0;
let touchendY = 0;
let pixelCount = 10;
scrollingImage.addEventListener('touchstart', function(event) {
touchstartX = event.changedTouches[0].screenX;
touchstartY = event.changedTouches[0].screenY;
touchmovinglastX = event.changedTouches[0].screenX;
touchmovinglastY = event.changedTouches[0].screenY;
document.body.style.overflow = "hidden";
}, false);
scrollingImage.addEventListener('touchmove', function(event) {
touchmovingX = event.changedTouches[0].screenX;
touchmovingY = event.changedTouches[0].screenY;
handleGesture();
}, false);
scrollingImage.addEventListener('touchend', function(event) {
touchendX = event.changedTouches[0].screenX;
touchendY = event.changedTouches[0].screenY;
document.body.style.overflow = "auto";
}, false);
function handleGesture() {
if (Math.abs(touchmovingY - touchmovinglastY) > pixelCount) {
const nextImg = activeImg[touchmovingY > touchmovinglastY ? "nextElementSibling" : "previousElementSibling"];
if (!nextImg) {
return;
}
activeImg.classList.remove("active");
nextImg.classList.add("active");
activeImg = nextImg;
touchmovinglastX = touchmovingX;
touchmovinglastY = touchmovingY;
}
}
let touchstartX2 = 0;
let touchstartY2 = 0;
let touchmovinglastX2 = 0;
let touchmovinglastY2 = 0;
let touchmovingX2 = 0;
let touchmovingY2 = 0;
let touchendX2 = 0;
let touchendY2 = 0;
scrollingImage2.addEventListener('touchstart', function(event) {
touchstartX2 = event.changedTouches[0].screenX;
touchstartY2 = event.changedTouches[0].screenY;
touchmovinglastX2 = event.changedTouches[0].screenX;
touchmovinglastY2 = event.changedTouches[0].screenY;
document.body.style.overflow = "hidden";
}, false);
scrollingImage2.addEventListener('touchmove', function(event) {
touchmovingX2 = event.changedTouches[0].screenX;
touchmovingY2 = event.changedTouches[0].screenY;
handleGesture2();
}, false);
scrollingImage2.addEventListener('touchend', function(event) {
touchendX2 = event.changedTouches[0].screenX;
touchendY2 = event.changedTouches[0].screenY;
document.body.style.overflow = "auto";
}, false);
function handleGesture2() {
if (Math.abs(touchmovingY2 - touchmovinglastY2) > pixelCount) {
const nextImg2 = activeImg2[touchmovingY2 > touchmovinglastY2 ? "nextElementSibling" : "previousElementSibling"];
if (!nextImg2) {
return;
}
activeImg2.classList.remove("active");
nextImg2.classList.add("active");
activeImg2 = nextImg2;
touchmovinglastX2 = touchmovingX2;
touchmovinglastY2 = touchmovingY2;
}
}
let touchstartX3 = 0;
let touchstartY3 = 0;
let touchmovinglastX3 = 0;
let touchmovinglastY3 = 0;
let touchmovingX3 = 0;
let touchmovingY3 = 0;
let touchendX3 = 0;
let touchendY3 = 0;
scrollingImage3.addEventListener('touchstart', function(event) {
touchstartX3 = event.changedTouches[0].screenX;
touchstartY3 = event.changedTouches[0].screenY;
touchmovinglastX3 = event.changedTouches[0].screenX;
touchmovinglastY3 = event.changedTouches[0].screenY;
document.body.style.overflow = "hidden";
}, false);
scrollingImage3.addEventListener('touchmove', function(event) {
touchmovingX3 = event.changedTouches[0].screenX;
touchmovingY3 = event.changedTouches[0].screenY;
handleGesture3();
}, false);
scrollingImage3.addEventListener('touchend', function(event) {
touchendX3 = event.changedTouches[0].screenX;
touchendY3 = event.changedTouches[0].screenY;
document.body.style.overflow = "auto";
}, false);
function handleGesture3() {
if (Math.abs(touchmovingY3 - touchmovinglastY3) > pixelCount) {
const nextImg3 = activeImg3[touchmovingY3 > touchmovinglastY3 ? "nextElementSibling" : "previousElementSibling"];
if (!nextImg3) {
return;
}
activeImg3.classList.remove("active");
nextImg3.classList.add("active");
activeImg3 = nextImg3;
touchmovinglastX3 = touchmovingX3;
touchmovinglastY3 = touchmovingY3;
}
}
let touchstartX4 = 0;
let touchstartY4 = 0;
let touchmovinglastX4 = 0;
let touchmovinglastY4 = 0;
let touchmovingX4 = 0;
let touchmovingY4 = 0;
let touchendX4 = 0;
let touchendY4 = 0;
scrollingImage4.addEventListener('touchstart', function(event) {
touchstartX4 = event.changedTouches[0].screenX;
touchstartY4 = event.changedTouches[0].screenY;
touchmovinglastX4 = event.changedTouches[0].screenX;
touchmovinglastY4 = event.changedTouches[0].screenY;
document.body.style.overflow = "hidden";
}, false);
scrollingImage4.addEventListener('touchmove', function(event) {
touchmovingX4 = event.changedTouches[0].screenX;
touchmovingY4 = event.changedTouches[0].screenY;
handleGesture4();
}, false);
scrollingImage4.addEventListener('touchend', function(event) {
touchendX4 = event.changedTouches[0].screenX;
touchendY4 = event.changedTouches[0].screenY;
document.body.style.overflow = "auto";
}, false);
function handleGesture4() {
if (Math.abs(touchmovingY4 - touchmovinglastY4) > pixelCount) {
const nextImg4 = activeImg4[touchmovingY4 > touchmovinglastY4 ? "nextElementSibling" : "previousElementSibling"];
if (!nextImg4) {
return;
}
activeImg4.classList.remove("active");
nextImg4.classList.add("active");
activeImg4 = nextImg4;
touchmovinglastX4 = touchmovingX4;
touchmovinglastY4 = touchmovingY4;
}
}
})();
</script>
Necessary CSS style code:
#images img {
display: none;
}
#images img.active {
display: inline;
margin: 0 auto;
}
#images2 img {
display: none;
}
#images2 img.active {
display: inline;
margin: 0 auto;
}
#images3 img {
display: none;
}
#images3 img.active {
display: inline;
margin: 0 auto;
}
#images4 img {
display: none;
}
#images4 img.active {
display: inline;
margin: 0 auto;
}
.div-1 {
display: inline-block;
}
I have no formal training in computer programming. This was an amalgamation of many threads on stackoverflow etc, so please excuse any code inefficiencies!