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 相关文章推荐
跟着Jquery API学Jquery之一 选择器
Apr 07 Javascript
让你的博文自动带上缩址的实现代码,方便发到微博客上
Dec 28 Javascript
JsDom 编程小结
Aug 09 Javascript
JS画线(实例代码)
Nov 20 Javascript
一个网页标题title的闪动提示效果实现思路
Mar 22 Javascript
jQuery源码解读之removeAttr()方法分析
Feb 20 Javascript
JavaScript编程中实现对象封装特性的实例讲解
Jun 24 Javascript
jQuery实现DIV响应鼠标滑过由下向上展开效果示例【测试可用】
Apr 26 jQuery
详解Vue.js v-for不支持IE9的解决方法
Dec 29 Javascript
一秒学会微信小程序制作table表格
Feb 14 Javascript
node.js中fs文件系统模块的使用方法实例详解
Feb 13 Javascript
手把手教你如何编译打包video.js
Dec 09 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中常用编辑器推荐
2007/01/02 PHP
PHP浮点比较大小的方法
2016/02/14 PHP
php获取给定日期相差天数的方法分析
2017/02/20 PHP
laravel 使用auth编写登录的方法
2019/09/30 PHP
php面向对象基础详解【星际争霸游戏案例】
2020/01/23 PHP
jquery select(列表)的操作(取值/赋值)
2011/03/16 Javascript
一行代码实现纯数据json对象的深度克隆实现思路
2013/01/09 Javascript
详解JavaScript语法对{}处理的坑爹之处
2014/06/05 Javascript
深入理解JavaScript系列(31):设计模式之代理模式详解
2015/03/03 Javascript
JS实现网页上随机产生超链接地址的方法
2015/11/09 Javascript
jQuery入门之层次选择器实例简析
2015/12/11 Javascript
Node.js配合node-http-proxy解决本地开发ajax跨域问题
2016/08/31 Javascript
jQuery实现立体式数字滚动条增加效果
2016/12/21 Javascript
xmlplus组件设计系列之路由(ViewStack)(7)
2017/05/02 Javascript
javascript计算渐变颜色的实例
2017/09/22 Javascript
通过fastclick源码分析彻底解决tap“点透”
2017/12/24 Javascript
JavaScript实现的九种排序算法
2019/03/04 Javascript
vue获取时间戳转换为日期格式代码实例
2019/04/17 Javascript
详解vue 路由跳转四种方式 (带参数)
2019/04/28 Javascript
vue实现拖拽的简单案例 不超出可视区域
2019/07/25 Javascript
layui固定下拉框的显示条数(有滚动条)的方法
2019/09/10 Javascript
layui--select使用以及下拉框实现键盘选择的例子
2019/09/24 Javascript
跟老齐学Python之有点简约的元组
2014/09/24 Python
python使用sorted函数对列表进行排序的方法
2015/04/04 Python
wxPython使用系统剪切板的方法
2015/06/16 Python
python对日志进行处理的实例代码
2018/10/06 Python
Python选择网卡发包及接收数据包
2019/04/04 Python
利用pyshp包给shapefile文件添加字段的实例
2019/12/06 Python
django模型动态修改参数,增加 filter 字段的方式
2020/03/16 Python
微软香港官网及网上商店:Microsoft HK
2016/09/01 全球购物
Lacoste澳大利亚官网:服装、鞋类及配饰
2018/11/14 全球购物
JPA的特点
2014/10/25 面试题
吨的认识教学反思
2014/04/27 职场文书
关于五一放假的通知
2015/08/18 职场文书
SpringBoot+Vue+JWT的前后端分离登录认证详细步骤
2021/09/25 Java/Android
go goth封装第三方认证库示例详解
2022/08/14 Golang