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 相关文章推荐
在浏览器中获取当前执行的脚本文件名的代码
Jul 19 Javascript
javascript实现表格排序 编辑 拖拽 缩放
Jan 02 Javascript
js实现在网页上简单显示时间的方法
Mar 02 Javascript
PHP+jQuery实现随意拖动层并即时保存拖动位置
Apr 30 Javascript
jQuery实现HTML表格单元格的合并功能
Apr 06 Javascript
js+html5实现canvas绘制网页时钟的方法
May 21 Javascript
AngularJS中的拦截器实例详解
Apr 07 Javascript
ES6中箭头函数的定义与调用方式详解
Jun 02 Javascript
iview同时验证多个表单问题总结
Sep 29 Javascript
Vue 使用beforeEach实现登录状态检查功能
Oct 31 Javascript
vue+ts下对axios的封装实现
Feb 18 Javascript
详解Vue中$props、$attrs和$listeners的使用方法
Feb 18 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程序中的常见漏洞进行攻击(下)
2006/10/09 PHP
phpmyadmin导入(import)文件限制的解决办法
2009/12/11 PHP
php 来访国内外IP判断代码并实现页面跳转
2009/12/18 PHP
PHP实现的简单分页类及用法示例
2016/05/06 PHP
php实现生成code128条形码的方法详解
2017/07/19 PHP
thinkphp5框架实现的自定义扩展类操作示例
2019/05/16 PHP
Laravel5.5+ 使用API Resources快速输出自定义JSON方法详解
2020/04/06 PHP
ext for eclipse插件安装方法
2008/04/27 Javascript
jQuery Ajax 仿AjaxPro.Utility.RegisterTypeForAjax辅助方法
2011/09/27 Javascript
JQuery实现简单时尚快捷的气泡提示插件
2012/12/20 Javascript
JS+CSS实现闪烁字体效果代码
2016/04/05 Javascript
详解webpack和webpack-simple中如何引入css文件
2017/06/28 Javascript
laravel5.4+vue+element简单搭建的示例代码
2017/08/29 Javascript
ES6中Array.find()和findIndex()函数的用法详解
2017/09/16 Javascript
Layui选项卡制作历史浏览记录的方法
2019/09/28 Javascript
python正则表达式的使用
2017/06/12 Python
基于sklearn实现Bagging算法(python)
2019/07/11 Python
python GUI库图形界面开发之PyQt5 MDI(多文档窗口)QMidArea详细使用方法与实例
2020/03/05 Python
Python 批量读取文件中指定字符的实现
2020/03/06 Python
CSS3实现鼠标悬停显示扩展内容
2016/08/24 HTML / CSS
html5中svg canvas和图片之间相互转化思路代码
2014/01/24 HTML / CSS
魔声耳机官方网站:Monster是世界第一品牌的高性能耳机
2016/10/26 全球购物
Unineed旗下时尚轻奢网站:FABHunt
2019/05/13 全球购物
荷兰鞋类购物网站:Donelli
2019/05/24 全球购物
Lovedrobe官网:英国领先的大码服装品牌
2019/09/19 全球购物
英国领先的隐形眼镜在线供应商:Lenstore.co.uk
2019/11/24 全球购物
NICKIS.com荷兰:设计师儿童时装
2020/01/08 全球购物
Traffic People官网:女式花裙、上衣和连身裤
2020/10/12 全球购物
老同学聚会感言
2014/02/23 职场文书
小学生交通安全寄语
2015/02/27 职场文书
KTV员工管理制度
2015/08/06 职场文书
祝福语集锦:送给闺蜜的生日祝福语
2019/10/08 职场文书
分析Python感知线程状态的解决方案之Event与信号量
2021/06/16 Python
零基础学java之带返回值的方法的定义和调用
2022/04/10 Java/Android
JAVA springCloud项目搭建流程
2022/05/11 Java/Android
Spring boot实现上传文件到本地服务器
2022/08/14 Java/Android