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 相关文章推荐
一个小型js框架myJSFrame附API使用帮助
Jun 28 Javascript
使用Grunt.js管理你项目的应用说明
Apr 24 Javascript
AngularJS 依赖注入详解和简单实例
Jul 28 Javascript
jquery 中toggle的2种用法详解(推荐)
Sep 02 Javascript
关于Function中的bind()示例详解
Dec 02 Javascript
JQuery查找子元素find()和遍历集合each的方法总结
Mar 07 Javascript
对于input 框限定输入值为浮点型的js代码
Sep 25 Javascript
微信小程序引入Vant组件库过程解析
Aug 06 Javascript
在VUE style中使用data中的变量的方法
Jun 19 Javascript
基于PHP pthreads实现多线程代码实例
Jun 24 Javascript
VUE Elemen-ui之穿梭框使用方法详解
Jan 19 Javascript
vue使用exif获取图片旋转,压缩的示例代码
Dec 11 Vue.js
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 将bmp图片转为jpg等其他任意格式的图片
2009/06/21 PHP
PHP 可阅读随机字符串代码
2010/05/26 PHP
关于php mvc开发模式的感想
2011/06/28 PHP
使用jscript实现二进制读写脚本代码
2008/06/09 Javascript
一款js和css代码压缩工具[附JAVA环境配置方法]
2010/04/16 Javascript
javascript完美拖拽的实现方法
2013/09/29 Javascript
javascript间隔刷新的简单实例
2013/11/14 Javascript
js中的onchange和onpropertychange (onchange无效的解决方法)
2014/03/08 Javascript
javascript实现简单查找与替换的方法
2015/07/22 Javascript
JS基于clipBoard.js插件实现剪切、复制、粘贴
2016/05/03 Javascript
javascript和jQuery实现网页实时聊天的ajax长轮询
2016/07/20 Javascript
vue轮播图插件vue-awesome-swiper
2017/11/27 Javascript
微信小程序时间戳转日期的详解
2019/04/30 Javascript
vue 实现LED数字时钟效果(开箱即用)
2019/12/08 Javascript
js里面的变量范围分享
2020/07/18 Javascript
Python文件和目录操作详解
2015/02/08 Python
Python环境下安装使用异步任务队列包Celery的基础教程
2016/05/07 Python
Python实现一个转存纯真IP数据库的脚本分享
2017/05/21 Python
在Pycharm中自动添加时间日期作者等信息的方法
2019/01/16 Python
Python+Selenium使用Page Object实现页面自动化测试
2019/07/14 Python
python中元组的用法整理
2020/06/15 Python
python不同版本的_new_不同点总结
2020/12/09 Python
聊聊Python pandas 中loc函数的使用,及跟iloc的区别说明
2021/03/03 Python
webView加载html图片遇到的问题解决
2019/10/08 HTML / CSS
缓解脚、腿和背部疼痛:Z-CoiL鞋
2019/03/12 全球购物
戴森英国官网:Dyson英国
2019/05/07 全球购物
const和static readonly区别
2013/05/20 面试题
网络信息安全承诺书
2014/03/26 职场文书
计算机专业毕业生求职信
2014/04/30 职场文书
预备党员综合考察材料
2014/05/31 职场文书
党支部组织生活会整改方案
2014/09/30 职场文书
手机被没收的检讨书
2014/10/04 职场文书
安全保证书
2015/01/16 职场文书
小学生安全保证书
2015/05/09 职场文书
关于golang高并发的实现与注意事项说明
2021/05/08 Golang
MySQL索引失效十种场景与优化方案
2023/05/08 MySQL