typescriptsvelte

Automatically trigger button to change image svelte


I'm a bit new with svelte, and I'm building a survay for AmazonTurk with it to have video-like player embeded in the webpage.

To do that, I am building an image carousel, and need to simulate an automatic image change to make it look as if it is a video player.

I have the setup, but can't figure out how to simulate the "next" button click until "pause" is pressed, in which case the transision should stop until "play" button is pressed etc.

My code is the following:

<script lang="ts">
    import { slide } from "svelte/transition";
    export let imageDir: string;
    export let startIndex = 0;
    export let endIndex = 0;
    let images = new Array(
        endIndex - startIndex).fill(0).map((_, i) => 
        imageDir + "/frame_" + (i + startIndex) + ".jpg"
    );

    let videoPaused = false;

    const nextImage = () => {
        currentSlideItem = (currentSlideItem + 1) % images.length;
    }

    const prevImage = () => {
        if (currentSlideItem != 0){
            currentSlideItem = (currentSlideItem - 1) % images.length;
        } else {
            currentSlideItem = images.length - 1;
        }
    }
    
    const play = () => {
        if (videoPaused){
            videoPaused = false;
        }
    }
    
    const pause = () => {
        if (!videoPaused){
            videoPaused = true;
        }
    }
</script>

<main>
    {#each [images[currentSlideItem]] as image, index}
        <img transition:slide="{{delay:200}}" src={image} alt='Blank' width="1080"/>
    {/each}
    <div class="carousel-buttons">
        <button id="play" on:click={() => play()}>&#9199</button>
        <button id="pause" on:click={() => pause()}>&#9208</button>
        <button id="prev" on:click={() => prevImage()}>&#9194</button>
        <button id="next" on:click={() => nextImage()}>&#9193</button>
    </div>
</main>

What I've Tried so Far:

  1. I've tried adding another <script></script> close inside the <main></main> close with the desired functionality as follows:
<main>
    {#each [images[currentSlideItem]] as image, index}
        <img transition:slide="{{delay:200}}" src={image} alt='Blank' width="1080"/>
    {/each}
    <div class="carousel-buttons">
        <button id="play" on:click={() => play()}>&#9199</button>
        <button id="pause" on:click={() => pause()}>&#9208</button>
        <button id="prev" on:click={() => prevImage()}>&#9194</button>
        <button id="next" on:click={() => nextImage()}>&#9193</button>
    </div>
        
    <script>
        prevBtn = document.getElementById("prev")
        playBtn = document.getElementById("play")
        pauseBtn = document.getElementById("pause")
        nextBtn = document.getElementById("next")

        while !videoPaused{
            triggerClick(nextBtn);
        }

    </script>
</main>

but it seems this code is not being even executed.

  1. My another try was adding the desired functionality in the first <script></script> close as follows:
<script lang="ts">
    import { slide } from "svelte/transition";
    export let imageDir: string;
    export let startIndex = 0;
    export let endIndex = 0;
    let images = new Array(
        endIndex - startIndex).fill(0).map((_, i) => 
        imageDir + "/frame_" + (i + startIndex) + ".jpg"
    );

    let videoPaused = false;

    const nextImage = () => {
        currentSlideItem = (currentSlideItem + 1) % images.length;
    }

    const prevImage = () => {
        if (currentSlideItem != 0){
            currentSlideItem = (currentSlideItem - 1) % images.length;
        } else {
            currentSlideItem = images.length - 1;
        }
    }
    
    const play = () => {
        if (videoPaused){
            videoPaused = false;
        }
    }
    
    const pause = () => {
        if (!videoPaused){
            videoPaused = true;
        }
    }
    const nextBtn = document.getElementById("next")
    for(let i = 0; i < 50; i++){
        videoPaused = true;
        nextBtn?.click();
    }
</script>

but again without luck.

What do I do wrong?

Here is the repo of the entire app

Thanks in advance.


Solution

  • The way I solved it is actually quite simple: We already have a function to show the next image so we can just call it repeatedly. JavaScript already has a (somewhat reliable) function to do this: setInterval

    In the end, my code looks like this and it works like a video player. You can adjust the speed by adjusting the delay in the setInterval function.

    <script lang="ts">
        import { slide } from "svelte/transition";
        export let imageDir: string;
        export let startIndex = 0;
        export let endIndex = 0;
    
        let images = new Array(endIndex - startIndex)
            .fill(0)
            .map((_, i) => imageDir + "/frame_" + (i + startIndex) + ".jpg");
    
        let currentSlideItem = 0;
        let videoPaused = false;
        console.log({ videoPaused });
    
        const nextImage = () => {
            currentSlideItem = (currentSlideItem + 1) % images.length;
        };
    
        const prevImage = () => {
            if (currentSlideItem != 0) {
                currentSlideItem = (currentSlideItem - 1) % images.length;
            } else {
                currentSlideItem = images.length - 1;
            }
        };
    
        const play = () => {
            videoPaused = false;
        };
    
        const pause = () => {
            videoPaused = true;
        };
    
        setInterval(() => {
            if (!videoPaused) {
                nextImage();
            }
        }, 50);
    </script>
    
    <main>
        {#each [images[currentSlideItem]] as image, index}
            <img
                transition:slide={{ delay: 200 }}
                src={image}
                alt="Blank"
                width="1080"
            />
        {/each}
        <div class="carousel-buttons">
            <button id="play" on:click={() => play()}>&#9199</button>
            <button id="pause" on:click={() => pause()}>&#9208</button>
            <button id="prev" on:click={() => prevImage()}>&#9194</button>
            <button id="next" on:click={() => nextImage()}>&#9193</button>
        </div>
    </main>
    

    Extra: In your github, you published the node_modules folder, which actually gives people cloning your repo a problem: They can't rum npm run dev. It is just as simple as removing the folder, but it does make for some inconvenience!