简体   繁体   English

动画回调在错误的时间调用

[英]Animation callback calling at wrong time

I am trying to make a bounce effect on my custom scroller. 我试图在自定义滚动条上产生反弹效果。 (I'm scrolling using translate3d .) I was finally able to implement it, but the only problem now is, if you keep on scrolling out of bounds (try double scrolling and just keep doing it till it gets stuck), then it will stay out of bounds until you scroll back down (or up, depending on where it's stuck). (我正在使用translate3d滚动。)我终于能够实现它,但是现在唯一的问题是,如果您继续滚动越界(尝试两次滚动并一直进行直到它卡住),那么它将保持界限,直到您向下滚动(或向上滚动,具体取决于其卡住的位置)。

Like this: http://imgur.com/SHAe1E4 (for some reason, stackoverflow isn't letting me to add the gif.) code: JSFiddle 像这样: http : //imgur.com/SHAe1E4 (由于某种原因,stackoverflow不允许我添加gif。)代码: JSFiddle

Question: 题:

How can I make a scrolling bounce effect using JavaScript and CSS? 如何使用JavaScript和CSS产生滚动跳动效果?

The rest is optional to read. 其余的可选阅读。

Reason why not working: 不起作用的原因:

I think the reason it's doing that is: there's an animation callback event. 我认为这样做的原因是:有一个动画回调事件。 So if the animation doesn't happen, ie, the transform doesn't change, then the animation callback will not fire. 因此,如果动画没有发生,即变换没有变化,那么动画回调将不会触发。

So when it's scrolled all the way to the top or bottom, and you scroll again, (before the animation completes, so the animation callback doesn't get called, because the transform value didn't change) the animation callback will not get called, and therefore it won't scroll (bounce) back to bounds. 因此,当它一直滚动到顶部或底部时,然后再次滚动,(在动画完成之前,因此不会调用动画回调,因为变换值没有变化),不会调用动画回调,因此它不会滚动(反弹)到边界。

What I tried: 我试过的

I tried checking to see if the scroll position changed, and if it didn't call the animation callback function, but it doesn't give the desired results. 我尝试检查滚动位置是否发生了变化,以及它是否未调用动画回调函数,但未给出所需的结果。 When you try scrolling out of bounds a lot at a time, it gives a 'stuttering' effect, and doesn't scroll all the way out of bounds, it stops midway and scrolls back. 当您尝试一次大量滚动到边界之外时,它会产生“口吃”的效果,并且不会一直滚动到边界之外,而是会中途停止并向后滚动。 Like this: http://imgur.com/a/xHYle JSFiddle 像这样: http : //imgur.com/a/xHYle JSFiddle

I also tried changing the transform value by 0.1 whenever the scrolling position didn't change, therefore the animation callback will get called. 每当滚动位置不变时,我也尝试将转换值更改为0.1,因此将调用动画回调。 The problem with that is, if you scroll out of bounds, it waits aa half a second to bounce back. 这样做的问题是,如果您滚动到界外,它会等待半秒钟才能弹回。
So I set the animation duration to a shorter time when the scrolling position didn't change, but if you scroll repeatedly out of bounds, ie, you keep on scrolling when it's out of bounds, up or down, then it waits a second, then bounces back. 因此,在滚动位置不变的情况下,我将动画持续时间设置为较短的时间,但是如果您反复滚动超出界限,即,当滚动超出界限时继续滚动(向上或向下),则它会等待一秒钟,然后反弹。 JSFiddle JSFiddle

 console.clear(); var innerWrapper = document.getElementById('innerWrapper'); var scrollBar = document.getElementById('scrollbar'); var scrollBarThumb = scrollBar.firstElementChild; var scrollPosition = 0; var scrolledToBottom = innerWrapper.scrollHeight - innerWrapper.parentElement.offsetHeight; scrollBarThumb.style.height = (innerWrapper.offsetHeight * innerWrapper.offsetHeight / innerWrapper.scrollHeight) + 'px'; innerWrapper.addEventListener('mousewheel', handleScroll); innerWrapper.addEventListener('DOMMouseScroll', handleScroll); innerWrapper.addEventListener('transitionend', bounceBack); innerWrapper.style.transform = 'translate3d(0px, 0px, 0px)'; function handleScroll(e) { // Prevent parents from scrolling e.preventDefault(); var direction = (e.detail < 0 || e.wheelDelta > 0) ? 1 : -1; // 1 = scroll down, -1 = scroll scrollPosition += direction * 100; // Cannot use `deltaY`, because not all browsers support it. scrollPosition = clamp(scrollPosition, -scrolledToBottom - 40, 40); // 40 = bounce amount var scrollThumbPosition = (scrollPosition * scrollBarThumb.offsetHeight / innerWrapper.parentElement.offsetHeight); scrollThumbPosition = clamp(scrollThumbPosition, -scrolledToBottom, 0); innerWrapper.style.transform = 'translate3d(0px, ' + scrollPosition + 'px, 0px)'; scrollBarThumb.style.transform = 'translate3d(0px, ' + -scrollThumbPosition + 'px, 0px)'; } function bounceBack(e) { // Scrolling stopped too high? if (scrollPosition > 0) { scrollPosition = 0; // Scrolling stopped too low? } else if (scrollPosition < -scrolledToBottom) { scrollPosition = -scrolledToBottom; // Scrolling stopped in viewport (ie not out of bounds)? } else { return; } // Bounce back with shorter animation innerWrapper.style.transitionDuration = '100ms'; innerWrapper.style.transform = 'translate3d(0px, ' + (scrollPosition) + 'px, 0px)'; // Need a 'pause' for the transform to finish with shorter animation setTimeout(function() { // Set animation time back to original innerWrapper.style.transitionDuration = '500ms'; }); } function clamp(val, min, max) { if (typeof min !== 'number') min = 0; if (typeof max !== 'number') max = 1; return Math.min(Math.max(val, min), max); } 
 #outerWrapper { height: 400px; overflow: hidden; display: flex; background-color: black; } #innerWrapper { transform: translate3d(0px, 0px, 0px); transition-property: transform; transition-duration: 500ms; transition-timing-function: ease; } #content { background-image: url("http://images.freeimages.com/images/premium/previews/3037/30376024-beautiful-flower-portrait.jpg"); width: 400px; } #scrollbar { height: 100%; width: 50px; background-color: orange; } #scrollbar_thumb { background-color: yellow; border: 2px solid blue; box-sizing: border-box; transform: translate3d(0px, 0px, 0px); transition-property: transform; transition-duration: 500ms; transition-timing-function: ease; } 
 <div id="outerWrapper"> <div id="innerWrapper"> <div id="content"> Lorem ipsum dolor sit amet consectetuer laoreet faucibus id ut et. Consequat Ut tellus enim ante nulla molestie vitae sem interdum turpis. Fames ridiculus cursus pellentesque Vestibulum justo sem lorem neque accumsan nulla. Lacinia Suspendisse vitae libero sem et laoreet risus Sed condimentum Cras. Nunc massa mauris tempor dolor pulvinar justo neque dui ipsum vitae. Lacinia dui cursus pellentesque Vestibulum justo sem lorem neque accumsan nulla. Lacinia Suspendisse vitae libero sem et laoreet risus Sed condimentum Cras. Nunc massa mauris tempor dolor pulvinar justo neque dui ipsum vitae. Lacinia dui scelerisque Sed convallis nonummy orci Vestibulum orci tempusLorem ipsum dolor sit amet consectetuer laoreet faucibus id ut et. Consequat Ut tellus enim ante nulla molestie vitae sem interdum turpis. Fames ridiculus cursus pellentesque Vestibulum justo sem lorem neque accumsan nulla. Lacinia Suspendisse vitae libero sem et laoreet risus Sed condimentum Cras. Nunc massa mauris tempor dolor pulvinar justo neque dui ipsum vitae. Lacinia dui scelerisque Sed convallis nonummy orci Vestibulum orci tempusLorem ipsum dolor sit amet consectetuer laoreet faucibus id ut et. Consequat Ut tellus enim ante nulla molestie vitae sem interdum turpis. Fames ridiculus cursus pellentesque Vestibulum justo sem lorem neque accumsan nulla. Lacinia Suspendisse vitae libero sem et laoreet risus Sed condimentum Cras. Nunc massa mauris tempor dolor pulvinar justo neque dui ipsum vitae. Lacinia dui scelerisque Sed convallis nonummy orci Vestibulum orci tempusLorem ipsum dolor sit amet consectetuer laoreet faucibus id ut et. Consequat Ut tellus enim ante nulla molestie vitae sem interdum turpis. Fames ridiculus cursus pellentesque Vestibulum justo sem lorem neque accumsan nulla. Lacinia Suspendisse vitae libero sem et laoreet risus Sed condimentum Cras. Nunc massa mauris tempor dolor Lorem ipsum dolor sit amet consectetuer laoreet faucibus id ut et. Consequat Ut tellus enim ante nulla molestie vitae sem interdum turpis. Fames ridiculus cursus pellentesque Vestibulum justo sem lorem neque accumsan nulla. Lacinia Suspendisse vitae libero sem et laoreet risus Sed condimentum Cras. Nunc massa mauris tempor dolor pulvinar justo neque dui ipsum vitae. Lacinia dui scelerisque Sed convallis nonummy orci Vestibulum orci tempusLorem ipsum dolor sit amet consectetuer laoreet faucibus id ut et. Consequat Ut tellus enim ante nulla molestie vitae sem interdum turpis. Fames ridiculus cursus pellentesque Vestibulum justo sem lorem neque accumsan nulla. Lacinia Suspendisse vitae libero sem et laoreet risus Sed condimentum Cras. Nunc massa mauris tempor dolor pulvinar justo neque dui ipsum vitae. Lacinia dui scelerisque Sed convallis nonummy orci Vestibulum orci tempusLorem ipsum dolor sit amet consectetuer laoreet faucibus id ut et. Consequat Ut tellus enim ante nulla molestie vitae sem interdum turpis. Fames ridiculus cursus pellentesque Vestibulum justo sem lorem neque accumsan nulla. Lacinia Suspendisse vitae libero sem et laoreet risus Sed condimentum Cras. Nunc massa mauris tempor dolor pulvinar justo neque dui ipsum vitae. Lacinia dui scelerisque Sed convallis nonummy orci Vestibulum orci tempusLorem ipsum dolor sit amet consectetuer laoreet faucibus id ut et. Consequat Ut tellus enim ante nulla molestie vitae sem interdum turpis. Fames ridiculus cursus pellentesque Vestibulum justo sem lorem neque accumsan nulla. Lacinia Suspendisse vitae libero sem et laoreet risus Sed condimentum Cras. Nunc massa mauris tempor dolor pulvinar justo neque dui ipsum vitae. Lacinia dui scelerisque Sed convallis nonummy orci Vestibulum orci tempusLorem ipsum dolor sit amet consectetuer laoreet faucibus id ut et. Consequat Ut tellus enim ante nulla molestie vitae sem interdum turpis. Fames ridiculus cursus pellentesque Vestibulum justo sem lorem neque accumsan nulla. Lacinia Suspendisse vitae libero sem et laoreet risus Sed condimentum Cras. Nunc massa mauris tempor dolor </div> </div> <div id="scrollbar"> <div id="scrollbar_thumb"></div> </div> </div> 

I might use the z component to make a subtle difference between each wheel event in order to trigger transition. 我可能会使用z组件在每个轮事件之间进行微妙的区别,以触发过渡。 fiddle 小提琴

var i = 0;
// onmousewheel
.. el.style.transform = `translate3d(0, 40px, ${i^=1}px)` 

// ontransitionend
.. el.style.transform = `translate3d(0, 0, -1px)`

If the above approach is too tricky, we may do an expensive query to find what the current translation y is. 如果上述方法太棘手,我们可能会进行一次昂贵的查询来查找当前翻译y是什么。 Then compare it to the bounceAmount(eg 40 in your case), if they are almost identical(diff. < Number.EPSILON) then call bounceBack() directly. 然后将其与bounceAmount(例如您的情况下为40)进行比较,如果它们几乎相同(差异<Number.EPSILON),则直接调用bounceBack()

var m = getComputedStyle(el).transform.match(/,\s*([^,]*?)\s*\)/)
m ? m[1] : 0 // current translation y

Update 更新资料

If the content is required to be bounce back instantly, we can simply disable onwheel when motion starts, then re-enable it after whole motion has completed. 如果需要立即使内容反弹,我们可以简单地在运动开始时禁用onwheel ,然后在整个运动完成后重新启用它。 This does not require the z component trick anymore. 这不再需要z组件技巧。 fiddle 小提琴

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 jQuery动画回调影响错误的元素 - Jquery Animation callback affecting the wrong elements 为什么我的回调使用 redux 多次调用 - Why my callback is calling multiple time with redux 错误的时间传递给 requestAnimationFrame 调用的回调 - wrong time passed to callback called by requestAnimationFrame jQuery Ajax回调类方法似乎调用了错误的实例 - jQuery Ajax callback to a class method seemingly calling wrong instance 使用HTLM5 Canvas时的动画回调体系结构和时间测量? - animation callback architecture and time measuring when using HTLM5 Canvas? 从回调中调用 setState 会在一半的时间内更新 React Context 中的状态 - Calling setState from callback updates the state in React Context half the time 如何编写一个只调用一次回调的function,然后返回第一次调用function的结果,后续每次调用 - How to write a function that only invokes a callback once, then returns the result of calling the function the first time, every subsequent calling 仅第一次调用 useQuery 有条件地返回错误输出 - calling useQuery conditionally returns wrong output only for the first time 回调CSS动画结束 - Callback on CSS animation end 回调中的动画不触发,SnapSVG - Animation in callback not firing, SnapSVG
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM