完整代码及动画运行效果:https://codepen.io/TristanBLG...
代码中实现了滚动页面至相应元素的功能,有疑问的地方在于代码利用Math.ceil
、Math.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
});
})
})
1
learningman 2022-01-27 22:11:27 +08:00
那你考虑浮点误差不(
差不多就可以了 |
2
thedrwu 2022-01-27 22:17:08 +08:00 via Android
这是要登月吗
|
3
manyfreebug OP |
4
iNaru 2022-01-27 22:30:22 +08:00
不需要取整等操作,有时 0.1 就是一个像素的误差。
|
5
manyfreebug OP @iNaru 临界条件怎么判断?if(Math.round(window.pageYOffset === destinationOffsetToScroll)) , 如果不去整, 可能永远没有相等的机会
|
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 内部也会帮你取整。 |
7
iNaru 2022-01-27 23:13:26 +08:00 1
@manyfreebug 取差值的绝对值是否在范围内
|
8
manyfreebug OP @iNaru 这个绝对值的范围取在多少合适? Math.abs(destinationOffsetToScroll - window.pageYOffset) < 1 ? 看运行效果似乎是可以的, 但怎么可以直观地看出差距在 1px 之内是可以的. 没有可能是 1.几甚至更高才行吗?
|
9
hallDrawnel 2022-01-27 23:42:51 +08:00
不用太纠结,因为除了你的计算,渲染也是浮点数计算的。感官 OK 的前提下用最简单的方式最好。
|
10
manyfreebug OP @hallDrawnel 不纠结的话就好了 , 直接取个 30 :) 现在的问题是尽可能地精确点
|
11
jinliming2 2022-01-28 01:48:09 +08:00 via iPhone 1
round 、floor 、ceil 的使用场景:
首先,你期望得到一个整数(比如在针对物理像素点做优化计算的时候,必须使用整数)才会要使用这些舍入方法。 round 就是四舍五入,结果可能比原值大,也可能比原值小,但一定是最接近原值的。通常变化的值就用这个来舍入。 而 ceil 和 floor ,一个是向上取整,一个是向下取整。通常用于边界条件的时候。比如容器大小固定,那么内容就得用 floor 来向下取整,不然就可能会把容器撑开或者折断截断导致布局出错。类似的,如果容器大小不固定,那就要考虑使用 ceil 来进行向上取整,原因和用 floor 是是一样的。 如果你不是要手动根据密度之类的东西去针对物理像素优化的话,那完全没必要舍入,直接用小数,由浏览器渲染引擎来决定与物理像素的映射。 另外,滚动的边界条件浏览器应该是有处理的,滚动到头得到的事件参数值和取到的目标滚动参数应该是一样的。 |
12
libook 2022-01-28 10:17:46 +08:00 1
可以取到显示内容的总高度,根据 viewport 倍率来判断精确到一个像素需要小数点后精确几位,然后再动态决定如何处理数值。
为什么一定要取近似整数,浮点数不可以吗? |
13
manyfreebug OP @libook 浮点数不有误差嘛 :)
|