本文讲述的原理和相关 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 图标 "动起来" 呢?
给每个圆设置 animation 实现明暗变化, 例如可以设置黑色的背景色然后动态变化 opacity
animation 属性可以设置 delay 实现动画延迟播放, 我们依次给圆设置等距的 delay, 例如 1s,2s,3s...
给 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 实现原理
设置两个 div: 外层 div 和内层 div
外层 div 设置横向匀速运动的动画
内层 div 设置纵向的匀加速直线运动的动画, 加速过程可以用 cubic-bezier 设置
cubic-bezier 又叫做贝塞尔曲线, 它可接收四个参数, 来规定动画的速度变化过程, 使用方法如下
transition-timing-function: cubic-bezier(0.1, 0.7, 1.0, 0.1);
我们可以通过下面这个官方网站去设置速度变化曲线, 然后获取生成的四个参数
https://cubic-bezier.com/#.59,.14,.93,.58
具体代码如下:
- // 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);
- }
结果
来源: https://www.cnblogs.com/penghuwan/p/12239817.html