利用 Math.ceil 等近似方法是否会影响该滚动动画的精确性? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a Javascript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
Javascript 权威指南第 5 版
Closure: The Definitive Guide
manyfreebug
V2EX    Javascript

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

  •  
  •   manyfreebug 2022-01-27 22:07:02 +08:00 2451 次点击
    这是一个创建于 1358 天前的主题,其中的信息可能已经有所发展或是发生改变。

    完整代码及动画运行效果: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 }); }) }) 
    13 条回复    2022-01-28 16:14:21 +08:00
    learningman
        1
    learningman  
       2022-01-27 22:11:27 +08:00
    那你考虑浮点误差不(
    差不多就可以了
    thedrwu
        2
    thedrwu  
       2022-01-27 22:17:08 +08:00 via Android
    这是要登月吗
    manyfreebug
        3
    manyfreebug  
    OP
       2022-01-27 22:28:30 +08:00
    没有要登月,但直观上上不好接受 :(
    @thedrwu
    @learningman
    iNaru
        4
    iNaru  
       2022-01-27 22:30:22 +08:00
    不需要取整等操作,有时 0.1 就是一个像素的误差。
    manyfreebug
        5
    manyfreebug  
    OP
       2022-01-27 22:44:58 +08:00
    @iNaru 临界条件怎么判断?if(Math.round(window.pageYOffset === destinationOffsetToScroll)) , 如果不去整, 可能永远没有相等的机会
    darkengine
        6
    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
        7
    iNaru  
       2022-01-27 23:13:26 +08:00   1
    @manyfreebug 取差值的绝对值是否在范围内
    manyfreebug
        8
    manyfreebug  
    OP
       2022-01-27 23:36:18 +08:00
    @iNaru 这个绝对值的范围取在多少合适? Math.abs(destinationOffsetToScroll - window.pageYOffset) < 1 ? 看运行效果似乎是可以的, 但怎么可以直观地看出差距在 1px 之内是可以的. 没有可能是 1.几甚至更高才行吗?
    hallDrawnel
        9
    hallDrawnel  
       2022-01-27 23:42:51 +08:00
    不用太纠结,因为除了你的计算,渲染也是浮点数计算的。感官 OK 的前提下用最简单的方式最好。
    manyfreebug
        10
    manyfreebug  
    OP
       2022-01-27 23:58:33 +08:00
    @hallDrawnel 不纠结的话就好了 , 直接取个 30 :) 现在的问题是尽可能地精确点
    jinliming2
        11
    jinliming2  
       2022-01-28 01:48:09 +08:00 via iPhone   1
    round 、floor 、ceil 的使用场景:
    首先,你期望得到一个整数(比如在针对物理像素点做优化计算的时候,必须使用整数)才会要使用这些舍入方法。
    round 就是四舍五入,结果可能比原值大,也可能比原值小,但一定是最接近原值的。通常变化的值就用这个来舍入。
    而 ceil 和 floor ,一个是向上取整,一个是向下取整。通常用于边界条件的时候。比如容器大小固定,那么内容就得用 floor 来向下取整,不然就可能会把容器撑开或者折断截断导致布局出错。类似的,如果容器大小不固定,那就要考虑使用 ceil 来进行向上取整,原因和用 floor 是是一样的。

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

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

    为什么一定要取近似整数,浮点数不可以吗?
    manyfreebug
        13
    manyfreebug  
    OP
       2022-01-28 16:14:21 +08:00
    @libook 浮点数不有误差嘛 :)
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2365 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 16:02 PVG 00:02 LAX 09:02 JFK 12:02
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86