js实现拖动缓动效果


Posted in Javascript onJanuary 13, 2020

话不多说,先上效果,一个体验非常好的拖拽缓动的效果,让页面提升一个档次。

js实现拖动缓动效果

这个效果看似很简单,到也困惑了很长时间,为什么别人写出来的拖拽体验为什么这么好?
直到我自己实现了以后,才发现,原来我想的实现方式不对。接下来,我通过简短的几句话,来提供这个功能的实现思路。

首先,我们要明白,我们鼠标拖拽是在一个2d平面上拖拽
2d平面只有x轴和y轴,而且获取的拖拽值也是基于平面的像素获取的。所以,我们第一步,先通过鼠标事件来获取到当前的拖拽的长度像素。

首先,绑定鼠标按下事件,来获取到鼠标基于浏览器窗口左上角的xy平面二维坐标。

然后,绑定move事件,在move事件回调内获取到鼠标拖拽的坐标,和按下坐标相减,求出拖拽的距离。

然后,我们需要通过一定比例,将拖拽的像素转换为旋转角度
我这里设置的比例是,
鼠标横向拖拽10像素,那模型沿3d的Y轴坐标就旋转5度,
鼠标纵向拖拽10像素,模型沿3d世界的X轴坐标旋转1度,并且还设置了范围,即沿x轴旋转再-45度到45度之间

function onDocumentMouseMove(event) {
    mouseX = event.clientX;
    mouseY = event.clientY;
    targetRotationX = targetRotationOnMouseDownX + (mouseX - mouseXOnMouseDownX) * 0.5;
    targetRotationY = Math.min(Math.max((targetRotationOnMouseDownY - (mouseY - mouseXOnMouseDownY) * 0.1), -45), 45); //拖拽后的目标位置
  }

上面获取到目标角度,重点来了,如何实现惰性旋转呢?

通过上面思路,我们知道了目标角度,那么直接设置目标角度,肯定就没有这种想要的效果了,那么如何实现这种惰性效果呢?

接下来,我们需要一个专门实现动画的requestAnimationFrame方法,这个方法是闲时运行,最大根据性能能够达到60帧每秒,有好多小伙伴感觉一直递归运行会不会卡顿,或者影响性能。那是你多虑了,这个方法会根据当前页面性能进行减帧,保证页面流畅运行。

我们有了这个以后,然后做什么呢,就是用来实现缓动,在每一帧里面,获取到目标角度和当前角度的角度差,然后每一次只选择总进度的百分之10 ,然后你会发现选择离目标角度越近,越慢,体验效果也是非常的棒。

而且在运行中,角度也会无限制的接近目标角度,当前demo是通过css3d来实现的:

function animate() {
    requestAnimationFrame(animate);
    rotateY += (targetRotationX - rotateY) * 0.1;
    rotateX += (targetRotationY - rotateX) * 0.1;
    box.style.transform = 'rotateY(' + rotateY + 'deg)';
    item.style.transform = 'rotateX(' + rotateX + 'deg)';
  }

案例全部代码

<!DOCTYPE html>
<html lang="zh">

<head>
  <meta charset="UTF-8">
  <title>css3d翻转</title>
  <style>
    * {
      padding: 0;
      margin: 0;
    }

    body {
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
      overflow: hidden;
      perspective: 1000px;
    }

    .item {
      width: 50vw;
      height: 50vh;
      transform: rotateX(-50deg);
      perspective: 5000px;
      transform-style: preserve-3d;
    }

    .box {
      background: #abb9c5;
      width: 100%;
      height: 100%;
      transform-style: preserve-3d;
      position: relative;
    }

    .font,
    .back {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      text-align: center;
      line-height: 50vh;

      background: #4cae4c;

      backface-visibility: hidden;
    }

    .back {
      background: #62ebff;
      transform: rotateY(180deg);
    }
  </style>
</head>

<body>
  <!--item 可以触发翻转的区域-->
  <div class="item">
    <!--box 可以翻转的容器-->
    <div class="box">
      <!--font 默认显示的正面-->
      <div class="font">正面</div>
      <!--back 背面-->
      <div class="back">背面</div>
    </div>
  </div>
</body>
<script>
  var targetRotationX = 0;
  var targetRotationY = 0;
  var targetRotationOnMouseDownX = 0;
  var targetRotationOnMouseDownY = 0;
  var mouseX = 0;
  var mouseY = 0;
  var mouseXOnMouseDownX = 0;
  var mouseXOnMouseDownY = 0;
  var box = document.querySelector('.box');
  var item = document.querySelector('.item');

  var rotateY = 0;
  var rotateX = 0;

  init();
  animate();

  function init() {
    // EVENTS
    document.addEventListener('mousedown', onDocumentMouseDown, false);
    document.addEventListener('touchstart', onDocumentTouchStart, false);
    document.addEventListener('touchmove', onDocumentTouchMove, false);
  }

  function onDocumentMouseDown(event) {
    event.preventDefault();
    document.addEventListener('mousemove', onDocumentMouseMove, false);
    document.addEventListener('mouseup', onDocumentMouseUp, false);
    mouseXOnMouseDownX = event.clientX;
    mouseXOnMouseDownY = event.clientY;
    targetRotationOnMouseDownX = targetRotationX;
    targetRotationOnMouseDownY = targetRotationY;
  }

  function onDocumentMouseMove(event) {
    mouseX = event.clientX;
    mouseY = event.clientY;
    targetRotationX = targetRotationOnMouseDownX + (mouseX - mouseXOnMouseDownX) * 0.5;
    targetRotationY = Math.min(Math.max((targetRotationOnMouseDownY - (mouseY - mouseXOnMouseDownY) * 0.1), -45), 45); //拖拽后的目标位置
  }

  function onDocumentMouseUp() {
    document.removeEventListener('mousemove', onDocumentMouseMove, false);
    document.removeEventListener('mouseup', onDocumentMouseUp, false);
  }

  function onDocumentTouchStart(event) {
    event.preventDefault();
    if (event.touches.length === 1) {
      mouseXOnMouseDownX = event.touches[0].pageX;
      mouseXOnMouseDownY = event.touches[0].pageY;
      targetRotationOnMouseDownX = targetRotationX;
      targetRotationOnMouseDownY = targetRotationY;
    }
  }

  function onDocumentTouchMove(event) {
    event.preventDefault();
    if (event.touches.length === 1) {
      mouseX = event.touches[0].pageX;
      mouseY = event.touches[0].pageY;
      targetRotationX = targetRotationOnMouseDownX + (mouseX - mouseXOnMouseDownX) * 0.5;
      targetRotationY = Math.min(Math.max((targetRotationOnMouseDownY - (mouseY - mouseXOnMouseDownY) * 0.1), -45), 45); //拖拽后的目标位置
    }
  }

  function animate() {
    requestAnimationFrame(animate);
    rotateY += (targetRotationX - rotateY) * 0.1;
    rotateX += (targetRotationY - rotateX) * 0.1;
    box.style.transform = 'rotateY(' + rotateY + 'deg)';
    item.style.transform = 'rotateX(' + rotateX + 'deg)';
  }
</script>

</html>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
取得父标签
Nov 14 Javascript
javascript 数组排序函数
Aug 20 Javascript
jQuery 学习第五课 Ajax 使用说明
May 17 Javascript
js取消单选按钮选中示例代码
Nov 14 Javascript
js操作IE浏览器弹出浏览文件夹可以返回目录路径
Jul 14 Javascript
javascript移动开发中touch触摸事件详解
Mar 18 Javascript
jQuery实现删除li节点的方法
Dec 06 Javascript
基于Vue单文件组件详解
Sep 15 Javascript
Node.js调用fs.renameSync报错(Error: EXDEV, cross-device link not permitted)
Dec 27 Javascript
微信小程序实现多选框全选与反全选及购物车中删除选中的商品功能
Dec 17 Javascript
vue.js 输入框输入值自动过滤特殊字符替换中问标点操作
Aug 31 Javascript
原生JavaScript实现购物车
Jan 10 Javascript
Vue.js中的高级面试题及答案
Jan 13 #Javascript
深入理解redux之compose的具体应用
Jan 12 #Javascript
2019年度web前端面试题总结(主要为Vue面试题)
Jan 12 #Javascript
html2canvas属性和使用方法以及如何使用html2canvas将HTML内容写入Canvas生成图片
Jan 12 #Javascript
ES2020 新特性(种草)
Jan 12 #Javascript
在微信小程序中渲染HTML内容3种解决方案及分析与问题解决
Jan 12 #Javascript
es6 for循环中let和var区别详解
Jan 12 #Javascript
You might like
PHP 获取MSN好友列表的代码(2009-05-14测试通过)
2009/09/09 PHP
迁移PHP版本到PHP7
2015/02/06 PHP
php nginx 实时输出的简单实现方法
2018/01/21 PHP
PHP实现生成数据字典功能示例
2018/05/24 PHP
JavaScript 解析Json字符串的性能比较分析代码
2009/12/16 Javascript
用JavaScript对JSON进行模式匹配(Part 1-设计)
2010/07/17 Javascript
javascript ie6兼容position:fixed实现思路
2013/04/01 Javascript
jQuery(js)获取文字宽度(显示长度)示例代码
2013/12/31 Javascript
JavaScript中join()方法的使用简介
2015/06/09 Javascript
Javascript实现检测客户端类型代码封包
2015/12/03 Javascript
JavaScript脚本库编写的方法
2015/12/09 Javascript
js判断数组key是否存在(不用循环)的简单实例
2016/08/03 Javascript
全面解析vue中的数据双向绑定
2017/05/10 Javascript
vue中配置mint-ui报css错误问题的解决方法
2017/10/11 Javascript
vue的token刷新处理的方法
2018/07/17 Javascript
vue中将html字符串转换成html后遇到的问题小结
2018/12/10 Javascript
vue中子组件传递数据给父组件的讲解
2019/01/27 Javascript
微信小程序中如何计算距离某个节日还有多少天
2019/07/15 Javascript
python中的函数用法入门教程
2014/09/02 Python
Python入门篇之编程习惯与特点
2014/10/17 Python
Python中的两个内置模块介绍
2015/04/05 Python
python制作爬虫并将抓取结果保存到excel中
2016/04/06 Python
numpy使用技巧之数组过滤实例代码
2018/02/03 Python
python利用插值法对折线进行平滑曲线处理
2018/12/25 Python
Python Tornado之跨域请求与Options请求方式
2020/03/28 Python
Python代码需要缩进吗
2020/07/01 Python
使用简单的CSS3属性实现炫酷读者墙效果
2014/01/08 HTML / CSS
html5仿支付宝密码框的实现代码
2017/09/06 HTML / CSS
进修护士自我鉴定
2013/10/14 职场文书
公司业务主管岗位职责
2013/12/07 职场文书
职业生涯规划书范文
2014/03/10 职场文书
社区党员公开承诺书
2014/08/30 职场文书
XX部保密工作制度范本
2019/08/27 职场文书
html2 canvas svg不能识别的解决方案
2021/06/03 HTML / CSS
Spring Data JPA使用JPQL与原生SQL进行查询的操作
2021/06/15 Java/Android
SpringBoot实现异步事件驱动的方法
2021/06/28 Java/Android