# # 使用

> 这里的使用都以单独引入为例，全局引入使用类似。

## # `WebLoading`

> 无论是`DOM`还是`FULL`、`MINI`最终都会走`WebLoading`。

    import { LoadingType } from "web-loading/src/types.d";
    import initLoading from 'web-loading/src/loading'
    let webLoading:LoadingType = initLoading({})
    console.log(webLoading)

> `LoadingType`：`loading`、`resize`、`close`、`update`、`getOptions`、`getLoadingId`。

### # loading

> 启动动画`loading`。

- 参数：

  - `dom:HTMLElement`
  - `options?:OptionsType`

- 返回：void

> 调用`loading`函数，会先检查`loadingId`是否存在(是否还在绘制)，只有无绘制状态才会启动。

> `loading`参数2，可以覆盖初始化`initLoading`的`options`。

### # resize

> 重新计算并绘制`loading`大小。

- 参数：无
- 返回：void

> `resize`会从绑定的`HtmlElement`中重新获取大小并重新绘制，此函数不会重新实例`WebLoading`与`loading`业务场景不同，例如`window.addEventListener('resize', loading.resize)`可能会使用到。

### # close

> 关闭`loading。`

- 参数：无
- 返回：void

> `close`首先会清空所有`WebLoading`的`store`以及其他记录，并停止`requestAnimationFrame`调用，最后根据`delayInto`清除相关元素，关闭过程中`WebLoading`会使用`hookCall`触发`hook`关闭钩子函数(`BEFORE_COLSE`:关闭前，`COLSED`：关闭后，也就是清除元素后)，以便于绘制model。

### # update

> 动态绘制`model`，不能动态修改公共配置。

- 参数：
  - `options?:OptionsType`
- 返回：void

> `update`函数不会重新实例`WebLoading`，并通过参数`options`重新绘制`model`。业务场景与`loading`类似，但`loading`每次都会初始化`WebLoading`，只有每次关闭才能实现相应业务，所以在动态绘制`model`业务场景下推荐使用`update`。

### # getOptions

> 获取当前options配置。

- 参数：无

- 返回：

  - `options:OptionsType`

### # getLoadingId

> 获取 `loadingId`。

- 参数：无

- 返回：

  - `loadingId:string | null`

> `loadingId`在初始化会赋值，`close`关闭后会赋值为`null`，因此，如果`loadingId`为`null`当前无绘制状态，否则相反。

## # options

    import { MODEL_TYPES } from "web-loading/src/utils";
    import initLoading from 'web-loading/src/loading'
    let webLoading = initLoading({
        model:MODEL_TYPES.GEAR
    })

### # 公共

属性 | 类型 | 默认值 | 备注
--- | --- | --- | ---
`html?:` | `string` | `''` | html加载方式(优先级大于custom)
`custom?:` | `typeof BaseModel 或 null` | `null` | 自定义model(优先级大于model)
`type?:` | `LOADING_TYPES` | `LOADING_TYPES.DOM` | 启动方式
`miniClass?:` | `string 或 null 或 undefined` | `mini` | 启动方式为MINI时的class
`model?:` | `MODEL_TYPES` | `MODEL_TYPES.RING` | model模块
`text?:` | `string` | `加载中...` | 字体内容
`textGap?:` | `number` | `8` | 字体间距
`fontSize?:` | `number` | `12` | 字体大小
`fontFamily?:` | `string` | `Microsoft YaHei` | 字体类型
`delay?:` | `number` | `65` | 动画延迟
`delayInto?:` | `number` | `520` | 进入/关闭 动画延迟
`optimization?:` | `boolean` | `false` | 优化处理（暂不支持）
`zIndex?:` | `string` | `2001` | loading层级
`themeColor?:` | `string` | `rgba(64,158,255,1)` | 主题色
`bgColor?:` | `string` | `rgba(0, 0, 0, 0.8)` | 背景色
`shadowColor?:` | `string` | `rgba(64,158,255,0.6)` | 阴影色
`shadowOffsetX?:` | `number` | `2` | 阴影X
`shadowOffsetY?:` | `number` | `2` | 阴影Y
`shadowBlur?:` | `number` | `5` | 阴影范围
`pointerEvents?:` | `boolean` | `false` | 事件穿透(DOM方式)
`toast?:` | `boolean` | `true` | 是否显示提示

- LOADING_TYPES

属性 | 枚举值 | 备注
--- | --- | ---
DOM | `dom` | 元素
FULL | `full` | 全屏
MINI | `mini` | 移动端

- MODEL_TYPES

支持的model

### # model

model配置详情

## # custom自定义model

### # TypeScript项目例子

- 自定义

    import type { OptionsType, LimitType, ElementType } from "web-loading/src/types.d";
    import BaseModel from "web-loading/src/draw/model/BaseModel";
    // 1?.如果model中options需要自定义参数，定义options类型
    interface CustomOptionsType extends OptionsType {
      size?: number;
    }
    // 2?.自定义model默认参数(如果无需自定义options参数，使用OptionsType即可)
    // 2.1?. 定义默认值
    let modelDefOptions: CustomOptionsType = {
      size: 10,
    };
    // 2.2?.限制值的大小
    let limits: Array<LimitType> = [
      {
        key: "size",
        message: "key < 100",
        limit: (key) => {
          return key < 100;
        },
      },
    ];
    // 3.自定义model类
    class CustomLoading extends BaseModel<CustomOptionsType> {
      constructor(w: number, h: number, canvas: HTMLCanvasElement, options: Required<CustomOptionsType>, element: ElementType) {
       super(w, h, canvas, options, element, modelDefOptions, limits, function modelDefCall(model) {
          // BaseModel初始化成功回调(可以自己做一些格外的初始化操作)
          // model 是CustomLoading本身，这里初始化默认画笔fill为红色
          model.ctx.fillStyle = "red";
        });
        // 根据周期调用
        this.run(this.draw);
      }
      draw() {
        // 清空画布
        this.clearRect();
        let op = this.options;
        // 绘制(这里获取配置的自定义options参数)
        this.ctx.fillRect(-op.size / 2, -op.size / 2, op.size, op.size);
      }
    }

> 这里简单自定义绘制了一个矩形

> - `BaseModel`是自定义继承类，如果是全局引入情况下，`window`中挂载`BaseModel`属性的。
> - 注意:`modelDefOptions`、`limits`、`modelDefCall`不是必填参数
> - 这里如果自定义的model无需`options`参数，可以省略1、2步骤。

- loading

    import { LoadingType } from "web-loading/src/types.d";
    import initLoading from "web-loading/src/loading";
    let dom = document.querySelector("xxx");
    // 配置参数
    let options: CustomOptionsType = {
      custom: CustomLoading as typeof BaseModel,
      size: 20,
    };
    let webLoading: LoadingType = initLoading(options);
    webLoading.loading(dom);

### # 原生html项目例子

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>web-loading</title>
      </head>
      <script src="https://cdn.jsdelivr.net/npm/web-loading"></script>
      <body>
        <div id="app">
          <div class="item">
            <div>
              id:
              <span class="value">001</span>
            </div>
            <div>
              user:
              <span class="value">use1</span>
            </div>
          </div>
        </div>
        <script>
          window.onload = () => {
            // encapsulation
            let modelDefOptions = {
              size: 10
            }
            let limits = [
              {
                key: 'size',
                message: 'key < 100',
                limit: (key) => {
                  return key < 100
                }
              }
            ]
            class CustomLoading extends BaseModel {
              constructor(w, h, canvas, options, store) {
                super(w, h, canvas, options, store, modelDefOptions, function modelDefCall(model) {
                  model.ctx.fillStyle = 'red'
                })
                this.run(this.draw)
              }
              draw() {
                this.clearRect()
                let op = this.options
                this.ctx.fillRect(-op.size / 2, -op.size / 2, op.size, op.size)
              }
            }
            // loading
            let docApp = document.querySelector('#app')
            const webLoading = initLoading({
              custom: CustomLoading,
              size: 20
            })
            window.addEventListener('resize', webLoading.resize)
            webLoading.loading(docApp)
            setTimeout(() => {
              webLoading.close()
            }, 1500)
          }
        </script>
      </body>
      <style>
        #app {
          margin: 12px;
          padding: 12px;
          height: 200px;
          border: 1px solid gray;
          overflow: auto;
          border-radius: 5px;
        }
        #app .item {
          display: flex;
          flex-direction: column;
          border: 1px solid gray;
          border-radius: 5px;
          padding: 10px;
          line-height: 30px;
          margin-bottom: 6px;
          height: 60px;
        }
        #app .item .value {
          display: inline-block;
          min-width: 31px;
          min-height: 16px;
          font-weight: bold;
          color: rgb(64, 158, 255);
        }
      </style>
    </html>

### # 1、`BaseModel`参数

> `WebLoading`调用`custom`的时候会自动注入相关参数。

参数 | 类型 | 备注
--- | --- | ---
`w` | `number` | 画布宽度
`h` | `number` | 画布高度
`canvas` | `HTMLCanvasElement` | 画布元素，`BaseModel`默认以及获取了`2d`的上下文，但您还可以根据画布元素获取其他上下文
`options` | `Required<CustomOptionsType>` | `options`是调节model参数，分为有公共参数与model参数最终会合并，`Required`标注你的参数不为空(已经初始值)
`element` | `ElementType` | 容器元素
`modelDefOptions?:` | `T` | 自定义model的默认参数(可选)
`limits?:` | `Array<LimitType>` | 自定义model的`options`值限制函数(可选)
`modelDefCall?:` | `(model: BaseModel<T>) => void` | `BaseModel`初始化完成回调(可选)

### # `ElementType`

> 继承了`HTMLElement`。

属性 | 类型 | 备注
--- | --- | ---
`loadingId` | `string或null` | 记录`loading`元素`id`
`$store` | `ElementStoreType` | `loding`缓存内容
`HTMLElement属性...` | ... | ...

### # `ElementType.$store:ElementStoreType`

> 绘制model时需要用到`WebLoading`的一些状态。

属性 | 类型 | 备注
--- | --- | ---
`options` | `OptionsType` | 储存最终合并的`options`参数
`animationId` | `number或undefined` | 记录`animation`状态
`loadingId` | `string或null` | 记录`loading`元素`id`
`hookCall` | `HooksCallType` | `loading`的钩子函数
`model` | `BaseModel或null` | 正在使用的model

### # `ElementType.$store.hookCall:HooksCallType`

> `WebLoading`关闭时触发的钩子函数。

> `HOOKSCALL_KEY`枚举。

属性 | 枚举值 | 类型 | 备注
--- | --- | --- | ---
`BEFORE_COLSE` | `beforeColse` | `Function` | 关闭前
`COLSED` | `colsed` | `Function` | 关闭后(删除元素后)

以自定义`custom`为例

    // 其余code省略
    class CustomLoading extends BaseModel<CustomOptionsType> {
        constructor(w: number, h: number, canvas: HTMLCanvasElement, options: Required<CustomOptionsType>, element: ElementType) {
            super(w, h, canvas, options, element);
            this.initOptions(defOptions);
            this.run(this.draw);
            this.store.hookCall.beforeColse(() => {
                console.log("关闭前");
            });
            this.store.hookCall.colsed(() => {
                console.log("关闭后");
            });
        }
        draw() {
            let op = this.options;
            this.ctx.fillRect(0, 0, op.size, op.size);
        }
    }

### # `LimitType`

> 限制`options`参数值的范围。

属性 | 类型 | 备注
--- | --- | ---
`key` | `string` | 需要限制的`options`属性
`message` | `string` | 超出限制的提示
`limit` | `(key: any) => boolean` | 限制操作

### # 2、`BaseModel函数`

> `BaseModel`自带的函数主要是用户扩展model绘制

函数 | 返回 | 备注
--- | --- | ---
`initContextCall` | `void` | initContextCall
`run` | `void` | run
`clearRect` | `void` | clearRect
`drowRadiusRect` | `void` | drowRadiusRect
`drawText` | `void` | drawText
`clearAnimationFrame` | `void` | clearAnimationFrame

### # `BaseModel:initContextCall`

> `BaseModel`初始化自定义画布属性(继承时已经触发)。

参数 | 类型 | 备注
--- | --- | ---
`modelDefOptions?:` | `T` | 自定义model的默认`options`值。
`limits?:` | `Array<LimitType>` | 自定义model的`options`值的限制范围。
`modelDefCall?:` | `BaseModel<T>` | `BaseModel`初始化完成回调。

### # `BaseModel:run`

> 根据`options`.delay延迟触发`requestAnimationFrame`。

参数 | 类型 | 备注
--- | --- | ---
`fun` | `Function` | 调用之前如果正处于加载，会清空上次的状态。

    this.run(()=>{
        // 根据options.delay触发
    })

### # `BaseModel:clearRect`

> 清空画布。

参数 | 类型 | 备注
--- | --- | ---
`x?:` | `number` | 清空起点X
`y?:` | `number` | 清空的起点Y
`w_r?:` | `number` | 清空终点X(宽度)，圆形清空情况下`w_r`是半径
`h?:` | `number` | 清空终点Y(高度)

- 清空全部

    this.clearRect()

> 考虑到绘制区域可能会超出默认宽高，全部清空会清空两倍的宽高。

- 自定义清空

    this.clearRect(0,0,100,100)

> 清空从`x=0,y=0`的坐标开始，到`x=100,y=100`终止。

- 圆形区域清空

    this.clearRect(0,0,10)

> 清空`x=0,y=0`并且半径为`10`的区域。

### # `BaseModel:drowRadiusRect`

> 绘制含有圆角的矩形。

参数 | 类型 | 备注
--- | --- | ---
`x` | `number` | 起点X
`y` | `number` | 起点Y
`w` | `number` | 终点X(宽度)
`h` | `number` | 终点Y(高度)

    this.drowRadiusRect(0, 0, 100, 100, 10)
    // 需要自己绘制
    this.ctx.fill()

> 绘制`x=0,y=0`宽高为`100`并圆角为`10`的矩形。

### # `BaseModel:drawText`

> 绘制默认文本。

参数 | 类型 | 备注
--- | --- | ---
`params?:` | `DrawTextParamsType` | 绘制文本参数

### # `BaseModel:drawText.DrawTextParamsType`

参数 | 类型 | 默认值 | 备注
--- | --- | --- | ---
`esGap` | `number` | `0` | 格外的文本空隙(本身textGap+fontSize)
`x` | `number` | `0` | x轴
`text` | `string` | `加载中...` | 文本
`textColor` | `string` | `rgba(64,158,255,1)` | 文本颜色

    this.drawText()

> 绘制`x=0,y=0`宽高为`100`并圆角为`10`的矩形。

### # `BaseModel:clearAnimationFrame`

> 清空(停止)`requestAnimationFrame`。

参数 | 类型 | 备注
--- | --- | ---
`id` | `number` | `animationId`

    this.clearAnimationFrame(element.$store.animationId)

> 传入的id是`requestAnimationFrame`返回的`id`，`WebLoading`在store中已经保存。

### # 3、`BaseModel` 技巧

### # 动态的options

> 无论是初始化还是在`run`函数中，修改`options`都是实时动态的，以此可以控制`requestAnimationFrame`触发的延迟时间不那么规律。

    this.run(()=>{
        // 动态修改delay会影响run函数的触发
        this.options.delay = 10
    })

## # html配置方式

> `options` 中添加html配置即可，它的优先级大于model与`custom`。

### # 初始化

    import initLoading from 'web-loading/src/loading'
    let webLoading = initLoading({
        html:`<div class="spinner">
        <svg viewBox="25 25 50 50" class="circular">
            <circle stroke-miterlimit="10" stroke-width="3" fill="none" r="20" cy="50" cx="50" class="path"></circle>
        </svg>
    </div>`
    })

> html优先级大于custom。

### # css动画

    .spinner {
      --red: #d62d20;
      --blue: #0057e7;
      --green: #008744;
      --yellow: #ffa700;
      position: relative;
      width: 60px;
    }

    .spinner:before {
      content: "";
      display: block;
      padding-top: 100%;
    }

    .circular {
      animation: rotate73451 2s linear infinite;
      height: 100%;
      transform-origin: center center;
      width: 100%;
      position: absolute;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
      margin: auto;
    }

    .path {
      stroke-dasharray: 1, 200;
      stroke-dashoffset: 0;
      animation: dash0175 1.5s ease-in-out infinite, color7123 6s ease-in-out infinite;
      stroke-linecap: round;
    }

    @keyframes rotate73451 {
      100% {
        transform: rotate(360deg);
      }
    }

    @keyframes dash0175 {
      0% {
        stroke-dasharray: 1, 200;
        stroke-dashoffset: 0;
      }

      50% {
        stroke-dasharray: 89, 200;
        stroke-dashoffset: -35px;
      }

      100% {
        stroke-dasharray: 89, 200;
        stroke-dashoffset: -124px;
      }
    }

    @keyframes color7123 {
      100%, 0% {
        stroke: var(--red);
      }

      40% {
        stroke: var(--blue);
      }

      66% {
        stroke: var(--green);
      }

      80%, 90% {
        stroke: var(--yellow);
      }
    }

> class命名尽量隔绝外部元素。