React 寫一個 spinner 圓形加載動畫

寫在最前面

  • 最近業務和設計稿須要須要寫一個加載的動畫,而後就決定構建一個 react 的 spinner 圓圈⭕️旋轉的加載動畫。
  • 關鍵Key: react,css3 clip-path

先來看看須要實現的效果

image

思路

  • 須要先構建一個圓,而後作一個循環旋轉的動畫,而後在動畫的過程當中切割圓的部分環,達到上圖的效果。
    • 圓:border-radius: 50%
    • 旋轉動畫:transform: rotate(...);
    • 切割環:clip-path:polygon(...)

css3 clip path

  • 這裏咱們來了解一下 clip-path 的使用方法,最開始這個屬性是 clip 而後最近改用了 clip-path.

兼容性

  • 首先看看瀏覽器適配問題
  • 不支持IE和Firefox,支持webkit瀏覽器。注意,在現代瀏覽器中須要使用-webkit-前綴。

使用方法

/* 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

  • 預覽
    • image
<div className="demo">
    <h4>三角裁剪</h4>
    <div className="clipClass1" />
    <h4>圓形裁剪</h4>
    <div className="clipClass2" />
    <h4>橢圓裁剪</h4>
    <div className="clipClass3" />
    <h4>裁剪插圖</h4>
    <div className="clipClass4" />
</div>
複製代碼
.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%);
}
複製代碼

構建加載動畫組件

  • 目錄javascript

    • src
      • index.js
      • help.tsx
      • spinner.tsx
      • style.css
  • spinner.tsxcss

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。html

  • 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} />; } }; 複製代碼
  • 最後java

  • spinner.tsxreact

    • 包裹上咱們的高階組件,而後再引用就 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); 複製代碼

而後就能夠看到最開始的動畫效果了css3

image

參考

相關文章
相關標籤/搜索