17

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

 4 years ago
source link: http://www.cnblogs.com/penghuwan/p/12239817.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

本文讲述的原理和相关demo

  • 扇形DIV的使用——实现雷达扫描图
  • DIV环形布局—实现loading圈
  • 动画的向量合成—实现抛物线动画
  • 无限滚动动画—实现跑马灯效果
  • perspective和transform的运用——实现卡片翻转

话不多说,请看。

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

在一些杀毒或文件扫描类的软件上,我们可能会看到一些雷达扫描的UI样式,例如下图所示

Q7B3Eja.png!web

如果我们要通过CSS该如何去实现话,我们的想法一般是先画个扇形,然后给它加上渐变。

实现渐变的方式很简单,但我们该如何实现一个扇形呢?

我们可以通过一些技巧实现这一点,请看:

RNzMjmA.png!web

没错,我们可以通过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)

eUfmiqI.gif

DIV环形布局—实现loading圈

loading加载条是常见的一种UI组件,如下图所示

Mz2Qb2Z.gif

而要实现它,就需要考虑怎么把一堆小圆等距地布局在一个“大圆”的边框上,也就是DIV的环形布局的问题。

当然我们可以通过暴力测量解决,但很麻烦且不优雅,而且如果小圆的数量变化的话要重新测一遍。

我的解决办法如下:

第一步:根据圆的数量计算相邻圆和圆心形成的夹角

例如假设我们需要排列8个圆,那么夹角为360度 / 8 = 45度。图示如下,每个数字代表以该位置为圆心放一个小圆

aMnIjmy.png!web

第二步:以外部DIV左下角为原点,批量计算小圆圆心的横纵坐标

批量算出所有圆的相对坐标,我们以编号8的圆为例,假设半径R和X轴的逆时针夹角为θ,则有以下等式

fUzANzm.png!web

(cos/sin可能有正负,而等式同样成立)

第三步,外部div相对定位,内部小圆绝对定位,并且将步骤二中计算的X/Y作为小圆的bottom和left去设置

这一步也是批量完成,下图以编号8的圆为例

7zaM7vI.png!web

代码

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);

实现效果如下

aqiimey.png!web

让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

yIRBBnv.gif

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

在饿了么,或者淘宝天猫之类的购物外卖相关的APP里,我们可能会看到类似于下面这种的抛物线的动画。

BVn6NzY.gif

如果要实现这种平抛效果,需要一点基础的高中物理知识。

平抛运动由水平方向的两种运动合成而得到

  • 水平方向 : 匀速直线运动
  • 垂直方向 :初速度为0的匀加速直线运动

如下所示

Bn2aYvv.jpg!web

如果我们通过图像捕捉的方式就可理解的更清楚了,从下面的图可以看到:

水平方向的速度是不变的,而垂直方向的速度是不断加快的

NRzu2yj.png!web

好,下面终于可以讲下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/

qiimeeb.png!web

具体代码如下:

 // 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");
};

效果如下

qEjYr2y.gif

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

当文本过长时候,我们可能需要做成跑马灯效果,然后无限滚动播放。

因为marquee这个HTML元素被废弃了,所以一般情况下我们需要手动通过动画去实现跑马灯

7Nvy2yY.png!web

实现图示如下,注意开始位置和结束位置是不可见的

uam2aq2.png!web

// 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;
}

结果

2qaEjaj.gif

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);
}

结果

6nuUn2j.gif


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK