一、运动动画
抛物线由两段
运动构成:
1、横
向
2、纵
向
忽略空气阻力。
横向是匀速
直线运动,
纵向是自由
落体运动。
用css做
一个元素不同方向动画
<html lang="en">
<head>
<style>
body {
margin: 0;
padding: 0;
}
.ball {
width: 50px;
height: 50px;
border-radius: 50%;
background: #B30204;
}
</style>
</head>
<body>
<div class="ball"></div>
</body>
</html>
给这个小球动画,分别一个x方向的移动,一个y方向的移动。
// x
@keyframes moveX {
to {
transform: translateX(200px);
}
}
// y
@keyframes moveY {
to {
transform: translateY(400px);
}
}
.ball {
width: 50px;
height: 50px;
border-radius: 50%;
background: #B30204;
animation: moveX 2s linear forwards, moveY 2s ease forwards;
}
看看效果:
不是我们想要的效果。为什么呢?是因为一个元素同一时间,赋予两个动画,既让它往左走200米,又让它走往下走400米。明显造成冲突了。
那么如何来解决这个问题呢?多元素动画。每一个负责一段运动就好了,那么就不存在css属性冲突这个问题了。
多元素各负责一个动画
<html lang="en">
<head>
<style>
body {
margin: 0;
padding: 0;
}
.ball {
width: 50px;
height: 50px;
border-radius: 50%;
background: #B30204;
animation: moveX 2s linear forwards;
}
.inner {
width: 50px;
height: 50px;
border-radius: 50%;
background: green;
animation: moveY 2s ease forwards;
}
// 动画
// x
@keyframes moveX {
to {
transform: translateX(200px);
}
}
// y
@keyframes moveY {
to {
transform: translateY(400px);
}
}
</style>
</head>
<body>
<div class="ball">
<div class="inner"></div>
</div>
</body>
</html>
现在有两个元素了。一个x轴向右匀速移动200px,一个y轴向下缓慢加速移动400px。我们来看看效果:
🔴 红色是父
🟢 绿色是子
终点(x, y)是(200, 400)
x轴方向的父确确实实也会影响y轴方向的子,只不过这个效果没达到我们想要的那种抛物线的效果。
贝塞尔曲线
那么,想要达到我们想要的效果,y轴方向,其实是需要先向上抛一段路程,再向下做加速运动的。css动画中能满足我们随心
所欲地定制
复杂动画
的需求,这里就需要用到贝塞尔曲线cubic-bezier()函数
。
基本语法:
cubic-bezier(x1, y1, x2, y2)
。
x1、y1、x2、y2
这四个参的取值范围都是(0到1
),较小就较慢,较大就较快。
参数含义:
x1, y1 :起始速度(水平,垂直)
-
x1
:控制动画起始
点的水平
方向速度
变化。 -
y1
:控制动画起始
点的垂直
方向速度
变化。
x2, y2 :结束速度(水平,垂直)
-
x2
:控制动画结束
点的水平
方向速度
变化。 -
y2
:控制动画结束
点的垂直
方向速度
变化。
我们平常碰到的ease
、ease-in
、ease-out
、ease-in-out
等等这些,实际上就是贝塞尔曲线cubic-bezier()
的特定实例。
实际上讲的是速度
。只要x1=y1, x2=y2
那即是匀速linear
。
从贝塞尔曲线
这点回到抛物线
这里。
要想做抛物线,就得知道这条线是先远离目标值
,再逼近目标值
。那么我们可以设置它的时间函数animation-timing-function
为cubic-bezier(0.5, -0.5, 1, 1)
。
二、实战
写按钮
(点击加号飞向购物车,实际上我们这案例是点加号,加号自己根据自己的位置飞向购物车,一般呢,这是不用选规格的商品就这样做,要选规格的,就跳到选这个商品的规格详情界面再去加,啰嗦了,大家去小程序上点个外卖瞅一眼)、加号
、购物车
。
<html lang="en">
<head>
<style>
// 加号位置动态变,方便后面用js控制它的位置
.plus {
left: var(--left);
top: var(--top);
animation: moveY 0.8s cubic-bezier(0.5, -0.5, 1, 1);
}
.plus .iconfont {
animation: moveX 0.8s linear;
}
@keyframes moveY {
to {
transform: translateY(var(--y));
}
}
@keyframes moveX {
to {
transform: translateX(var(--x));
}
}
</style>
</head>
<body>
<div class="cart"></div> // 购物车
<button class="btn">点击</button> // 点击按钮
// 这个静态固定位置的(动态时需注释掉,需动态添加),下面动态js代码如下👇
<div class="plus" style="--left: 300px; --top: 300px; --y: 400px; --x: 200px;"> // 加号
<i class="iconfont"></i>
</div>
</body>
</html>
以上是静态加号抛物线加入购物车。现在动态做,js去添加号,动态主要是要去算元素的位置,而不是固定的位置。
// 根据加号的父元素的位置,去设置加号的位置
/**
* 1、实现一个点击按钮,在按钮位置生成一个加号。
* 2、通过动画效果将加号抛物线姿势飞向购物车,飞完后移除该加号。
* 3、动态设置--left、--top、--x和--y去控制加号的位置和移动路径。
*/
const cart = document.querySelector(".cart");
const btn = document.querySelector(".btn");
const PLUS_SIZE = 30; // 加号的宽度
btn.onclick = function () {
const div = document.createElement("div");
div.className = "plus";
div.innerHTML = `<i class="iconfont"></i>`;
const btnRect = btn.getBoundingClientRect();
const left = btnRect.left + btnRect.width / 2 - PLUS_SIZE / 2,
top = btnRect.top + btnRect.height / 2 - PLUS_SIZE / 2;
// 横向移动的距离是 加号的left加上它自身宽度的一半,减去购物车的left加上它自身宽度的一半。
const catRect = cart.getBoundingClientRect();
const x = (catRect.left + catRect.width / 2) - PlUS_SIZE/2-left;
// 纵向移动的距离是 加号的top加上它自身高度的一半,减去购物车的top加上它自身高度的一半。
const y = (catRect.top + catRect.height / 2) - PLUS_SIZE/2-top;
div.style.setProperty("--left", `${left}px`);
div.style.setProperty("--top", `${top}px`);
div.style.setProperty("--x", `${x}px`);
div.style.setProperty("--y", `${y}px`);
div.addEventListener("animationend", () => { div.remove(); }) // 飞完后消失
document.body.appendChild(div);
}
css
.plus {
--duration: 0.5s; // 定义自定义属性--duration,设置为0.5s,代表动画持续0.5s
animation: moveY var(--duration) cubic-bezier(0.5, -0.5, 1, 1);
}
.plus .iconfont {
animation: moveX var(--duration) linear;
}
--duration 这种是自定义属性,包括上面的
--left --top --x --y
这些都是自定义属性,以便重复使用。
三、拓展
Houdini API
说到动画,那么非常值得去看看一下Houdini API
。是css引擎
暴露出来的一组底层api
。可以直接和浏览器的css引擎
去交互。
tween.js
,js动画库,创一些平滑的动画效果。不必手动去处理一些复杂的动画细节。
脑子内存记不了太多,勤写一些笔记。
☎️ 希望对大家有所帮助,本文有些地方可能考虑不够周到,有些纰漏,就当抛砖引玉,还望您海涵,如有错误,望不吝赐教,欢迎评论区留言互相学习。感谢阅读,祝您开发有乐趣。