写在最前面
- 最近业务和设计稿需要需要写一个加载的动画,然后就决定构建一个 react 的 spinner 圆圈⭕️旋转的加载动画。
- 关键Key: react,css3 clip-path
先来看看需要实现的效果
思路
- 需要先构建一个圆,然后做一个循环旋转的动画,然后在动画的过程中切割圆的部分环,达到上图的效果。
- 圆:border-radius: 50%
- 旋转动画:transform: rotate(...);
- 切割环:clip-path:polygon(...)
css3 clip path
- 这里我们来了解一下 clip-path 的使用方法,最开始这个属性是 clip 然后最近改用了 clip-path.
兼容性
- 首先看看浏览器适配问题
- caniuse.com/#feat=css-c…
- 不支持IE和Firefox,支持webkit浏览器。注意,在现代浏览器中需要使用-webkit-前缀。
使用方法
- developer.mozilla.org/zh-CN/docs/…
/* Geometry values */
clip-path: inset(100px 50px);
clip-path: circle(50px at 0 100px);
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
/* Box and geometry values combined */
clip-path: padding-box circle(50px at 0 100px);
一些 demo
- 预览
<div className="demo">
<h4>三角裁剪</h4>
<div className="clipClass1" />
<h4>圆形裁剪</h4>
<div className="clipClass2" />
<h4>椭圆裁剪</h4>
<div className="clipClass3" />
<h4>裁剪插图</h4>
<div className="clipClass4" />
</div>
代码语言:javascript复制.demo > div {
width: 100px;
height: 100px;
margin: 20px;
background: lightcoral;
}
.clipClass1 {
-webkit-clip-path: polygon(0 100%, 50% 0, 100% 100%);
clip-path: polygon(0 100%, 50% 0, 100% 100%);
}
.clipClass2 {
-webkit-clip-path: circle(50% at 50% 50%);
clip-path: circle(50% at 50% 50%);
}
.clipClass3 {
-webkit-clip-path: ellipse(30% 20% at 50% 50%);
clip-path: ellipse(30% 20% at 50% 50%);
}
.clipClass4 {
-webkit-clip-path: inset(25% 0 25% 0 round 0 25% 0 25%);
clip-path: inset(25% 0 25% 0 round 0 25% 0 25%);
}
构建加载动画组件
- 目录
- src
- index.js
- help.tsx
- spinner.tsx
- style.css
- src
- spinner.tsx
import React, { Component } from "react";
import PropTypes from "prop-types";
/**
* @desc 加载动画组件
* @param
* size: 半径大小
* spinnerColor: 颜色
* spinnerWidth: 圆圈宽度
* visible: 是否显示
*/
export interface ISpinnerProps {
size?: number;
spinnerColor?: string;
spinnerWidth?: number;
visible?: boolean;
}
class Spinner extends Component<ISpinnerProps> {
static defaultProps = {
size: 40,
spinnerColor: "#333333",
spinnerWidth: 5,
visible: true
};
render() {
const { visible } = this.props;
if (!visible) {
return null;
}
const { id, size, width, height, spinnerColor, spinnerWidth } = this.props;
const dimension = size || Math.min(width, height);
return (
<div
id={id}
className="spinner"
style={{
width: dimension,
height: dimension,
borderColor: spinnerColor,
borderWidth: spinnerWidth
}}
/>
);
}
}
export default Spinner;
这里我们还缺 spinner 的样式,我们这里创建一个高阶组件更好的扩充我们的 spinner。
- help.tsx
- 关键点 clip-path: polygon 动画
- animation、transform 动画的使用
- 使用上面的使用方法
import React from "react";
const css = `
.spinner {
width: 80px;
height: 80px;
border-radius: 50%;
border: 10px solid #333;
box-sizing: border-box;
// animation: 动画名 动画时长 动画速度曲线 轮流反向播放动画(alternate) 循环次数(infinite)
animation: sweep 1s linear alternate infinite, rota 0.8s linear infinite;
}
// 其他浏览器支持 @-webkit-等 这里为了节省空间就不加了,需要支持
@keyframes rota {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes sweep {
0% {
clip-path: polygon(0% 0%, 0% 0%, 0% 0%, 50% 50%, 0% 0%, 0% 0%, 0% 0%);
}
50% {
clip-path: polygon(0% 0%, 0% 100%, 0% 100%, 50% 50%, 100% 0%, 100% 0%, 0% 0%);
}
100% {
clip-path: polygon(0% 0%, 0% 100%, 100% 100%, 50% 50%, 100% 100%, 100% 0%, 0% 0%);
}
}
`;
const SPINNER_ID = "spinner_id_style";
const ID_HOLDER = {};
ID_HOLDER.id = 0;
export const SpinnerMixin = Component =>
class extends React.Component {
constructor(props) {
super(props);
// 在 head 中插入上面的样式
if (!document.getElementById(SPINNER_ID)) {
const head = document.head || document.getElementsByTagName("head")[0];
const sprc = document.createElement("style");
sprc.id = SPINNER_ID;
sprc.type = "text/css";
if (sprc.styleSheet) {
sprc.styleSheet.cssText = css;
} else {
sprc.appendChild(document.createTextNode(css));
}
if (head) {
head.appendChild(sprc);
}
}
//加上唯一 id 区分多个 spinner
ID_HOLDER.id = 1;
this.state = {
id: `spinner_${ID_HOLDER.id}`
};
}
render() {
return <Component {...this.props} {...this.state} />;
}
};
- 最后
- spinner.tsx
- 包裹上我们的高阶组件,然后再引用就 ok 了
import React, { Component } from "react";
import PropTypes from "prop-types";
import { SpinnerMixin } from "./help";
// ...... some codes
export default SpinnerMixin(Spinner);
- index.js
import React from "react";
import ReactDOM from "react-dom";
import Spinner from "./spinner";
function App() {
return (
<div className="App">
<h1>Hello Clip-Path</h1>
<div className="container">
<h4>加载动画</h4>
<Spinner
spinnerColor="red"
spinnerWidth={10}
size={100}
visible={true}
/>
</div>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
然后就可以看到最开始的动画效果了
- 完整代码,请看 codepen
- codesandbox.io/s/j7mpvy47r…
参考
- developer.mozilla.org/zh-CN/docs/…
- www.w3school.com.cn/cssref/pr_a…
- css-tricks.com/almanac/pro…