# MinAs 库帮助文档

## 简介

MinAs 是一个运行在微信小程序的JavaScript 库，仅仅10几kb左右，旨在让开发者能像使用 pixijs和Flash AS3 一样，在微信小程序的 Canvas 中优雅地使用精灵Sprite、补简动画Tween、逐帧动画精灵AnimatedSprite、摄像机Camera视线跟随。同样也特别适合那些想在微信小程序中开发小游戏功能的项目,又或者你想做个游戏界面风格的应用小程序。

后续作者会推出使用这个库开发的简单的小游戏示例、ui库等。

## 功能特性

- 仅仅10几kb左右，和那些动则几百kb的库相比，太适合寸土寸金的小程序了。
- 支持创建普通精灵（`Sprite`）和动画精灵（`AnimatedSprite`）。
- 提供资源加载器，方便加载图片和动画资源。
- 支持精灵的变换动画和事件处理。
- 支持简单的Tween补间动画。
- 支持Camera摄像机实时跟随。
- 
## 版本历史

- **v1.0.0**：初始版本，支持基本功能。
- **v1.0.8**：新增Layer图层类、Camera舞台相机、minAs添加屏幕宽高设置。
- **v1.0.9**：视线跟随lookAt，添加offset偏移参数。
- **v1.1.0**：新增Shape类。

## 安装与导入

### 小程序中使用
直接复制min-as.umd.js文件到小程序的文件夹中比如utils中，然后在微信小程序的*.js文件中引入。
```javascript:
  import MinAs, { Sprite, AnimatedSprite } from './utils/min-as.umd.js'
```
### uniapp中使用
```javascript:
  npm install min-as

  import MinAs, { Sprite, AnimatedSprite } from 'min-as'
```

## 开始使用
### uniapp中使用
- uniapp完整代码，需要注意canvas的调用方式，demo是vue2的写法，vue3写法差不多。
```javascript:
    <template>
      <view class="content">
        <canvas type="2d" id="myCanvas" class="gamecont" :disable-scroll="true" @touchstart="touchEvent"
          @touchmove="touchEvent" @touchend="touchEvent" @touchcancel="touchEvent"></canvas>
      </view>
    </template>

    <script>
    import MinAs, { Sprite, AnimatedSprite } from "min-as"
    let minAs = null
    export default {
      data() {
        return {
          title: 'Hello'
        }
      },
      mounted() {
        const info = uni.getSystemInfoSync()
        const sw = info.screenWidth //获取屏幕宽高
        const sh = info.windowHeight //获取屏幕宽高
        const ratio = info.devicePixelRatio
        //#ifdef MP-WEIXIN

        wx.createSelectorQuery()
          .select('#myCanvas')
          .node(({ node: canvas }) => {
            //新版canvas只能这么获取
            const ctx = canvas.getContext('2d')
            canvas.width = sw * ratio
            canvas.height = sh * ratio
            ctx.scale(ratio, ratio)
            //初始化minAs实例   
            minAs = new MinAs()
            minAs.init({ 
              canvas,
              screenWidth: sw,
              screenHeight: sh
            })
            //
            const sprite = new Sprite({
              x: 10,
              y: 10,
              width: 375,
              height: 100,
              backgroundColor: "#f20"
            })
            minAs.appendChild(sprite)
            //开始渲染
            minAs.render()

          }).exec();

        //#endif
      },
      methods: {
        //#ifdef MP-WEIXIN
        touchEvent(e) {
          //小程序事件绑定至minAs实例
          if (minAs) {
            minAs.dispatchEvent(e)
          }
        }
        //#endif
      }
    }
    </script>

    <style lang="scss" scoped>
    .content {
      width: 100vw;
      height: 100vh;

      .gamecont {
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
        z-index: 0;
      }
    }
    </style>

```
### 原生微信小程序中使用
- 微信*.wxml部分片段。

```html:
<canvas type="2d" id="myCanvas" bind:touchstart="touchEvent"
      bind:touchmove="touchEvent" bind:touchend="touchEvent" bind:touchcancel="touchEvent"></canvas>
```

- 微信*.js部分片段。

```javascript:
import MinAs, { Sprite, AnimatedSprite } from 'min-as'
onReady() {
  const query = wx.createSelectorQuery()
  query.select('#myCanvas')
    .fields({ node: true, size: true })
    .exec((res) => {
      const minAs = new MinAs()
      const canvas = res[0].node
      const ctx = canvas.getContext('2d')
      const dpr = wx.getSystemInfoSync().pixelRatio
      canvas.width = res[0].width * dpr
      canvas.height = res[0].height * dpr
      ctx.scale(dpr, dpr)
      //开始初始化minAs
      minAs.init({ 
        canvas,
        screenWidth: res[0].width,
        screenHeight: res[0].height
      })
      //定义红色精灵
      const sprite = new Sprite({ x: 100, y: 100, width: 100, height: 100, backgroundColor: 'red' })
      //监听事件，如果无效请检查是否绑定了touchEvent事件
      sprite.on("touchstart", (e) => {
        console.log("红色精灵被点击:", e)
      }
      minAs.appendChild(sprite)
      minAs.render()
      //初始化结束
    })
},
touchEvent(e) {
  //非常重要，将微信小程序事件传递给minAs
  if (this.minAs) {
    this.minAs.dispatchEvent(e)
  }
}

```

## 补间动画

MinAs 支持使用 Tween 进行补间动画。以下是如何创建和使用 Tween 的示例js片段：

```javascript:
  // 假设我们有一个精灵对象 sprite, 需要将它移动到 (300, 300) 位置, 并在 2 秒内完成, 运动曲线减速"Out"
  const sprite = new Sprite({ x: 0, y: 0, width: 100, height: 100, backgroundColor: 'red' })
  const tween = minAs.transition(sprite,  { 
    x: 300, 
    y: 300,
    duration: 2, 
    timing:"Out"
    onEnd: () => {
      console.log("动画结束")
    } 
  })
  // 假设我们有一个精灵对象 sprite, 需要将它移动到 (300, 300) 位置, 然后再运行到(400, 400)位置，一直往复运动"
  const sprite = new Sprite({ x: 0, y: 0, width: 100, height: 100, backgroundColor:'green' })
  const tweenCtr = minAs.transitionList(sprite,  [
    { x: 300, y: 300, duration: 2, timing:"Out" },
    { x: 400, y: 400, duration: 2, timing:"Out" }
  ], {
    loop: true,
    onEnd: () => {
      console.log("动画结束")
    }
  })

```

MinAs只是封装简单的匀速、加速、减速、先加速再减速。如果需要更高级更精细的运动，推荐直接使用[tween.js](https://createjs.com/tweenjs)库进行操作,。

## 逐帧动画

MinAs 支持逐帧动画，允许开发者创建逐帧动画精灵。以下是如何创建和使用逐帧动画的示例：

```javascript:
   const urls = {
    laoshu: "https://**********demodata/laoshu.json",
   }
   //如果想修改json默认图片地址，还可以自定义贴图地址
   //  const urls = {
   //     laoshu2: {
   //       url: "https://**********demodata/laoshu.json",
   //       metaImage: "https://*********/**.png"
   //     }
   //   }
   minAs.assetsLoad(urls).then(keys=> {
          const anSprite = new AnimatedSprite({
            x: 400,
            y: 200,
            width: 48,
            height: 48,
            backgroundColor: "#f20",
            currentFrame: 3,
            json: minAs.getAssets('laoshu')
          })
          minAs.appendChild(anSprite)
          //运行动画
          //anSprite.play()
   })
```

## 制作逐帧动画过程

1. 直接下载免费制作[工具ShoeBox](https://renderhjs.net/shoebox/),当然其他工具也行
2. 按照doc文件夹中的3张图片操作，即可得到最后要的json文件和纹理贴图。

## 关于摄像机Camera

MinAs 提供了一个简单的摄像机camera实例，用于控制场景的显示区域，舞台在移动其实也就是相机在动。
相机只关联系统内置的roleLayer角色层,相机固定显示宽高为屏幕宽高，相机移动范围不会超出角色层大小。
如果要看到效果一定要使用minAs.setRoleLayer(options)设置角色层大小，宽或者高要超出屏幕宽或高，这要才能参数移动的效果
以下是如何创建和使用摄像机跟随示例3种方法，尽量不要修改相机的视线宽高：

```javascript:
  const roleLayer = minAs.getRoleLayer()
  roleLayer.showHotPolygon = true
  // 直接跟随某个节点
  minAs.setRoleLayer({
        width: 600,
        height: 1000
  })
  minAs.lookAt(spriteA)
  minAs.transition(spriteA,  { 
    x: 500, 
    y: 1300,
    duration: 2, 
    timing:"Out"
  })
  ...
  // 或者在渲染持续修改摄像关联点位置
  minAs.render(()=> {
    minAs.lookAt([x, y])
  })
  ...
  // 或者绘制持续修改摄像关联点位置
  new Sprite({
    onDraw: ()=> {
      minAs.lookAt([x, y])
    }
  })
```

## API 说明

### `MinAs` 类

- **`init(options)`**：初始化 MinAs 实例，传入包含 `canvas` 的配置对象。
  | 参数           | 类型      | 描述                                   |
  | -------------- | --------- | -------------------------------------- |
  | options        | Object    | 初始化数据，目前就一个参数{ canvas }。 |
  | options.canvas | wx.Canvas | 微信小程序的 Canvas 节点对象。         |
  | options.screenWidth | Number\|undefind | 微信小程序的屏幕宽高，不写则为canvas的，这个对使用抗锯齿缩放后的canvas很有用。         |
  | options.screenHeight | Number\|undefind | 微信小程序的屏幕宽高，不写则为canvas的,这个对使用抗锯齿缩放后的canvas很有用。         |
- **`use(BasePlugin, options)`**：加载自定义插件。
  | 参数       | 类型               | 描述                             |
  | ---------- | ------------------ | -------------------------------- |
  | BasePlugin | BasePlugin         | 插件实例(详细请看自定义插件部分) |
  | options    | Object\| undefined | 自定义传递给插件数据             |
- **`destroy()`**： 销毁 MinAs 实例(全局销毁)。
- **`dispatchEvent(e)`**：绑定wx.Canvas事件。
  | 参数 | 类型     | 描述                   |
  | ---- | -------- | ---------------------- |
  | e    | wx.Event | 微信小程序的事件对象。 |
- **`on(eventName, callback)`**：注册事件。
  | 参数      | 类型     | 描述                                                                    |
  | --------- | -------- | ----------------------------------------------------------------------- |
  | eventName | String   | 不要使用内置名称： "touchstart", "touchmove", "touchend", "touchcancel" |
  | callback  | Function | 回调函数,返回事件e对象                                                  |
- **`off(eventName, callback)`**： 移除事件。
  | 参数      | 类型     | 描述                                                                   |
  | --------- | -------- | ---------------------------------------------------------------------- |
  | eventName | String   | 不要使用内置名称："touchstart", "touchmove", "touchend", "touchcancel" |
  | callback  | Function | 回调函数,返回事件e对象                                                 |
- **`emit(eventName, ...args)`**：触发事件。
  | 参数      | 类型   | 描述                                                                   |
  | --------- | ------ | ---------------------------------------------------------------------- |
  | eventName | String | 不要使用内置名称："touchstart", "touchmove", "touchend", "touchcancel" |
  | args      | any    | 任意数据                                                               |
- **`render(callback)`**：开始渲染。
  | 参数     | 类型                 | 描述     |
  | -------- | -------------------- | -------- |
  | callback | Function\| undefined | 持续调用 |
- **`appendChild(sprite)`**：将精灵添加到渲染列表。
  | 参数   | 类型   | 描述     |
  | ------ | ------ | -------- |
  | sprite | Sprite | 精灵对象 |
- **`removeChild(sprite)`**：移除子精灵。
  | 参数   | 类型   | 描述     |
  | ------ | ------ | -------- |
  | sprite | Sprite | 精灵对象 |
- **`removeAll()`**：移除所有子精灵。
- **`assetsLoad(urls, progressCallback)`**：加载资源。
  | 参数                 | 类型                     | 描述                                                                                                                   |
  | -------------------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------- |
  | `urls`             | String\| Array \| Object | 当为string类型时候加载一个，Array加载多个。（单个数据可以直接使用string类型，也可以写成{url: string}这样的object结构） |
  | `progressCallback` | Function\| undefined     | 加载进度，当urls为数组时才有意义，返回加载百分比number。                                                               |
  | 返回值               | 类型                     | 描述                                                                                                                   |
  | res                  | Promise                  | 回一个 Promise，完成后返回加载的资源键名或者键名列表                                                                   |
- **`getAssets(key)`**：根据键名获取已加载的资源。
  | 参数   | 类型               | 描述                                                                   |
  | ------ | ------------------ | ---------------------------------------------------------------------- |
  | key    | String\| undefined | 资源建名称                                                             |
  | 返回值 | 类型               | 描述                                                                   |
  | key    | String\| Array     | 当key为string返回当前资源的内容，key为空或者不存在时候返回全部资源列表 |
- **`transition(sprite, options)`**：内置tween过渡方法。
  | 参数             | 类型                           | 描述                                                                                          |
  | ---------------- | ------------------------------ | --------------------------------------------------------------------------------------------- |
  | sprite           | Sprite                         | 精灵实例                                                                                      |
  | options          | Object                         | 过渡配置参数                                                                                  |
  | options.duration | numbet                         | \undefind                                                                                     |
  | options.timing   | "Linear", "In", "Out", "InOut" | 运动函数名称，默认Linear                                                                      |
  | options.onUpdate | Function\| undefind            | 运动结束回调，返回运动的srpite精灵                                                            |
  | options.onEnd    | Function\| undefind            | 运动结束回调，返回srpite精灵                                                                  |
  | options.[..res]  | any                            | sprite精灵中需要变化到的任意number属性,比如精灵的x,y,scaleX,scaleY,width,height,angle,opacity |
  | 返回值           | 类型                           | 描述                                                                                          |
  | tween            | Tween                          | 返回一个tween运动对象                                                                         |
  | tween.resume     | Function                       | 恢复动画                                                                                      |
  | tween.pause      | Function                       | 暂停动画                                                                                      |
  | tween.stop       | Function                       | 停止动画                                                                                      |
- **`transitionList(sprite, optionslist, options)`**：内置tween过渡方法列表，多个transition组成的运动列表。
  | 参数             | 类型                | 描述                                                                   |
  | ---------------- | ------------------- | ---------------------------------------------------------------------- |
  | sprite           | Sprite              | 精灵实例                                                               |
  | optionslist      | Array               | 多个transition配置参数的集合，会按照顺序一个完成再执行下一个，持续下去 |
  | options.loop     | Boolean\| undefind  | 是否循环 ,默认true                                                     |
  | options.onUpdate | Function\| undefind | 运动结束回调，返回运动的srpite精灵                                     |
  | options.onChange | Function\| undefind | 运动切换的时候回调，返回运动的srpite精灵和下标index                    |
  | options.onEnd    | Function\| undefind | 运动结束回调，返回srpite精灵, 当loop为true才有意义                     |
  | 返回值           | 类型                | 描述                                                                   |
  | tweenCtr         | Object              | 返回一个控制运动的对象                                                 |
  | tweenCtr.resume  | Function            | 恢复动画                                                               |
  | tweenCtr.pause   | Function            | 暂停动画                                                               |
  | tweenCtr.stop    | Function            | 停止动画                                                               |
- **`lookAt(obj, offset)`**：内置相机视线跟随指定精灵元素，只能针对一个目标。
  | 参数              | 类型                | 描述                                                                   |
  | ---------------- | ------------------- | ---------------------------------------------------------------------- |
  | obj        | Sprite\|Array\|undefind              |                         |
  | obj为Sprite       |               |相机跟随sprite精灵运动                        |
  | obj为Array       |[x,y]               |相机跟随[x,y] 点运动                        |
  | obj为undefind       |空               |停止相机跟随                       |
  | offset        | Array\|undefind              |   相机偏移[x,y]                      |
- **`setRoleLayer(options)`**：控制角色_roleLayer层属性方法。
  | 参数              | 类型                | 描述                                                                   |
  | ---------------- | ------------------- | ---------------------------------------------------------------------- |
  | options.x        | Number|\undefind              | x                        |
  | options.y        | Number|\undefind              | y                        |
  | options.width        | Number|\undefind              | x                        |
  | options.height        | Number|\undefind              | y                        |
- **`getRoleLayer()`**：获取角色_roleLayer实例。  

### `Sprite` 类

构造函数接受一个配置对象，支持以下属性：

- `x`：精灵的 x 坐标。默认0
- `y`：精灵的 y 坐标。默认0
- `width`：精灵的宽度。默认0
- `height`：精灵的高度。默认0
- `scaleX`：精灵的水平缩放比例。默认1
- `scaleY`：精灵的垂直缩放比例。默认1
- `backgroundColor`：精灵的背景颜色。默认""
- `texture`：精灵的纹理（图片）。默认""
- `angle`: 精灵的旋转角度。默认0
- `opacity`: 精灵的透明度。默认1
- `pointerEvents`: 指针判断触摸事件，默认true,不会穿透到子元素。layer类默认false
- `hotPolygon`: 元素的热点多边形, 用于事件碰撞检测。默认最小外接矩形[[0, 0], [width, 0], [width, height], [0, height]]。如果是异形需要精准点击检查，这里可以自己定义热区数组
  比如下面就会显示一个红色矩形框，里面是一个五角星的事件点击区域，只有五角星可以点击，其他区域皆不可：
  ```javascript:
  const sprite = new Sprite({
      x: 0,
      y: 0,
      width: 200,
      height: 200,
      hotPolygon: [
        [100, 0], [138, 72], [200, 72], [146, 116],
        [162, 180], [100, 146], [38, 180], [54, 116], [0, 72], [62, 72]
      ],
      showHotPolygon: true,
      backgroundColor: 'red',
  })
  ```
- `showHotPolygon`: 是否显示热区多边形。默认false，主要用来调试时候看热区位置和大小
- `visible`: 精灵的可见性。默认true
- `onDraw`: 绘制回调函数Function，返回精灵实例，用于自定义绘制逻辑。默认空

方法：

- **`on(eventName, callback)`**：监听事件。
  | 参数      | 类型                                                 | 描述                   |
  | --------- | ---------------------------------------------------- | ---------------------- |
  | eventName | "touchstart", "touchmove", "touchend", "touchcancel" | 监听事件               |
  | callback  | Function                                             | 回调函数,返回事件e对象 |
- **`appendChild(sprite)`**：将精灵添加到渲染列表。
  | 参数   | 类型   | 描述     |
  | ------ | ------ | -------- |
  | sprite | Sprite | 精灵对象 |
- **`removeChild(sprite)`**：移除子精灵。
  | 参数   | 类型   | 描述     |
  | ------ | ------ | -------- |
  | sprite | Sprite | 精灵对象 |
- **`removeAll()`**：移除所有子精灵。
- **`removeFromParent()`**：将此对象从其当前父对象中移除。
- **`toFront()`**：将精灵层级置顶。
- **`toBack()`**：将精灵层级置底。
- **`getIndex()`**：获取当前精灵层级。
- **`setIndex(index)`**：将精灵层级到指定位置。

### `AnimatedSprite` 类

继承自 `Sprite`，构造函数额外支持以下属性：

- `currentFrame`：初始帧索引。默认0
- `loop`：是否循环播放。默认true
- `animationSpeed`：帧率number, 默认15(帧/秒)
- `json`：动画配置 JSON。来自ShoeBox 导出的帧动画标准json文件,详细请看下面帧动画部分。

方法：

- **`play(frame)`**：播放动画，可指定播放帧。
  | 参数  | 类型              | 描述                                 |
  | ----- | ----------------- | ------------------------------------ |
  | frame | Number\| undefind | 为空时候从头播放，不为空从指定帧播放 |
- **`pause()`**：暂停动画。
- **`stop(frame)`**：停止动画，可指定停止帧。
  | 参数   | 类型              | 描述                                 |
  | ------ | ----------------- | ------------------------------------ |
  | sprite | Number\| undefind | 为空时停在最后一帧，不为空停在指定帧 |

### `Layer` 类
图层类继承自 `Sprite`，构造函数额外支持以下属性：
- `pointerEvents`: 指针判断触摸事件，默认false,穿透到子元素。
图层是特殊的精灵，它主要用来给游戏分层，比如背景层、前景层、UI层等。
roleLayer角色层自带摄像机的控制,可以使用minAs.getRoleLayer()获取实例。但是建议使用setRoleLayer(options)设置x,y,width,height。
只有舞台minAs实例appendChild添加图层才有意义。
舞台的层级是这样的：
```plaintext
    
  ├─ state舞台图层  
  │  ├─ roleLayer 角色层，特殊图层，被相机Camera所有关联。还有minAs.appendChild的Sprite元素都会放在这个图层中  
  │  └─ 其他用户自己新建的图层Layer，比如不想受到相机影响图层，minAs.appendChild的Layer图层都会和roleLayer放在同一个层级。
``` 
### `Shape` 图形类
可以.graphics绘制各种图形，比如矩形、圆形、椭圆、多边形、直线、曲线等。
代码示例:
```javascript:
  const shape = new Shape({
      x: 0,
      y: 0,
      width: 100,
      height: 100
  })
  shape.graphics.beginFill('#f20') // 填充颜色
  shape.graphics.drawRect(0, 0, 100, 100) // 绘制矩形
```  
从上面的示例可以看出，其实shape.graphics就是一个CanvasRenderingContext2D对象ctx，你可以直接使用这个对象进行绘制。
注：shape对象是不带任何事件操作的，得加入到精灵中才能使用
## 自定义插件

MinAs 提供了一个插件机制，允许开发者自定义插件以扩展其功能。以下是如何创建和使用自定义插件的步骤：

你可以直接复制项目中的plugin文件夹到你的任意位置，然后引入文件，使用minAs.use加载插件

假设我们开发的插件叫DemoPlugin，我们先定义类然后extends BasePlugin继承一下这个基础类，然后就可以使用minAs内部的一些方法

如果需要将插件的方法挂在到minAs对象上去，只要在apis数组中写上方法名称即可

比如你写了个方法叫demoMethod，你需要static apis = ["demoMethod"], 然后就可以在外部使用minAs.demoMethod

```javascript:
//引入插件
import DemoPlugin from './plugin/DemoPlugin.js'
//挂载插件
minAs.use(DemoPlugin, {})
//使用插件方法
minAs.demoMethod()
```
## 可以打赏的话

[如果觉得这个库对你有帮助，欢迎点击这里跳转打赏](https://qianqianquege.com/dream/)

## 联系作者
- 邮箱：wugemianfei@163.com
- 网址: https://qianqianquege.com/dream/
 