• 苏格团队
  • 作者:Jason

前言

某一天我收到了产品发来的微信消息。小X,我们的业务现在需要一个类似加入购物车的掉落动画,经过组织的慎重考虑,这个需求就交给你了。于是便有了这篇文章。本文并没有描述多少高深的技术,更多的是一些笔者在做动画时对动画原理的思考以及如何优化动画的一些思路。实现效果如下:

图片质量有限,请谅解

技术分析

前端实现动画的方式有很多。无论是JS动画,CSS动画,Canvas动画还是SVG动画,哪怕是GIF动画实现一个简单的抛物线都是足够的。但考虑业务场景的需求以及可玩性,最终决定使用JS来实现这个动画。

实现分析

在笔者看来大多数动画效果,归根到底还是 数学公式的应用。所谓抛物线动画也无非就是让元素的运动符合抛物线的运动轨迹。
抛物线的方程为: Y = A*X*X + B*X + C 也许大家看到这个公式有点陌生。但曾经物理老师念念有词的 L(距离) = 1/2*A(加速度)*T*T(时间) 想必大家都一定熟记于心。笔者正是利用这个公式来完成抛物线动画。

具体实现

步骤一 获取抛物线的起点和终点

由于业务本身的特殊性,需要在用户点击物品时获取到该元素在窗口中的绝对位置。即元素相对于浏览器可见区域的X, Y的坐标。 这里笔者推荐使用getBoundingClientRect()函数结合具体业务计算绝对位置。 当然,在一些场景里你可以直接使用鼠标点击位置或者其他任意方法获取动画的起点。

步骤二 设定抛物线参数

1. 加速度

加速度A决定了元素在设定方向(下文都用垂直方向代替)的速度变化快慢。当动画的起点和终点都固定时,由公式 L(垂直距离) = 0.5 * A(加速度) * T * T(时间) 可得出此时 加速度A与时间T的平方成反比
需要注意的是,正的加速度A会一直扩大帧与帧之间的垂直移动距离,所以过大的加速度A可能会导致 动画的末期小球有闪烁感

2. 时间 T 与 X 轴初速度

在抛物线的动画中,一般的我们认为元素的 水平移动速度固定。那么同样由公式 L(水平距离) = T * Xspeed(水平速度) 可得出水平速度Xspeed实际上决定了动画的执行时长。
综合加速度的概念,我们可以得出以下结论:
当动画的起始点和结束点一定时,若我们设定X轴的初速度为固定值,则动画的执行时长被固定,此时为了让小球达到既定位置。加速度A需要计算生成。 具体计算公式如下:

// 确定动画起始点和终点
let XStart = 0, YStart = 0, XEnd = 1000, YEnd = 1000;
// 确定关键参数
let Xspeed = XX;
// 根据关键参数Xpeed计算动画时间与垂直加速度
let Time = (XEnd - XStart) / Xspeed;
let A = 2 * (YEnd - YStart ) / (Time * Time);
复制代码

如果需要动画执行的时长固定呢?

// 确定关键参数
let Time = XX;
// 根据关键参数Time计算水平速度与垂直加速度
let Xpeed = (XEnd - XStart) / Time;
let A = 2 * (YEnd - YStart ) / (Time * Time);
复制代码

如果需要加速度固定呢?
不,你不需要 .....

3. Y轴初速度

Y轴的初速度,即抛物线抛出时垂直速度。一般的我会设置 Y轴初速度为负值。 此时会有向上抛然后自然下落的动画,略生动... 这时加速度A的计算公式变为:

let A = 2 * (YEnd - YStart - Yspeed * Time) / (Time * Time);
复制代码

这里需要注意的是,设定不同比值的Xspeed 与 Yspeed可以 改变曲线的形态。背后原理为:Yspeed和加速度A(有可能受Xspeed控制)共同决定了抛出小球后小球上升阶段能达到的 最高点, 而Xspeed决定了此时的X轴位置。

步骤三 让它动起来

常规的JS动画,我们一般使用 setTimeOut 或 requestAnimationFram去实现 。下面我们以requestAnimationFram实现 固定动画执行时长 为例。

1.首先生成小球并确定动画起点和终点,以及关键参数

// 起点和终点请自由设定
let XStart = 0, YStart = 0, XEnd = 1000, YEnd = 1000;
let Time = T;
let Xpeed = (XEnd - XStart) / Time;
let Ypeed = -YY;
let A = 2 * (YEnd - YStart - Yspeed * Time) / (Time * Time);
// 生成元素
let Node = document.createElement('div');
// 自由控制形体,定位一般设定为Fixed
Node.className = 'myNode';
document.body.appendChild(Node);
Node.style.top = YStart + 'px';
Node.style.left = XStart + 'px';复制代码

2.在requestAnimationFram回调内改变元素位置

// 记录元素实时位置
let nowX = XStart;
let nowY = YEnd;
// 单位时间
let loop = 0;
//
let move = () => {if (nowY >= targetTop) {// 销毁实例的判断可自行设定Node.remove();return;}// 当前位置等于原始位置 + 单位时间内的位移nowX += Xspeed;// nowY += (A * loop + Yspeed);requestAnimationFram(() => {Node.style.top = nowY + 'px';Node.style.left = nowX + 'px';loop++;move();});
};
复制代码

3. 小球可能会超过目的点

根据停止动画的代码逻辑,小球在最后一次位移时,也许会超越我们设定的目的点。在下一次setTimeOut的判断中我们才会停止动画和销毁实例。解决方式如下。

requestAnimationFram(() => {Node.style.top = Math.min(nowY, XEnd) + 'px';Node.style.left = Math.min(nowX, YEnd) + 'px';loop++;move();
});
复制代码

顺便一提:这里利用Math.min()或Math.max()可以实现很多有趣的动画,自己去发现新大陆吧

如何实现动态模糊效果

动态模糊实现效果,图片质量有限,请谅解普通实现效果,图片质量有限,请谅解

何为动态模糊?

动态模糊,这里采用百度百科对其的定义 动态模糊或运动模糊(motion blur)是静态场景或一系列的图片像电影或是动画中快速移动的物体造成明显的模糊拖动痕迹。
笔者理解就是视觉信息的残留,即当前时刻的视觉来源(比如图片,视频,脑补)中残留有上一时刻的视觉信息。 这样有什么好处呢?适当的动态模糊会使连续的画面变化 变得更加流畅和自然

如何实现动态模糊?

首先我们做个排除法,肯定是不能放电影的...
让我们在看一遍动态模糊实现的效果造成明显的模糊拖动痕迹。 也就是说如果实现了模糊拖动的痕迹就可以模仿动态模糊效果。 那么模糊拖动又是什么效果呢?

这是一张正常的图片 这是一张动态模糊的图片

笔者认为,动态模糊的效果可模拟为 在元素周围添加数个透明度渐变的相同元素

代码实现

在代码实现之前,我们在首先要确定我们需要实现的目标。以抛物线动画中的小球为目标,即 在运动的小球周围生成数个透明度渐变的小球。具体添加小球的位置呢?笔者的想法是,在小球俩帧位置之间插入残影小球

第一步 包装

将原有实现包装在一个函数里

let animat = (初始位置, 结束位置) => {...参数设定// 位置变换nowX += Xspeed; nowY += (A * loop + Yspeed);requestAnimationFrame(() => {Node.style.top = nowY + 'px';Node.style.left = nowX + 'px';loop++;move();});
}
复制代码

目的很简单,就是生成的残影的小球也需要和原有小球位置信息同步。

第二步 生成残影

思考:每一次残影小球的位置都要与真实小球相关。(通过相同初始值设定的小球自然轨迹相同) 所以我们不能变动小球的真实位置,那么translate似乎就是一个不错的选择。

let animat = (初始位置, 结束位置, 是否是残影) => {...参数设定// 位置变换nowX += Xspeed; nowY += (A * loop + Yspeed);requestAnimationFrame(() => {Node.style.top = nowY + 'px';Node.style.left = nowX + 'px';if (isShadow) {item.style.transform = `translate(${(0.5 * Xspeed)}px ,${-(0.5 * (A * loop + Yspeed))}px)`;item.style.opacity = 0.5;   }}loop++;move();});
}
复制代码

这一步需要注意的是 透明度的变化至关重要。 透明度的取值笔者推荐 0.1至 0.5之间

第三步 生成多个残影

如果只是一个生成一个小球的话,动态模糊的效果不会和明显。所以我们需要新建一个控制小球数量的函数。

createShadow(初始位置, 结束位置,  num) {for (let i = 0; i < num; i++) {animat(初始位置, 结束位置, true, i / (num + 1));}
},
复制代码

animat函数更改为

let animat = (初始位置, 结束位置, 是否是残影, num) => {.....requestAnimationFrame(() => {....if (isShadow) {item.style.transform = `translate(${-(num * Xspeed)}px ,${-(num * (A * loop + Yspeed))}px)`;item.style.opacity = (1 - num) * 0.5;   }}.....});
}
复制代码

大功告成。

结束语

如果对本文有不解,不赞同之处或你有更好的点子,请在留言区留言。一起交流,共同进步。

原生JS实现抛物线动画以及动态模糊效果相关推荐

  1. 原生js判断css动画结束 css 动画结束的回调函数

    原文:原生js判断css动画结束 css 动画结束的回调函数 css3 的时代,css3--动画 一切皆有可能: 传统的js 可以通过回调函数判断动画是否结束:即使是采用CSS技术生成动画效果,Jav ...

  2. 原生js实现移动动画,变化动画

    初生牛犊不怕虎,想用博客来记录自己以前的一些学习经历,对您有帮助的话请留下你们的赞哦! 话不多说进入正题 首先我们应该怎么用原生js实现动画,思路是这样的 通过某些方法获取到当前元素的样式属性值,例如 ...

  3. react中react-custom-scrollbars返回顶部功能,如何使其有平滑动画效果;原生js scroll平滑动画效果

    1.Scrollbars是react-custom-scrollbars插件暴露出来的组件 scrollRef.current.view.scroll({top: 0,left: 0,behavior ...

  4. JS 实现抛物线动画案例

    相信大家都有浏览过,很多购物网站购物车的添加商品动画,今天,我们就手写一个简单的抛物线动画,先上案例: 一.绘制页面 我们这里简单实现,一个按钮,一个购物车图标,样式这里直接跳过,最终dom结构: & ...

  5. 原生JS实现任意数据的动态表格

    昨天晚上有个女生叫我帮他看一下前端怎么通过 JQuery 向后台发送数据,今天上午刚测试完Java,下午就花了一点时间来实现动态表格案例,其实也不难,就是使用原生的JS来实现:其中有一个很奇葩的需求. ...

  6. 使用原生JS在Vue实例中动态插入元素

    说明:项目需要,这里使用到的是Ant Design框架中的modal模态框组件. 完整代码: 经供参考,根据自己的业务需求适当修改!

  7. H5移动端原生JS封装附件上传服务器

    本文主要通过原生的js封装附件上传upload.js.可成功内嵌钉钉,ios和安卓端可正常使用,支持单个.多个附件上传. 一.业务需求 封装原生JS附件上传,动态创建附件列表,可对附件列表进行删除和新 ...

  8. vue如何使用原生js写动画效果_原生js写一个无缝轮播图插件(支持vue)

    轮播图插件(Broadcast.js) 前言:写这个插件的原因 前段时间准备用vue加上网易云的nodejs接口,模拟网易云音乐移动端.因为想自己写一遍所有的代码以及加固自己的flex布局,所以没有使 ...

  9. 原生JS仿造华为商城案例-实现了简单页面-两种轮播图思路的实现-动态展示数据

    文章目录 功能简介 项目准备 项目说明 方案一:源代码 CSS样式 HTML结构 JS逻辑 方案二:源代码 CSS样式 HTML结构 JS逻辑 功能简介 页面展示 该项目功能实现: 无限滚动轮播图 j ...

最新文章

  1. golang 编译提示 cannot assign interface {} 和golang断言使用
  2. system-copy 和 ShellExecute 用法
  3. ACL 2021 | 基于依存句法驱动注意力图卷积神经网络的关系抽取
  4. vc中ASSERT()和VERIFY()区别
  5. linux之通过htop操作进程使用总结
  6. CSDN转载博客的方法
  7. im4java开发向导
  8. java中identifiers什么意思_javassist.是什么意思
  9. android sdk 环境签名,gradle打包APK,并使用jarsigner签名
  10. mysql无法连接10061_Day062 连接数据库异常问题记录
  11. 我的Android进阶之旅------Android检测wifi连接状态
  12. INV 调试: 如何获取库存物料事务处理调试信息
  13. codesys编程_明晚20:00,CODESYS教您制作可编程控制器
  14. 服务器上Jupyter notebook环境搭建
  15. M1芯片安装PR(Premiere)2021已激活解决教程
  16. caffe 安装指南
  17. dismiss和remove_rule out与dismiss的区别
  18. 涂鸦智能进击南美取暖器市场
  19. 嵌入式系统 操作系统 uC/OS uClinux
  20. Android studio关闭启动默认打开上次项目

热门文章

  1. linux中生成内核模块时部分函数undefined的应对方法
  2. 《游戏数据分析的艺术》读书笔记1
  3. JS——canvas基础及其应用
  4. NFS(网络文件系统)简介及搭建
  5. python求解一阶常微分方程
  6. 【SVR预测】基于matlab EMD优化SVR预测【含Matlab源码 1403期】
  7. canvas根据坐标点绘制图形
  8. java处理Oracle Clob Blob类型数据
  9. 【git系列】重命名文件后为何就是untracked状态了 以及 应该如何正规地修改文件呢
  10. C++:使用高斯-勒让德正交估计柯西主值 (CPV) 某些奇异积分(附完整源码)