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
  • 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…