Javascript-window.scroll({behavior:'smooth'})在Safari中不起作用

时间:2018-07-08 07:25:10

标签: javascript safari

正如标题所述,它在Chrome上运行良好。但是在Safari中,它只是将页面设置为所需的顶部和左侧位置。这是预期的行为吗?有没有办法使其正常工作?

8 个答案:

答案 0 :(得分:4)

IE / Edge / Safari不完全支持

行为选项,因此您必须自己实现一些功能。我相信jQuery已经有了一些东西,但是如果您不使用jQuery,这是一个纯Javascript实现:

function SmoothVerticalScrolling(e, time, where) {
    var eTop = e.getBoundingClientRect().top;
    var eAmt = eTop / 100;
    var curTime = 0;
    while (curTime <= time) {
        window.setTimeout(SVS_B, curTime, eAmt, where);
        curTime += time / 100;
    }
}

function SVS_B(eAmt, where) {
    if(where == "center" || where == "")
        window.scrollBy(0, eAmt / 2);
    if (where == "top")
        window.scrollBy(0, eAmt);
}

如果需要水平滚动:

function SmoothHorizontalScrolling(e, time, amount, start) {
    var eAmt = amount / 100;
    var curTime = 0;
    var scrollCounter = 0;
    while (curTime <= time) {
        window.setTimeout(SHS_B, curTime, e, scrollCounter, eAmt, start);
        curTime += time / 100;
        scrollCounter++;
    }
}

function SHS_B(e, sc, eAmt, start) {
    e.scrollLeft = (eAmt * sc) + start;
}

一个示例调用为:

SmoothVerticalScrolling(myelement, 275, "center");

答案 1 :(得分:2)

最重要的解决方法是弥补Safari对行为的缺乏支持。

仍然有必要检测何时需要解决方法。

此小功能将检测浏览器是否支持平滑滚动。在Safari上返回false,在Chrome和Firefox上返回true:

// returns true if browser supports smooth scrolling
const supportsSmoothScrolling = () => {
  const body = document.body;
  const scrollSave = body.style.scrollBehavior;
  body.style.scrollBehavior = 'smooth';
  const hasSmooth = getComputedStyle(body).scrollBehavior === 'smooth';
  body.style.scrollBehavior = scrollSave;
  return hasSmooth;
};

答案 2 :(得分:2)

window.requestAnimationFrame 可用于执行平滑滚动。

为了平滑的垂直滚动,可以使用以下函数。请注意,水平滚动的实现方式大致相同。

/*
   @param time: the exact amount of time the scrolling will take (in milliseconds)
   @param pos: the y-position to scroll to (in pixels)
*/
function scrollToSmoothly(pos, time) {
    var currentPos = window.pageYOffset;
    var start = null;
    if(time == null) time = 500;
    pos = +pos, time = +time;
    window.requestAnimationFrame(function step(currentTime) {
        start = !start ? currentTime : start;
        var progress = currentTime - start;
        if (currentPos < pos) {
            window.scrollTo(0, ((pos - currentPos) * progress / time) + currentPos);
        } else {
            window.scrollTo(0, currentPos - ((currentPos - pos) * progress / time));
        }
        if (progress < time) {
            window.requestAnimationFrame(step);
        } else {
            window.scrollTo(0, pos);
        }
    });
}

演示:

/*
   @param time: the exact amount of time the scrolling will take (in milliseconds)
   @param pos: the y-position to scroll to (in pixels)
*/
function scrollToSmoothly(pos, time) {
    var currentPos = window.pageYOffset;
    var start = null;
    if(time == null) time = 500;
    pos = +pos, time = +time;
    window.requestAnimationFrame(function step(currentTime) {
        start = !start ? currentTime : start;
        var progress = currentTime - start;
        if (currentPos < pos) {
            window.scrollTo(0, ((pos - currentPos) * progress / time) + currentPos);
        } else {
            window.scrollTo(0, currentPos - ((currentPos - pos) * progress / time));
        }
        if (progress < time) {
            window.requestAnimationFrame(step);
        } else {
            window.scrollTo(0, pos);
        }
    });
}

document.querySelector('button').addEventListener('click', function(e){
  scrollToSmoothly(500, 1500);
});
html, body {
  height: 1000px;
}
<button>Scroll to y-position 500px in 1500ms</button>

另见:How to smoothly scroll to an element in pure JavaScript

答案 3 :(得分:1)

使用 smootscroll polyfill ,易于应用且轻量级依赖性: https://github.com/iamdustan/smoothscroll

通过npm或yarn安装后,将其添加到主 .js,.ts 文件(第一个执行)

import smoothscroll from 'smoothscroll-polyfill';
// or if linting/typescript complains
import * as smoothscroll from 'smoothscroll-polyfill';

// kick off the polyfill!
smoothscroll.polyfill();

答案 4 :(得分:0)

具有最平滑性能的解决方案,尤其是如果要合并缓动时,请使用requestAnimationFrame:

const requestAnimationFrame = window.requestAnimationFrame ||
          window.mozRequestAnimationFrame ||
          window.webkitRequestAnimationFrame ||
          window.msRequestAnimationFrame;

const step = (timestamp) => {
  window.scrollBy(
    0,
    1, // or whatever INTEGER you want (this controls the speed)
  );

  requestAnimationFrame(step);
};


requestAnimationFrame(step);

如果以后要取消滚动,则需要引用requestAnimationFrame(在使用requestAnimationFrame(step)的任何地方进行此操作):

this.myRequestAnimationFrame = requestAnimationFrame(step);

const cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame;
cancelAnimationFrame(this.myRequestAnimationFrame);

现在,如果要在滚动中使用缓动并在滚动操作之间超时,该怎么办?

创建一个包含60个元素的数组(requestAnimationFrame通常每秒调用60次。从技术上讲,浏览器的刷新率是多少,但最常见的是60。)我们将非线性填充此数组,然后使用这些数字来控制在requestAnimationFrame的每个步骤中滚动多少:

let easingPoints = new Array(60).fill(0)

选择缓动功能。假设我们正在进行三次缓和:

function easeCubicOut(t) {
    return --t * t * t + 1;
}

创建一个虚拟数组,并通过缓动函数通过管道填充数据。您一会儿就会明白为什么我们需要这样做:

    // easing function will take care of decrementing t at each call (too lazy to test it at the moment. If it doesn't, just pass it a decrementing value at each call)
    let t = 60;
    const dummyPoints = new Array(60).fill(0).map(()=> easeCubicOut(t));
    const dummyPointsSum = dummyPoints.reduce((a, el) => {
                                a += el;
                               return a;
                           }, 0);

借助每个dummyPoint比率对dummyPointsSum映射easingPoints:

    easingPoints = easingPoints.map((el, i) => {
        return Math.round(MY_SCROLL_DISTANCE * dummyPoints[i] / dummyPointsSum);
    });

在您的滚动功能中,我们将进行一些调整:

     const requestAnimationFrame = window.requestAnimationFrame ||
              window.mozRequestAnimationFrame ||
              window.webkitRequestAnimationFrame ||
              window.msRequestAnimationFrame;

     let i = 0;
     const step = (timestamp) => {
       window.scrollBy(
         0,
         easingPoints[i],
       );


        if (++i === 60) {
                i = 0;
                return setTimeout(() => {
                  this.myRequestAnimationFrame = requestAnimationFrame(step);
                }, YOUR_TIMEOUT_HERE);
        }
      };


      this.myRequestAnimationFrame = requestAnimationFrame(step);

答案 5 :(得分:0)

结合George Danielterrymorse的答案,以下内容可用于使用本机JavaScript的所有浏览器支持。

Chrome和Firefox支持CSS,scroll-behavior: smooth;用于不支持此属性的浏览器,我们可以在下面添加。

HTML:

<a onclick="scrollToSection(event)" href="#section">
    Redirect On section
</a>
  
<section id="section">
  Section Content
</section>

CSS:

body {
  scroll-behavior: smooth;
}

JavaScript:

function scrollToSection(event) {
  if (supportsSmoothScrolling()) {
    return;
  }
  event.preventDefault();
  const scrollToElem = document.getElementById("section");
  SmoothVerticalScrolling(scrollToElem, 300, "top");
}

function supportsSmoothScrolling() {
  const body = document.body;
  const scrollSave = body.style.scrollBehavior;
  body.style.scrollBehavior = 'smooth';
  const hasSmooth = getComputedStyle(body).scrollBehavior === 'smooth';
  body.style.scrollBehavior = scrollSave;
  return hasSmooth;
};
 
function SmoothVerticalScrolling(element, time, position) {
  var eTop = element.getBoundingClientRect().top;
  var eAmt = eTop / 100;
  var curTime = 0;
  while (curTime <= time) {
    window.setTimeout(SVS_B, curTime, eAmt, position);
    curTime += time / 100;
  }
}

function SVS_B(eAmt, position) {
  if (position == "center" || position == "")
  window.scrollBy(0, eAmt / 2);
  if (position == "top")
  window.scrollBy(0, eAmt);
}

答案 6 :(得分:0)

感谢 T.Dayya,我结合了关于该主题的几个答案,这里是 ts 模块 扩展函数 scrollSmoothIntoView。

    export default {}
    
    declare global {
    
        interface Element {
            scrollSmoothIntoView(): void;
        }
    }
    
    Element.prototype.scrollSmoothIntoView = function()
    {
        const t = 45;
        const tstep = 6.425/t;
        const dummyPoints = new Array(t).fill(0).map((t, i) => circ(i * tstep));
        const dummyPointsSum = dummyPoints.reduce((a, el) => { a += el; return a;}, 0);
    
        const _window: any = window;
        const _elem: any = getScrollParent(this);
    
        const scroll_distance: any = (this as any).offsetTop - (!_elem.parentElement ? _window.scrollY : 0);
    
        let easingPoints = new Array(t).fill(0)
        easingPoints = easingPoints.map((el, i) => {
            return Math.round(scroll_distance * dummyPoints[i] / dummyPointsSum);
        });
    
        const requestAnimationFrame = _window.requestAnimationFrame ||
            _window.mozRequestAnimationFrame ||
            _window.webkitRequestAnimationFrame ||
            _window.msRequestAnimationFrame;
    
        let i = 0;    
        const step = (timestamp:any) => {
            _elem.scrollBy(0, easingPoints[i]);
    
            if (++i < t)
                setTimeout(() => { requestAnimationFrame(step) }, 2);
        };
    
        window.requestAnimationFrame(()=>requestAnimationFrame(step));
    }
    
    function getScrollParent(element: any, includeHidden?: any):any {
        var style = getComputedStyle(element);
        var excludeStaticParent = style.position === "absolute";
        var overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/;
    
        if (style.position === "fixed") return document.body;
        for (var parent = element; (parent = parent.parentElement);) {
            style = getComputedStyle(parent);
            if (excludeStaticParent && style.position === "static") {
                continue;
            }
            if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX)) return parent;
        }
    
        return document.body;
    }
    
    function circ(t:any) {
        return 1+Math.cos(3+t);
    }

使用 html_element.scrollSmoothIntoView()。

答案 7 :(得分:-1)

一个适用于Safari的简单jQuery修复程序:

$('a[href*="#"]').not('[href="#"]').not('[href="#0"]').click(function (t) {
    if (location.pathname.replace(/^\//, "") == this.pathname.replace(/^\//, "") && location.hostname == this.hostname) {
        var e = $(this.hash);
        e = e.length ? e : $("[name=" + this.hash.slice(1) + "]"), e.length && (t.preventDefault(), $("html, body").animate({
            scrollTop: e.offset().top
        }, 600, function () {
            var t = $(e);
            if (t.focus(), t.is(":focus")) return !1;
            t.attr("tabindex", "-1"), t.focus()
        }))
    }
});