Write it up front
- Recently business and design needed to write a loading animation, then decided to build a react spinner circle ⭕️ to rotate the loading animation.
- Key Key: react,css3 clip-path
Let’s look at what we need to achieve
Train of thought
- You need to build a circle first, then do a circular rotation animation, and then cut part of the circle during the animation to achieve the effect of the above image.
- Round: border – the radius: 50%
- Rotate animation: transform: rotate(…) ;
- Clip-path: Polygon (…)
css3 clip path
- Here we look at the use of clip-path. The property was originally clip and recently changed to clip-path.
compatibility
- Let’s start with browser adaptation
- Caniuse.com/#feat=css-c…
- Internet Explorer and Firefox are not supported, but WebKit is supported. Note that the -webkit- prefix is required in modern browsers.
Method of use
- 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);
Copy the code
Some demo
- preview
<div className="demo">
<h4>The triangle cut out</h4>
<div className="clipClass1" />
<h4>Circular cut</h4>
<div className="clipClass2" />
<h4>Oval cut</h4>
<div className="clipClass3" />
<h4>Cut out illustrations</h4>
<div className="clipClass4" />
</div>
Copy the code
.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%);
}
Copy the code
Build load animation components
- directory
- src
- index.js
- help.tsx
- spinner.tsx
- style.css
- src
- spinner.tsx
import React, { Component } from "react";
import PropTypes from "prop-types";
/ * * *@desc Load the animation component@param* size: radius size * spinnerColor: color * spinnerWidth: circle width * Visible: whether to display */
exportinterface 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;
Copy the code
We’re missing the spinner style here, so we’ll create a higher-order component to augment our spinner.
- help.tsx
- Key point clip-path: Polygon animation
- The use of animation and Transform animation
- Use the method above
import React from "react";
const css = ` .spinner { width: 80px; height: 80px; border-radius: 50%; border: 10px solid #333; box-sizing: border-box; // Animation: animation name animation length animation speed curve alternate play animation infinite Sweep 1s Linear alternate infinite, Rota 0.8s Linear infinite; @keyframes rota {from {transform: rotate(0deg); 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);
// Insert the above style in 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); }}// Add a unique ID to distinguish multiple spinners
ID_HOLDER.id += 1;
this.state = {
id: `spinner_${ID_HOLDER.id}`
};
}
render() {
return <Component {. this.props} {. this.state} / >; }};Copy the code
-
The last
-
spinner.tsx
- Wrap our higher-order components around it and then reference them again
import React, { Component } from "react";
import PropTypes from "prop-types";
import { SpinnerMixin } from "./help";
/ /... some codes
export default SpinnerMixin(Spinner);
Copy the code
- 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>Loading animation</h4>
<Spinner
spinnerColor="red"
spinnerWidth={10}
size={100}
visible={true}
/>
</div>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Copy the code
Then you can see the original animation
- For the complete code, see Codepen
- Codesandbox. IO/s/j7mpvy47r…
reference
- Developer.mozilla.org/zh-CN/docs/…
- www.w3school.com.cn/cssref/pr_a…
- Css-tricks.com/almanac/pro…