npm package: @karl_fang/mosaic
Description: This is a mosaic tool
Author: karl fang
Latest version: 1.0.0

# mosaic

## 作者的话

这是一个简单的图片马赛克处理小工具，因为突然看到网上有人做了马赛克，因此自己也打算做一个。



## 使用

```js
import DrawTool from './index.js';

const side = 50;
const draw = new DrawTool({
	el: canvas,
  imageData: res,
  immediate: false,
  mosaic: {
  	side
  }
});
```



## 构造函数

- 使用方法：`new DrawTool(config) `

| 参数         | 功能                   | 类型                                              | 默认值  | 是否必须 |
| ------------ | ---------------------- | ------------------------------------------------- | ------- | -------- |
| el           | 渲染canvas的节点       | DOM                                               |         | ✅        |
| imageData    | canvas的像素信息       | ImageData                                         |         | ✅        |
| immediate    | 是否立即显示马赛克     | Boolean                                           | true    | ❎        |
| mosaic.side  | 每个马赛克边长的像素值 | Number                                            | 10      | ❎        |
| mosaic.color | 每个马赛克边长的颜色   | "first"\|"last"\|"random"\|valid color\|(i,j)=>{} | 'first' | ❎        |

> valid color: 也就是合法的颜色字符串(十六进制)，如#f00或#123456
>
> (i,j)=>{}: 传入的参数代表是第i行第j列的马赛克



## mosaic

- 使用方法：`DrawTool.prototype.mosaic(config) `

| 参数    | 功能                   | 类型   | 默认值      | 是否必须 |
| ------- | ---------------------- | ------ | ----------- | -------- |
| width   | 马赛克的宽度           | Number | el.width    | ❎        |
| height  | 马赛克的高度           | Number | el.height   | ❎        |
| dx      | 马赛克的起点的横坐标   | Number | 0           | ❎        |
| dy      | 马赛克的起点的纵坐标   | Number | 0           | ❎        |
| newSide | 每个马赛克边长的像素值 | Number | mosaic.side | ❎        |



## scatter

- 使用方法：`DrawTool.prototype.scatter(duration, fn) `

| 参数     | 功能                       | 类型     | 默认值              | 是否必须 |
| -------- | -------------------------- | -------- | ------------------- | -------- |
| duration | 动画时长(秒)               | Number   | 1                   | ❎        |
| fn       | 动画开始前每个像素的颜色值 | Function | (i) => [0, 0, 0, 0] | ❎        |

> (i) => [0, 0, 0, 0]: 默认每个像素的颜色为透明色，传入的参数i代表第i个像素值（将canvas展平成一行后）



## setSide

- 使用方法：`DrawTool.prototype.setSide(newVal) `

| 参数   | 功能                       | 类型   | 默认值 | 是否必须 |
| ------ | -------------------------- | ------ | ------ | -------- |
| newVal | 设置每个马赛克边长的像素值 | Number |        | ✅        |



## setColor

- 使用方法：`DrawTool.prototype.setColor(fn) `

| 参数 | 功能                 | 类型      | 默认值 | 是否必须 |
| ---- | -------------------- | --------- | ------ | -------- |
| fn   | 每个马赛克边长的颜色 | (i,j)=>{} |        | ✅        |

> (i,j)=>{}: 传入的参数代表是第i行第j列的马赛克，同mosaic.color



## Example

### 基本代码

```html
<body>
  <canvas id="myCanvas"></canvas>

  <script type="module">
    import DrawTool from './index.js';
    // 获取<canvas>元素和绘图上下文
    const canvas = document.querySelector("#myCanvas");
    const ctx = canvas.getContext('2d');
    function request(url) {
      return new Promise((resolve, reject) => {
        const img = new Image();
        img.src = url;
        img.onload = function () {
          var scale = 0.5;

          // 计算缩小后的宽度和高度
          const width = img.width * scale;
          const height = img.height * scale;

          canvas.width = width;
          canvas.height = height;

          ctx.drawImage(img, 0, 0, width, height);
          resolve(ctx.getImageData(0, 0, width, height));
        }

        img.onerror = function () {
          reject('img load fail!');
        }
      })
    }
  </script>
</body>
```



### 显示马赛克

```js
request('./demo.jpeg').then(res => {
  const side = 50;
  const draw = new DrawTool({
    el: canvas,
    imageData: res,
    immediate: true,
    mosaic: {
      side
    }
  });
}).catch(console.error);
```



效果如下：

<img src="./gif/1.png" width="50%" />



### 马赛克加载动画

```js
request('./demo.jpeg').then(res => {
  const side = 50;
  const draw = new DrawTool({
    el: canvas,
    imageData: res,
    immediate: false,
    mosaic: {
      side
    }
  });
  
  let cnt = 50, step = 1, timer = null;
  const loading = () => {
    cnt -= step;
    draw.setSide(cnt);
    draw.mosaic();
    if(cnt > 0) {
      timer = requestAnimationFrame(loading);
    } else {
      cancelAnimationFrame(timer);
    }
  }
  requestAnimationFrame(loading);
  
}).catch(console.error)
```



效果如下：

<img src="./gif/1.gif" width="50%" />



### 马赛克画笔

```js
request('./demo.jpeg').then(res => {
  const side = 50;
  const draw = new DrawTool({
    el: canvas,
    imageData: res,
    immediate: false,
    mosaic: {
      side
    }
  });
  
  
  canvas.addEventListener('mousedown', (e) => {

  	const { offsetTop, offsetLeft } = canvas;

      const move = e => {
        const { pageX, pageY } = e;
        draw.mosaic({
          dx: pageX - offsetLeft,
          dy: pageY - offsetTop,
          newSide: 5
        });
      }
      
    	document.addEventListener('mousemove', move);

    	document.addEventListener('mouseup', () => {

        document.removeEventListener('mousemove', move);
      })
  	})
  
}).catch(console.error)
```



效果如下：

<img src="./gif/2.gif" width="50%" />



### 散点加载动画

```js
request('./demo.jpeg').then(res => {
  const side = 50;
  const draw = new DrawTool({
    el: canvas,
    imageData: res,
    immediate: false,
    mosaic: {
      side
    }
  });
  draw.scatter(2, (i) => {
    if(i % 3 === 0) {
      return [255, 0, 0, 255]
    } else if(i % 3 === 1) {
      return [0, 255, 0, 255]
    } else {
      return [0, 0, 255, 255];
    }
  });
  
}).catch(console.error)
```



效果如下：

<img src="./gif/3.gif" width="50%" />