利用 Math.ceil 等近似方法是否会影响该滚动动画的精确性?

2022-01-27 22:07:02 +08:00
 manyfreebug

完整代码及动画运行效果:https://codepen.io/TristanBLG...

代码中实现了滚动页面至相应元素的功能,有疑问的地方在于代码利用Math.ceilMath.round做了一些近似的处理,这是否会影响到页面滚动到对应元素的精确性?涉及到小数总是难以判断.

比如使用Math.ceil处理后会比原始值大 0.x,如果要求动画精确一点,这 0.x 是这个例子中不需要考虑的吗? 为什么有些地方用Math.ceil?而有些地方又用了Math.round?

做了近似处理的地方: 下面的这两处代码作了近似处理,可以知道大致知道它们加起来近似了多少吗? 0.几? 会不会近似处理后离精准位置多了好几 pixel,而不仅仅是 0.几?

window.scroll(0, Math.ceil((time * (destinationOffsetToScroll - start)) + start))
if(time >= 1 || Math.round(window.pageYOffset) === destinationOffsetToScroll) {}

完整 JavaScript 代码:

const scrollTo = function({target, duration = 200, callback} = {}){
  if(!target){
    console.error('scrollTo() => You must specify a target.')
    return false
  }
  const targetHref                  = target.getAttribute('href').replace( /^#/ , '')
  const destination                 = document.getElementById(targetHref)
  const start                       = window.pageYOffset
  const startTime                   = 'now' in window.performance ? performance.now() : new Date().getTime()
  const documentHeight              = Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight)
  const windowHeight                = window.innerHeight || document.documentElement.clientHeight || document.getElementsByTagName('body')[0].clientHeight
  const destinationOffset           = typeof destination === 'number' ? destination : destination.offsetTop
  const destinationOffsetToScroll   = Math.round(documentHeight - destinationOffset < windowHeight ? documentHeight - windowHeight : destinationOffset)

  if('requestAnimationFrame' in window === false) {
      window.scroll(0, destinationOffsetToScroll)
      if(callback) {
          callback()
      }
      return
  }
  
  const scroll = function() {
    const now   = 'now' in window.performance ? performance.now() : new Date().getTime()
    const time  = Math.min(1, ((now - startTime) / duration))
    
    window.scroll(0, Math.ceil((time * (destinationOffsetToScroll - start)) + start))
    
    if(time >= 1 || Math.round(window.pageYOffset) === destinationOffsetToScroll) {
      if(callback) {
        callback()
      }
      return
    }

    requestAnimationFrame(scroll)
  }

  requestAnimationFrame(scroll)
};

const navLink = Array.from(document.querySelectorAll('[href^="#"]'))
  navLink.forEach(el => {
    el.addEventListener('click', function(ev) {
      ev.preventDefault()
      ev.stopPropagation()
      scrollTo({
        duration: 300,
        target: ev.target
      });
    })
})

1970 次点击
所在节点    JavaScript
13 条回复
learningman
2022-01-27 22:11:27 +08:00
那你考虑浮点误差不(
差不多就可以了
thedrwu
2022-01-27 22:17:08 +08:00
这是要登月吗
manyfreebug
2022-01-27 22:28:30 +08:00
没有要登月,但直观上上不好接受 :(
@thedrwu
@learningman
iNaru
2022-01-27 22:30:22 +08:00
不需要取整等操作,有时 0.1 就是一个像素的误差。
manyfreebug
2022-01-27 22:44:58 +08:00
@iNaru 临界条件怎么判断?if(Math.round(window.pageYOffset === destinationOffsetToScroll)) , 如果不去整, 可能永远没有相等的机会
darkengine
2022-01-27 22:57:36 +08:00
window.scroll(x-coord, y-coord)

Parameters
x-coord is the pixel along the horizontal axis of the document that you want displayed in the upper left.
y-coord is the pixel along the vertical axis of the document that you want displayed in the upper left.

根据文档这两个参数就是像素值,那么你这里再精确到小数点之后多少位有啥用,你不取整 API 内部也会帮你取整。
iNaru
2022-01-27 23:13:26 +08:00
@manyfreebug 取差值的绝对值是否在范围内
manyfreebug
2022-01-27 23:36:18 +08:00
@iNaru 这个绝对值的范围取在多少合适? Math.abs(destinationOffsetToScroll - window.pageYOffset) < 1 ? 看运行效果似乎是可以的, 但怎么可以直观地看出差距在 1px 之内是可以的. 没有可能是 1.几甚至更高才行吗?
hallDrawnel
2022-01-27 23:42:51 +08:00
不用太纠结,因为除了你的计算,渲染也是浮点数计算的。感官 OK 的前提下用最简单的方式最好。
manyfreebug
2022-01-27 23:58:33 +08:00
@hallDrawnel 不纠结的话就好了 , 直接取个 30 :) 现在的问题是尽可能地精确点
jinliming2
2022-01-28 01:48:09 +08:00
round 、floor 、ceil 的使用场景:
首先,你期望得到一个整数(比如在针对物理像素点做优化计算的时候,必须使用整数)才会要使用这些舍入方法。
round 就是四舍五入,结果可能比原值大,也可能比原值小,但一定是最接近原值的。通常变化的值就用这个来舍入。
而 ceil 和 floor ,一个是向上取整,一个是向下取整。通常用于边界条件的时候。比如容器大小固定,那么内容就得用 floor 来向下取整,不然就可能会把容器撑开或者折断截断导致布局出错。类似的,如果容器大小不固定,那就要考虑使用 ceil 来进行向上取整,原因和用 floor 是是一样的。

如果你不是要手动根据密度之类的东西去针对物理像素优化的话,那完全没必要舍入,直接用小数,由浏览器渲染引擎来决定与物理像素的映射。

另外,滚动的边界条件浏览器应该是有处理的,滚动到头得到的事件参数值和取到的目标滚动参数应该是一样的。
libook
2022-01-28 10:17:46 +08:00
可以取到显示内容的总高度,根据 viewport 倍率来判断精确到一个像素需要小数点后精确几位,然后再动态决定如何处理数值。

为什么一定要取近似整数,浮点数不可以吗?
manyfreebug
2022-01-28 16:14:21 +08:00
@libook 浮点数不有误差嘛 :)

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/831019

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX