vue.js3: 给滑动滚动添加上惯性(vue@3.2.26)
Vue 添加 滚动 滑动 26 3.2 js3 惯性
2023-09-14 09:01:18 时间
一,代码:
<template> <div class="wrapper" ref="wrapper" @touchstart.prevent="onStart" @touchmove.prevent="onMove" @touchend.prevent="onEnd" @touchcancel.prevent="onEnd" @mousedown.prevent="onStart" @mousemove.prevent="onMove" @mouseup.prevent="onEnd" @mousecancel.prevent="onEnd" @mouseleave.prevent="onEnd" @transitionend="onTransitionEnd"> <div class="list" ref="scroller" :style="scrollerStyle"> <div > <img src="static/image/01.jpg" style="width:100%;height:100vh;"/> </div> <div> <img src="static/image/back1.jpg" style="width:100%;height:100vh;"/> </div> <div> <img src="static/image/back2.jpg" style="width:100%;height:100vh;"/> </div> <div> <img src="static/image/back3.jpg" style="width:100%;height:100vh;"/> </div> <div> <img src="static/image/back4.png" style="width:100%;height:100vh;"/> </div> </div> </div> </template> <script> import {ref,onMounted,nextTick,computed} from "vue" export default { name: "Guanxing2", setup() { const wrapper = ref(null); const scroller = ref(null); const minY = ref(0); const maxY = ref(0); const wrapperHeight = ref(0); const offsetY = ref(0); const duration = ref(0); const bezier = ref('linear'); const startY = ref(0); const pointY = ref(0); const startTime = ref(0); // 惯性滑动范围内的 startTime const momentumStartY = ref(0); // 惯性滑动范围内的 startY const momentumTimeThreshold = ref(300); // 惯性滑动的启动 时间阈值 const momentumYThreshold = ref(15); // 惯性滑动的启动 距离阈值 const isStarted = ref(false); // start锁 onMounted(() => { nextTick(()=>{ const { height: lwrapperHeight } = wrapper.value.getBoundingClientRect(); const { height: lscrollHeight } = scroller.value.getBoundingClientRect(); wrapperHeight.value = lwrapperHeight; console.log("wrapperHeight:"+lwrapperHeight); console.log("scrollHeight:"+lscrollHeight); minY.value = lwrapperHeight - lscrollHeight; }) }); const scrollerStyle = computed(()=> { return { 'transform': `translate3d(0, ${offsetY.value}px, 0)`, 'transition-duration': `${duration.value}ms`, 'transition-timing-function': bezier.value, } } ) //按下 const onStart = (e)=>{ const point = e.touches ? e.touches[0] : e; isStarted.value = true; duration.value = 0; stop(); pointY.value = point.pageY; momentumStartY.value = startY.value = offsetY.value; startTime.value = new Date().getTime(); } //移动 const onMove = (e) => { if (!isStarted.value) return; const point = e.touches ? e.touches[0] : e; const deltaY = point.pageY - pointY.value; // 浮点数坐标会影响渲染速度 let loffsetY = Math.round(startY.value + deltaY); // 超出边界时增加阻力 if (loffsetY < minY.value || loffsetY > maxY.value) { loffsetY = Math.round(startY.value + deltaY / 3); } console.log("onMove:"+loffsetY); offsetY.value = loffsetY; const now = new Date().getTime(); // 记录在触发惯性滑动条件下的偏移值和时间 if (now - startTime.value > momentumTimeThreshold.value) { momentumStartY.value = offsetY.value; startTime.value = now; } } //放开 const onEnd = (e) => { console.log(e); if (!isStarted.value) return; isStarted.value = false; console.log("onEnd:isNeedReset:begin"); if (isNeedReset()) return; const absDeltaY = Math.abs(offsetY.value - momentumStartY.value); const lduration = new Date().getTime() - startTime.value; // 启动惯性滑动 if (lduration < momentumTimeThreshold.value && absDeltaY > momentumYThreshold.value) { console.log("开始惯性滑动:"+offsetY.value); const lmomentum = momentum(offsetY.value, momentumStartY.value, lduration); offsetY.value = Math.round(lmomentum.destination); duration.value = lmomentum.duration; bezier.value = lmomentum.bezier; } } //过渡结束后的监听 const onTransitionEnd = () => { isNeedReset(); } //计算得到惯性的值 const momentum = (current, start, duration) => { const durationMap = { 'noBounce': 2500, 'weekBounce': 800, 'strongBounce': 400, }; const bezierMap = { 'noBounce': 'cubic-bezier(.17, .89, .45, 1)', 'weekBounce': 'cubic-bezier(.25, .46, .45, .94)', 'strongBounce': 'cubic-bezier(.25, .46, .45, .94)', }; let type = 'noBounce'; // 惯性滑动加速度 const deceleration = 0.003; // 回弹阻力 const bounceRate = 10; // 强弱回弹的分割值 const bounceThreshold = 300; // 回弹的最大限度 const maxOverflowY = wrapperHeight.value / 6; let overflowY; const distance = current - start; const speed = 2 * Math.abs(distance) / duration; let destination = current + speed / deceleration * (distance < 0 ? -1 : 1); if (destination < minY.value) { overflowY = minY.value - destination; type = overflowY > bounceThreshold ? 'strongBounce' : 'weekBounce'; destination = Math.max(minY.value - maxOverflowY, minY.value - overflowY / bounceRate); } else if (destination > maxY.value) { overflowY = destination - maxY.value; type = overflowY > bounceThreshold ? 'strongBounce' : 'weekBounce'; destination = Math.min(maxY.value + maxOverflowY, maxY.value + overflowY / bounceRate); } return { destination, duration: durationMap[type], bezier: bezierMap[type], }; } // 超出边界时需要重置位置 const isNeedReset = () => { let loffsetY; //console.log("offsetY:"+offsetY.value+";minY:"+minY.value+";maxY:"+maxY.value) if (offsetY.value < minY.value) { loffsetY = minY.value; } else if (offsetY.value > maxY.value) { loffsetY = maxY.value; } if (typeof loffsetY !== 'undefined') { console.log("isNeedReset:"+loffsetY); offsetY.value = loffsetY; duration.value = 500; bezier.value = 'cubic-bezier(.165, .84, .44, 1)'; return true; } return false; } //stop const stop = () => { // 获取当前 translate 的位置 const matrix = window.getComputedStyle(scroller.value).getPropertyValue('transform'); offsetY.value = Math.round(+matrix.split(')')[0].split(', ')[5]); } return { //------变量 wrapper, scroller, minY, maxY, wrapperHeight, offsetY, duration, bezier, startY, pointY, startTime, // 惯性滑动范围内的 startTime momentumStartY, // 惯性滑动范围内的 startY momentumTimeThreshold, // 惯性滑动的启动 时间阈值 momentumYThreshold, // 惯性滑动的启动 距离阈值 isStarted, // start锁 //方法 onStart, onMove, onEnd, onTransitionEnd, momentum, isNeedReset, stop, //computed scrollerStyle, }; }, } </script> <style scoped> body, ul { margin: 0; padding: 0; } ul { list-style: none; } .wrapper { position: absolute; top: 0; left: 0; right: 0; margin: 0 auto; height: 100%; width: 100%; border: 0px solid #000; overflow: hidden; } .list { background-color: #70f3b7; } .list-item { height: 40px; line-height: 40px; width: 100%; text-align: center; border-bottom: 1px solid #ccc; } </style>
说明:刘宏缔的架构森林是一个专注架构的博客,地址:https://www.cnblogs.com/architectforest
对应的源码可以访问这里获取: https://github.com/liuhongdi/
或: https://gitee.com/liuhongdi
说明:作者:刘宏缔 邮箱: 371125307@qq.com
二,查看效果
三,查看vue的版本:
liuhongdi@lhdpc:/data/vue/demo1$ npm list vue demo1@0.1.0 /data/vue/demo1 ├─┬ @vue/cli-plugin-babel@4.5.15 │ └─┬ @vue/babel-preset-app@4.5.15 │ └── vue@3.2.26 deduped ├─┬ element-plus@1.2.0-beta.6 │ ├─┬ @element-plus/icons-vue@0.2.4 │ │ └── vue@3.2.26 deduped │ ├─┬ @vueuse/core@7.4.1 │ │ ├─┬ @vueuse/shared@7.4.1 │ │ │ └── vue@3.2.26 deduped │ │ ├─┬ vue-demi@0.12.1 │ │ │ └── vue@3.2.26 deduped │ │ └── vue@3.2.26 deduped │ └── vue@3.2.26 deduped └─┬ vue@3.2.26 └─┬ @vue/server-renderer@3.2.26 └── vue@3.2.26 deduped
四,参考来源:
https://blog.csdn.net/qq_41903941/article/details/106679904
相关文章
- Vue.js快速介绍-超级马里奥像素艺术
- vue 【 子子组件 => 子组件 => 父组件 】 的事件和参数的传递
- vue.js3: 用pdf-lib给pdf文件添加中文字体水印(pdf-lib@1.17.1 / vue@3.2.37)
- vue.js3: 调节图片的亮度/对比度/饱和度并保存(vue@3.2.37)
- [Vue @Component] Dynamic Vue.js Components with the component element
- vue利用canvas将图片上传到服务器
- 关于购物车添加按钮的动画(vue.js)
- 纯Vue实现网页日常任务清单小功能(数据存储在浏览器)
- vue如何正确销毁当前组件的scroll事件?
- vue自定义指令v-scroll(directive)
- 200:vue+openlayers 添加删除多边形,modify feature,双向互动颜色显示
- 113:vue+openlayers 动态添加layer到layerGroup,并动态删除( 示例代码 )
- 100:vue+openlayers根据名称添加删除图层 (代码示例)
- 015:vue+openlayers 添加鹰眼效果( 代码示例 )