uikit / uikit

A lightweight and modular front-end framework for developing fast and powerful web interfaces
http://getuikit.com
MIT License
18.32k stars 2.32k forks source link

Make uk-scroll speed customizable (again) #4442

Open simonflick opened 3 years ago

simonflick commented 3 years ago

Summary

What feature are you requesting? What problem does it solve?

The timing function of uk-scroll is hard coded but some projects have tight requirements and need a different speed. This was already implemented in UIkit 2. It could be implemented by adding a parameter that defines a speed multiplier (default 1.0) or the total time the scroll takes in milliseconds:

uk-scroll="speed: 1.4" or uk-scroll="duration: 750" (analogous to UIkit 2)

Priority

On a scale of 1-5, how important is this feature to you? Please explain.

The current speed doesn't hurt the usability so this change is only cosmetic -> 1

jbjhjm commented 3 years ago

Needed this feature so I built a custom component based on the existing uk-scroll. For anyone needing this, or as a reference to ship this in a future version. Had to modify utils/viewport:scrollIntoView, but as the new variable defaults to 1, there are no breaking changes.

Usage: uk-velocityscroll="offset:200; velocity:0.4" -- will throttle the speed to 40%.

/*
    UIkit 3 has no speed control for animated scroll ATM.
    This is a custom implementation allowing to pass a velocity multiplier.
    Code from util/viewport.js
    added velocity variable, divide getDuration result by it
*/
function scrollIntoView(element, {offset: offsetBy = 0, velocity = 1} = {}) {

    const parents = UIkit.util.scrollParents(element);
    let diff = 0;
    return parents.reduce((fn, scrollElement, i) => {

        const {scrollTop, scrollHeight} = scrollElement;
        const maxScroll = scrollHeight - UIkit.util.getViewportClientHeight(scrollElement);

        let top = Math.ceil(
            UIkit.util.offset(parents[i - 1] || element).top
            - UIkit.util.offset(UIkit.util.getViewport(scrollElement)).top
            - offsetBy
            + diff
            + scrollTop
        );

        if (top > maxScroll) {
            diff = top - maxScroll;
            top = maxScroll;
        } else {
            diff = 0;
        }

        return () => scrollTo(scrollElement, top - scrollTop).then(fn);

    }, () => Promise.resolve())();

    function scrollTo(element, top) {
        return new Promise(resolve => {

            const scroll = element.scrollTop;
            const duration = getDuration(Math.abs(top)) / velocity;
            const start = Date.now();

            (function step() {

                const percent = ease(UIkit.util.clamp((Date.now() - start) / duration));

                UIkit.util.scrollTop(element, scroll + top * percent);

                // scroll more if we have not reached our destination
                if (percent !== 1) {
                    requestAnimationFrame(step);
                } else {
                    resolve();
                }

            })();
        });
    }

    function getDuration(dist) {
        return 40 * Math.pow(dist, .375);
    }

    function ease(k) {
        return 0.5 * (1 - Math.cos(Math.PI * k));
    }

}

document.addEventListener('readystatechange',function(){
    UIkit.component('velocityscroll', {
        /*
        Code from core/scroll.js
        added velocity variable and pass it to scrollIntoView
        */
        props: {
            offset: Number,
            velocity: Number
        },
        data: {
            offset: 0,
            velocity: 1,
        },

        methods: {
            scrollTo(el) {
                if (UIkit.util.trigger(this.$el, 'beforescroll', [this, el])) {
                    scrollIntoView(el, {offset: this.offset, velocity: this.velocity}).then(() =>
                        UIkit.util.trigger(this.$el, 'scrolled', [this, el])
                    );
                }
            }
        },

        events: {
            click(e) {
                if (e.defaultPrevented) return;
                e.preventDefault();
                const targetId = `${escape(decodeURIComponent((this.$el.hash || '').substr(1)))}`;
                this.scrollTo(document.getElementById(targetId));
            }
        }
    })
});
Orgoth commented 4 months ago

It's very strange that this feature was already available.
Yootheme referred here because I need the option to change the delay of the smooth scrolling.
https://yootheme.com/support/question/141440#answer-452580 The option of jbjhjm is unfortunately not an option, because Yootheme Pro does not support this and always sets uk-scroll as default.

But I also don't want to add a separate JavaScript to try to block the uk-scroll feature somehow, just because the developers of UIKit thought to remove the feature is a good idea and Yootheme Pro doesn't want to offer an option to simply disable uk-scroll completely. 🤦

In my case, I really need it, since it is hurting the usability.

@janschoenherr @saschadube