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 相关文章推荐
详解JavaScript中的表单验证
Jun 16 Javascript
jQuery弹簧插件编写基础之“又见弹窗”
Dec 11 Javascript
AngularJS ng-bind-template 指令详解
Jul 30 Javascript
Bootstrap 3 进度条的实现
Feb 22 Javascript
微信小程序教程系列之设置标题栏和导航栏(7)
Jun 29 Javascript
socket.io学习教程之基本应用(二)
Apr 29 Javascript
jQuery获取单选按钮radio选中值与去除所有radio选中状态的方法
May 20 jQuery
WdatePicker.js时间日期插件的使用方法
Jul 26 Javascript
浅谈Node Inspector 代理实现
Oct 19 Javascript
JavaScript设计模式之调停者模式实例详解
Feb 03 Javascript
vue路由跳转传参数的方法
May 06 Javascript
react-native滑动吸顶效果的实现过程
Jun 03 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完整的日历类(CLASS)
2006/11/27 PHP
PHP中mb_convert_encoding与iconv函数的深入解析
2013/06/21 PHP
php上传图片之时间戳命名(保存路径)
2014/08/15 PHP
php使用function_exists判断函数可用的方法
2014/11/19 PHP
完美解决thinkphp验证码出错无法显示的方法
2014/12/09 PHP
PHP基于自定义类随机生成姓名的方法示例
2017/08/05 PHP
PHPUnit + Laravel单元测试常用技能
2019/11/06 PHP
jquery keypress,keyup,onpropertychange键盘事件
2010/06/25 Javascript
浅析JS中document对象的一些重要属性
2014/03/06 Javascript
gameboy网页闯关游戏(riddle webgame)--仿微信聊天的前端页面设计和难点
2016/02/21 Javascript
NodeJS整合银联网关支付(DEMO)
2016/11/09 NodeJs
详解基于webpack和vue.js搭建开发环境
2017/04/05 Javascript
一个简易的js图片轮播效果
2017/07/22 Javascript
jQuery dateRangePicker插件使用方法详解
2017/07/28 jQuery
详解在React中跨组件分发状态的三种方法
2018/08/09 Javascript
微信小程序使用component自定义toast弹窗效果
2018/11/27 Javascript
VUE基于NUXT的SSR 服务端渲染
2018/11/30 Javascript
ES6 Promise对象的应用实例分析
2019/06/27 Javascript
Python黑魔法@property装饰器的使用技巧解析
2016/06/16 Python
解决Django模板无法使用perms变量问题的方法
2017/09/10 Python
浅谈Python NLP入门教程
2017/12/25 Python
python批量查询、汉字去重处理CSV文件
2018/05/31 Python
python3 flask实现文件上传功能
2020/03/20 Python
自适应线性神经网络Adaline的python实现详解
2019/09/30 Python
在 Windows 下搭建高效的 django 开发环境的详细教程
2020/07/27 Python
python爬虫中的url下载器用法详解
2020/11/30 Python
python如何用matplotlib创建三维图表
2021/01/26 Python
意大利体育用品网上商城:Nencini Sport
2016/08/18 全球购物
美国眼镜网站:EyeBuyDirect
2017/04/13 全球购物
一些PHP的面试题
2015/05/06 面试题
渗透攻击的测试步骤
2014/06/07 面试题
学校安全生产月活动总结
2014/07/05 职场文书
创先争优活动承诺书
2014/08/30 职场文书
2016年员工年度考核评语
2015/12/02 职场文书
导游词之蓬莱长岛
2019/12/17 职场文书
Python中文纠错的简单实现
2021/07/07 Python