flyingant / react-scroll-to-component

smooth scrolling to react component via reference.
https://flyingant.github.io/react-scroll-to-component/
168 stars 27 forks source link

Allow easing functions #3

Open Noitidart opened 7 years ago

Noitidart commented 7 years ago

Please allow easing functions. For instance I use this set:

function B1(t) { return t*t*t }
function B2(t) { return 3*t*t*(1-t) }
function B3(t) { return 3*t*(1-t)*(1-t) }
function B4(t) { return (1-t)*(1-t)*(1-t) }
export function getBezier(percent,x2,y2,x3,y3) {
  var pos = {x:0, y:0};
  pos.x = 0*B1(percent) + x2*B2(percent) + x3*B3(percent) + 1*B4(percent);
  pos.y = 0*B1(percent) + y2*B2(percent) + y3*B3(percent) + 1*B4(percent);
  return pos;
}

Then I calculate timing and values. I actually had my own scroll function, but came to yours and loved it. Just missing these easing functions.

This was my scroll to function:

export const scrollIntoView = async function(domel, {duration=500, align='top', behavior='smooth', needed=0.5, smooth='ease'}={}) {
    // domel can be element or string, if string starts with space, dot, hash then it users querySelector
    // only does Y right now
    // duration - ms
    // align - top, none
    // bheavior - smooth, instant
    // needed - is the if needed part. portion of the domel that should be visible. with respect to align. its a decimal percent. so align=top and portion=0.5 means top 50% shoudl be visible at least. if it is not then scrolling is done
    // smooth - css smoothing - accepts: ease, linear, ease-in, ease-out, ease-in-out, or array of 4 numbers

    if (!domel) throw new Error('scrollIntoView: domel does not exist');

    if (typeof(domel) == 'string') {
        let selector = domel;
        let method = [' ', '.', '#'].includes(domel[0]) ? 'querySelector' : 'getElementById';
        domel = await retry(async () => {
            let domel = document[method](selector);
            if (!domel) throw new Error('retry');
            return domel;
        }, { interval:50, sec:1 });
    }

    if (!Array.isArray(smooth)) {
        let special_smooth = {
            ease: [.25,.1,.25,1],
            linear: [0,0,1,1],
            'ease-in': [.42,0,1,1],
            'ease-out': [0,0,.58,1],
            'ease-in-out': [.42,0,.58,1]
        };
        smooth = special_smooth[smooth];
    }

    let scroll = getScroll();
    let domel_ytop = parseInt(getElementScrollPosition(domel).y);

    if (align === 'top') {
        if (behavior === 'smooth') {
            let end = domel_ytop;
            let start = scroll.y;

            let steps = [];
            let lasttime = -10000000;
            const MIN_TIME_GAP = 10; // ms
            for (let i=0; i<101; i++) {
                let scale = getBezier((100-i)/100, ...smooth);
                let cur_val = Math.round((scale.y * (end - start)) + start);

                if (!steps.length || steps[steps.length-1].val !== cur_val) {
                    let cur_time = Math.round(scale.x * duration);
                    if (cur_time - lasttime >= MIN_TIME_GAP) {
                        lasttime = cur_time;
                        steps.push({time:cur_time,val:cur_val});
                    }
                }
            }
            // console.log('steps:', steps);

            // let timeouts = [];
            steps.forEach(step => {
                // timeouts.push( setTimeout(()=>window.scrollTo(scroll.x, step.val), step.time) );
                setTimeout(()=>window.scrollTo(scroll.x, step.val), step.time);
            });
        } else {
            // behavior == 'instant'
            window.scrollTo(scroll.x, domel_ytop);
        }
    }
}

Other features in my function that might be cool here are:

flyingant commented 7 years ago

Actually I already added the ease . Could you check that with the latest demo page?

@Noitidart Thanks for your advices. I will try to imeplement it later but let's make sure the function is simple and easy to use. I will backlog this issue now.

Noitidart commented 7 years ago

Thanks @flyingant ! The equations above allow us to use it just like we use CSS transitions. So it offers not only ease, but familiarity. We can specify: linear, ease, ease-in, ease-out, ease-in-out. Or supply an array, this is the same way you provide a CSS cubic-bezier. So we can use regular CSS tools in the browser to get a easing function like http://cubic-bezier.com/#.17,.67,.83,.67 - and I can plug this (.17,.67,.83,.67) straight into the function as an array.