Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Allow programmatic zooming / default zoom value #3908

Open
jaredgei opened this issue Oct 13, 2024 · 3 comments
Open

Feature Request: Allow programmatic zooming / default zoom value #3908

jaredgei opened this issue Oct 13, 2024 · 3 comments
Labels

Comments

@jaredgei
Copy link

Currently there is no way to programmatically control ZoomPlugin. There are a number of use cases where this would be helpful.
• For long audio files, it would make sense to allow a default zoom value (i.e. show 30s of audio on screen) because a super long horizontally squished waveform is not particularly useful. Similar to the how the minimap plugin is working but allowing us to set a custom zoom level.
• Mousewheel scrolling isn't always obvious for users, so having the option to create a separate slider would be really nice if we could control the zoom through code.
• For those of us using regions plugin, it'd be awesome to be able to zoom into a specific region when the user creates or interacts with it.
• Having the option to programatically zoom into a specific part of the track to bring the user's attention to it has a ton of benefits for a bunch of different uses cases.

My suggestion is
• Add an option to zoomPlugin something like intialZoom
• Add a function to zoomplugin zoomTo that takes two timestamps start, end, and sets the zoom appropriately..

Thanks so much for all your hard work on this project!

@jaredgei jaredgei added the bug label Oct 13, 2024
@katspaugh katspaugh removed the bug label Oct 15, 2024
@katspaugh
Copy link
Owner

Doesn’t the zoom method already cover it?

https://wavesurfer.xyz/docs/classes/wavesurfer.default#zoom

@jaredgei
Copy link
Author

Ah I wasn't aware of this -- it's a big help for the first suggestion.
It seems like with some math I could probably handle the second suggestion too (zooming into a region) too using zoom, settime, and innerWidth, so that'll work for me for now! I think it'd still be nice to have an option to zoom to a startTime, endTime just for easier use though.

Also it'd be nice to have an option for the zoom method to animate so the user can tell what is changing instead of just a sudden waveform change.

Thank you for your help though!

@MrEdwards007
Copy link

I also needed a default or rather minimum zoom value for each waveform. I handled this by calculating the minimum pixels per second from the duration of the audio and the size of the container. What you see on the screen are the results, which work perfectly for me. I set the minimum just 10 pixels below that initial value, to prevent the scrollbar from automatically being activated and to give the user room to adjust the scrollbar to a minimum, below the initial value.

This isn't all of the code but it should be enough to follow what I did. Note, I show what the current value is in the image but once I'm finished with my coding, I am going to hide the label showing the current zoom value, as its for testing purposes only. This enables a per-waveform default zoom and also enables a consistent responsive zoom experience.

Notice that the length of the audio guides the process. My audio files can be less than one second and as long as 30 seconds in length. The responsiveness of the zoom is consistent across all of the waveforms.

The instance being passed is ' wavesurfer{idx} ' and the others are the selectors for the UI elements. For the resize event, I had to push all of the instances and loop through them.

It took me a little while to make this work consistently across waveforms and hopefully this works for you too.

image


            const waveSurferInstances = [];
            


                // Update on window resize
                window.addEventListener('resize', () => {
                    waveSurferInstances.forEach(({ instance, container, sliderId, labelId }) => {
                        updateMinPxPerSec(instance, container, sliderId, labelId);
                    });
                });


            function updateMinPxPerSec(instance, containerSelector, sliderId, labelId) {
                const container = document.querySelector(containerSelector);
                if (!container) return;

                const containerWidth = container.offsetWidth;
                const duration = instance.getDuration();
                if (duration > 0) {
                    const minPxPerSec = Math.max(10, containerWidth / duration);
                    const controlMinimum = Math.max(0, minPxPerSec - 10);

                    // Start with a zoom level slightly below the minimum to prevent the scrollbar initially
                    // Ensure initialZoomLevel is always valid
                    const initialZoomLevel = Math.max(0, controlMinimum - 1); // Minimum zoom level of 0

                    // Apply the initial zoom level and update UI elements
                    instance.zoom(initialZoomLevel);
                    document.querySelector(sliderId).value = initialZoomLevel.toFixed(2); // Slider value
                    document.querySelector(sliderId).min = controlMinimum.toFixed(2); // Slider minimum
                    document.querySelector(labelId).textContent = initialZoomLevel.toFixed(2); // Label value

                    //console.log('Initialized with minPxPerSec:', initialZoomLevel.toFixed(2), 'Control minimum:', controlMinimum.toFixed(2));
                }
            }



                const wavesurfer{idx} = WaveSurfer.create({{
                    container: '#waveform-{idx}',
                    waveColor: '#1e90ff',
                    progressColor: '#00ff00',
                    height: 100,
                    hideScrollbar: false,
                    autoCenter: true
                    
                }});


                waveSurferInstances.push({{
                    instance: wavesurfer{idx},
                    container: '#waveform-{idx}',
                    sliderId: '#zoom-slider-{idx}',
                    labelId: '#zoom-level-{idx}'
                }});


                wavesurfer{idx}.on('ready', () => {{
                    const spinner = document.getElementById('spinner-{idx}');
                    updateMinPxPerSec(wavesurfer{idx}, '#waveform-{idx}', '#zoom-slider-{idx}', '#zoom-level-{idx}');

                    // Re-enable the slider.  
                    // Addresses slider being active during a long load and user playing with the slider before the audio is loaded. 
                    changeSliderState({idx}, false)

                    if (spinner) {{
                        spinner.style.display = 'none'; // Ensure the spinner disappears
                    }} else {{
                        console.error(`Spinner with ID spinner-{idx} not found.`);
                    }}                   

                }});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants