CSS动效集锦,视觉魔法的碰撞与融合(三)

目录

 

正文

本文讲述的原理和相关 demo
  • 扇形 DIV 的使用——实现雷达扫描图
  • DIV 环形布局—实现 loading 圈
  • 动画的向量合成—实现抛物线动画
  • 无限滚动动画—实现跑马灯效果
  • perspective 和 transform 的运用——实现卡片翻转
 
话不多说,请看。
 
回到顶部

扇形 DIV 的使用——实现雷达扫描图

在一些杀毒或文件扫描类的软件上,我们可能会看到一些雷达扫描的 UI 样式,例如下图所示
 
如果我们要通过 CSS 该如何去实现话,我们的想法一般是先画个扇形,然后给它加上渐变。
实现渐变的方式很简单,但我们该如何实现一个扇形呢?
 
我们可以通过一些技巧实现这一点,请看:
没错,我们可以通过 skew 函数,将黄色的 div 倾斜,然后溢出部分通过 overflow:hidden 遮住就可以了。
  • 锐角扇形:deg<0,向右边倾斜,即可得到锐角扇形
  • 钝角扇形:deg>0, 向左边倾斜,即可得到钝角扇形
 
代码如下
// CSS 代码
@keyframes rotateAnimate {
  from {
    transform: rotate(0deg) skew(-30deg)
  }

to {
transform
: rotate(360deg) skew(-30deg)
}
}

.fan-wrapper {
overflow
: hidden;
position
: relative;
margin
: 100px;
width
: 200px;
height
: 200px;
border-radius
: 50%;
background
: red;
}

.fan {
position
: absolute;
right
: 0;
animation
: rotateAnimate 2s linear infinite;
/ 这一行很重要,设置左下角为旋转点 /
transform-origin
: 0% 100%;
width
: 100px;
height
: 100px;
background
: blue;
}
// HTML 代码
<div class="fan-wrapper">
<div class="fan"></div>
</div>

 
实现效果如下图所示
(因为篇幅有限,渐变就不加了 2333)
 
回到顶部

DIV 环形布局—实现 loading 圈

 
loading 加载条是常见的一种 UI 组件,如下图所示
而要实现它,就需要考虑怎么把一堆小圆等距地布局在一个“大圆”的边框上,也就是 DIV 的环形布局的问题。
当然我们可以通过暴力测量解决,但很麻烦且不优雅,而且如果小圆的数量变化的话要重新测一遍。
我的解决办法如下:
 
第一步:根据圆的数量计算相邻圆和圆心形成的夹角
例如假设我们需要排列 8 个圆,那么夹角为 360 度 / 8 = 45 度。图示如下,每个数字代表以该位置为圆心放一个小圆
 
第二步:以外部 DIV 左下角为原点,批量计算小圆圆心的横纵坐标
批量算出所有圆的相对坐标,我们以编号 8 的圆为例,假设半径 R 和 X 轴的逆时针夹角为θ,则有以下等式
(cos/sin 可能有正负,而等式同样成立)
第三步,外部 div 相对定位,内部小圆绝对定位,并且将步骤二中计算的 X/Y 作为小圆的 bottom 和 left 去设置
这一步也是批量完成,下图以编号 8 的圆为例
 
代码
CSS/HTML 代码如下:
我们在一个父 div 内部放 8 个子 div。父 div 相对定位,而子 div 绝对定位
// CSS 代码
.circles {
  position: relative;
  margin: 50px;
  width: 200px;
  height: 200px;
}

.circle {
position
: absolute;
width
: 20px;
height
: 20px;
border-radius
: 50%;
background
: black;
}
// HTML
<div class="circles">
<div class="circle circle1"></div>
<div class="circle circle2"></div>
<div class="circle circle3"></div>
<div class="circle circle4"></div>
<div class="circle circle5"></div>
<div class="circle circle6"></div>
<div class="circle circle7"></div>
<div class="circle circle8"></div>
</div>

 

 
JS 代码如下
第一步:编写 calcXYs 方法: 以外部 DIV 左下角为原点,批量计算小圆圆心的横纵坐标
/**
 * R: 大圆半径,2*R = 外部正方形的边长
 * r: 在大圆边上等距排列的小圆的半径
 * counts: 圆的数量
 * 返回值:
 *  [*    [x1,y1],
 *    [x2,y2],
 *    ...
 *  ]
 */
function calcXYs(R, r, counts) {
  // 当前度数
  let deg = 0;
  // 单位度数,两小圆和圆心的夹角
  const pDeg = 360 / counts;
  // 存放返回结果
  const arr = [];
  for (let i = 0; i < counts; i++) {
    // 度数以单位度数递增
    deg = pDeg * i;
    // Math.sin 接收的参数以 π 为单位,需要根据 360 度 = 2π进行转化
    const proportion = Math.PI / 180;
    // 以外部 DIV 左下角为原点,计算小圆圆心的横纵坐标
    let Y = R + R * Math.sin(proportion * deg);
    let X = R + R * Math.cos(proportion * deg);
    // 存放结果
    arr.push([X, Y, deg]);
  }
  return arr;
}
 
第二步:编写 resizeCircles 方法: 根据上一步的结果:调整绝对定位的小圆的位置
/**
 * R,r,counts:含义同上
 * selector: 获取所有小圆的标志符
 * 作用:根据上一步的坐标计算结果,调整绝对定位的小圆的位置
 */
function resizeCircles(selector, R, r, counts) {
  // 获取所有小圆 NodeList 的选择器
  let list = document.querySelectorAll(selector);
  //调用 calcXYs 方法
  const XYs = calcXYs(R, r, counts);
  // 遍历每个小圆的 XY 坐标
  for (let i = 0; i < list.length; i++) {const [X, Y] = XYs[i];
    const e = list[i];
    // 修改小圆距离外部 DIV 底部和左边的距离
    e.style.left = X + "px";
    e.style.bottom = Y + "px";
  }
}
 
最后我们只需要调用 resizeCircles 方法就可以啦
resizeCircles(".circle", 60, 20, 8);

 

实现效果如下
 
让 loading 图标动起来
好,现在布局完成了,那我们该怎么去让这个 loading 图标“动起来”呢?
  1. 给每个圆设置 animation 实现明暗变化,例如可以设置黑色的背景色然后动态变化 opacity
  2. animation 属性可以设置 delay 实现动画延迟播放,我们依次给圆设置等距的 delay,例如 1s,2s,3s...
  3. 给 animation 属性设置 alternate,表示往复播放,设置 infinite,表示无限循环播放
 
@keyframes k {
  from {
    opacity: 1;
  }

to {
opacity: 0;
}
}
.circle1 {
animation: k 1s ease 0s alternate infinite;
}

.circle2 {
animation: k 1s ease 0.2s alternate infinite;
}

.circle3 {
animation: k 1s ease 0.4s alternate infinite;
}
// circle4 ~ circle8 同理,delay 以 0.2s 递增

 

 
Demo
 
回到顶部

动画的向量合成—实现抛物线动画

 
在饿了么,或者淘宝天猫之类的购物外卖相关的 APP 里,我们可能会看到类似于下面这种的抛物线的动画。
 
如果要实现这种平抛效果,需要一点基础的高中物理知识。
平抛运动由水平方向的两种运动合成而得到
  • 水平方向: 匀速直线运动
  • 垂直方向:初速度为 0 的匀加速直线运动
如下所示
 
如果我们通过图像捕捉的方式就可理解的更清楚了,从下面的图可以看到:
水平方向的速度是不变的,而垂直方向的速度是不断加快的
好,下面终于可以讲下 CSS 的实现思路了
CSS 实现原理
  1. 设置两个 div:外层 div 和内层 div
  2. 外层 div 设置横向匀速运动的动画
  3. 内层 div 设置纵向的匀加速直线运动的动画,加速过程可以用 cubic-bezier 设置
 
cubic-bezier 又叫做贝塞尔曲线,它可接收四个参数,来规定动画的速度变化过程,使用方法如下
transition-timing-function: cubic-bezier(0.1, 0.7, 1.0, 0.1);
 
我们可以通过下面这个官方网站去设置速度变化曲线,然后获取生成的四个参数
https://cubic-bezier.com/
 
具体代码如下:
 // HTML
<div id="outer">
  <div id="inner"></div>
</div>
<button id='btn'> 抛物线效果 </button>
// CSS #outer
{ transition: all 1.5s linear; }

#inner {
width
: 30px;
height
: 30px;
border-radius
: 50%;
background
: red;
transition
: all 1.5s cubic-bezier(.54, .11, .95, .68);
}

.outer-active {
transform
: translateX(300px);
}

.inner-active {
transform
: translateY(300px) scale(0.3);
}

 

JS
document.getElementById("btn").onclick = function() {
  document.getElementById("outer").classList.add("outer-active");
  document.getElementById("inner").classList.add("inner-active");};
 
效果如下
 
 
 
回到顶部

无限滚动动画—实现跑马灯效果

当文本过长时候,我们可能需要做成跑马灯效果,然后无限滚动播放。
 
因为 marquee 这个 HTML 元素被废弃了,所以一般情况下我们需要手动通过动画去实现跑马灯
实现图示如下,注意开始位置和结束位置是不可见的
 
// HTML
<div class="marquee">
  <p>ABCDEFGHIJKLMN</p>
</div>
// CSS
@keyframes marquee {
  from {
    transform: translateX(-200px) }

to {
transform: translateX(200px)
}
}

.marquee {
overflow: hidden;
margin: 100px;
width: 200px;
}

.marquee p {
animation: marquee 3s linear infinite;
}

 
结果
 
 
回到顶部

perspective 和 transform 的运用——实现卡片翻转

卡片翻转三要素
  • transform: rotateY(x deg) 翻转卡片
  • backface-visibility:hidden 翻转后隐藏背面,重要!必须要加
  • perspective:增加透视和立体效果
 
// HTML
<div id="img-wrapper">
  <img src='./timg.jpg' id='img1' class="img disable-img1" />
  <img src='./timg2.jpg' id='img2' class="img" />
</div>

// CSS
#img-wrapper {
perspective
: 1200px;
position
: relative;
height
: 479px;
}

#img1,
#img2 {
position
: absolute;
transition
: all 1s linear;
backface-visibility
: hidden;
}

#img1 {
transform
: rotateY(-180deg);
}

#img-wrapper:hover #img1 {
transform
: rotateY(-360deg);
}

#img-wrapper:hover #img2 {
transform
: rotateY(-180deg);
}

 

 
结果