├── .babelrc ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── README.md ├── README_EN.md ├── README_ZH.md ├── build ├── webpack.base.js ├── webpack.build.js └── webpack.example.js ├── dist ├── mcanvas.node.js └── mcanvas.web.js ├── example ├── dist │ ├── css │ │ └── main.css │ ├── images │ │ ├── 1.7a9a8328.jpg │ │ ├── 3.f15f13f2.jpg │ │ ├── ear.c5b725cb.png │ │ └── watermark.c925393e.jpg │ ├── index.html │ ├── node.js │ ├── web.js │ └── zepto.min.js ├── images │ ├── 1.jpg │ ├── 1.png │ ├── 2.jpg │ ├── 3.jpg │ ├── ear.png │ ├── icon_download.png │ ├── loading.gif │ ├── mask.png │ ├── neck.png │ ├── nose.png │ ├── p.jpg │ ├── p2.jpg │ ├── pinch.png │ ├── transparent.gif │ └── watermark.jpg ├── index.html ├── main.scss ├── node.ts ├── type.d.ts ├── web.ts └── zepto.min.js ├── package-lock.json ├── package.json ├── src ├── canvas │ ├── index.ts │ ├── node.ts │ └── web.ts ├── index.ts ├── lib.d.ts ├── modules │ ├── mcanvas.ts │ └── mimage.ts ├── types │ ├── commom.d.ts │ ├── mcanvas.d.ts │ └── mimage.d.ts └── utils │ ├── crop.ts │ ├── extend.ts │ ├── filter.ts │ ├── index.ts │ ├── is.ts │ └── queue.ts ├── tsconfig.json └── tslint.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["es2015", { "modules": false }]], 3 | "plugins": [ 4 | "syntax-flow", 5 | "transform-class-properties", 6 | "transform-flow-strip-types", 7 | "transform-object-rest-spread" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | test 3 | rollup.config.js 4 | .babelrc 5 | .eslintrc 6 | .eslintignore 7 | .gitignore 8 | example 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 更新: 2 | - 2.0.8 3 | - MCanvas.text 新增 wordBreak 属性,用于设置自动换行点; 4 | - 更新文档; 5 | - 修复声明文件错误; 6 | 7 | - 2.0.0 8 | - 重整项目架构,更好的模块化代码,引入 Typescript, Promise; 9 | - 重写 MCrop, 优化实现方式,新增 radius; 10 | - 原 MCanvas 调整为 MComposer, 11 | - 优化 `mc.rect`,新增 radius; 12 | - 优化 `mc.add.crop`, 新增 radius; 13 | - `mc.draw` 方法新增 exportConfig: 'base64' | 'canvas' 配置导出格式; 14 | - 抽离 Queue 类; 15 | - 新增 MFilter 滤镜器: 16 | - 高斯模糊; 17 | - 翻转; 18 | - 灰白; 19 | 20 | - 1.3.9 (2019-5-10) 21 | - 优化文档; 22 | - 修复 MCrop 裁剪时图片格式的设置问题; 23 | - 优化 rollup 打包,导出 MCrop 方法; 24 | 25 | - 1.3.8 (2019-5-9) 26 | - 升级新版 rollup 脚手架 (rollup-typescript-startkit); 27 | - 新增图片裁剪工具 MCrop, 更新文档; 28 | - 修复小数点计算问题导致图片变模糊的问题; 29 | 30 | - 1.3.4 (6.22) 31 | - 修改文字绘制机制,新增文字旋转参数,`option.pos.rotate`; 32 | 33 | - 1.3.2 (4.28) 34 | - `add`函数`width`参数新增`origin`, 表示按原图尺寸绘制; 35 | - 文字绘制新增容错: 当 `options.width < fontSize` 时,则按`fontSize`进行绘制,避免换空行的问题; 36 | - 修复初始化不传参数报错的问题; 37 | 38 | - 1.3.1 (4.28) 39 | - 修复`text`调用多次会相互影响的问题; 40 | 41 | - 1.3.0 (2.22) 42 | - 修改新建实例时的参数格式,较为统一且便于拓展; 43 | - 优化储存管理分类,实例上新增 `fn` 及 `data` 属性; 44 | - 新增Api `mc.rect` / `mc.circle`, 用于绘制矩形和圆形; 45 | - 纠正`mc.text`方法定位不准确的问题,并新增描边文字,渐变文字,文字阴影的功能; 46 | - 新增错误回调,当图片加载失败是会触发由 `draw` 中传入的 `error`回调; 47 | - 优化文档及代码; 48 | 49 | - 1.2.9 (12.20) 50 | - 新增Api `mc.clear`: 清空画布; 51 | 52 | - 1.2.8 (10.27) 53 | - 修复`background` API接口的`left/top`参数纵向居中问题,另外新增支持`left:'center'`,统一API; 54 | 55 | - 1.2.6 (9.27) 56 | - `background` API接口的`left/top`参数新增`0%`、`50%` 和 `100%`; 57 | 58 | - 1.2.5 (9.27) 59 | - 修复文档错误; 60 | 61 | - 1.2.3 (9.26) 62 | - 增加素材`canvas`大小的限制,ios8为2096,其余限制为4096; 63 | 64 | - 1.2.2 (9.25) 65 | - 增加参数的向下取整; 66 | 67 | - 1.2.1 (9.7) 68 | - 修改 `add` 函数中的 `crop` 裁剪功能BUG; 69 | 70 | - 1.2.0 (8.28) 71 | - 修改`mc.draw()`的传参形式,增加可配置的导出图片格式及图片质量,兼容旧版`api`; 72 | - 增加一些可能引发错误的错误提醒,便于业务方定位问题; 73 | 74 | - 1.1.8 (8.13) 75 | - 更新文档; 76 | 77 | - 1.1.7 (8.12) 78 | - 增加`mc.background(bg)`接口初次调用时的错误提醒,修改异常提示的内容; 79 | 80 | - 1.1.6 (8.11) 81 | - 修复竖直素材下,为了规避裁剪的画布放大倍数错误的问题; 82 | 83 | - 1.1.5 (8.10) 84 | - 增加 `add/background/watermark` 传入的`image`可以为 `canvas`; 85 | 86 | - 1.1.4 (8.9) 87 | - 增加英文文档; 88 | - 增加初始化时可配置默认背景色; 89 | 90 | - 1.1.3 (8.3) 91 | - 修改一处警告语法错误; 92 | - 增加画素材的小画布放大机制,用于解决因旋转导致的裁剪; 93 | - 增加小画布绘制后清空机制,避免因素材太大导致占用内存过高; 94 | 95 | - 1.0.9 (7.27) 96 | - 获取图片宽度由 `img.width` 修改为 `img.naturalWidth`,以避免直接传入`DOM`节点时获取宽高被`css`影响; 97 | - 增加背景图片错误提醒; 98 | 99 | - 1.0.8 (7.27) 100 | - 调整构造函数结构,使用 `prototype` 原型链的形,以兼容不带 `new` 的调用方式; 101 | - 调整完善文档; 102 | - 删除错误的包依赖,调整包名; 103 | 104 | - 1.0.2 (7.26) 105 | - 修正完善文档 106 | - 修改 bigStyle --> largeStyle; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mcanvas 2 | 3 | New version 2.0.0 is coming. It contains some new features: 4 | 5 | - Support new function: 6 | - image merge; 7 | - image compress; 8 | - image crop; 9 | - image filter; 10 | - Support usage in node.js; 11 | - .draw() support usage by promise; 12 | - Rewrite in typescript; 13 | 14 | **[Example](http://guoxiaodong.net/)** 15 | 16 | **[Git](https://github.com/xd-tayde/mcanvas)** 17 | 18 | ## Document 19 | 20 | [English](https://github.com/xd-tayde/mcanvas/blob/master/README_EN.md) | [中文版](https://github.com/xd-tayde/mcanvas/blob/master/README_ZH.md) 21 | 22 | ## Introduction 23 | 24 | mcanvas is a image handler plugin that can easily merge, crop, compress, filter the image and export a image of base64 finally. It provides some simple api that based on canvas, in order to make your work more efficiently and conveniently. 25 | 26 | ## Installation 27 | 28 | - You can download the latest version from the [GitHub](https://github.com/xd-tayde/mcanvas/blob/master/dist) 29 | - use a npm CDN [Web](https://unpkg.com/mcanvas/dist/mcanvas.web.js) and use `window.MCanvas` 30 | 31 | - Or you can install via npm: 32 | 33 | ```js 34 | npm install mcanvas --save 35 | 36 | import { MCanvas } from 'mcanvas' 37 | ``` 38 | 39 | ## Basic Usage 40 | 41 | - `MCanvas` 42 | 43 | ```js 44 | // create the canvas by width and height; 45 | import { MCanvas } from 'mcanvas' 46 | 47 | const mc = new MCanvas({ 48 | width, 49 | height, 50 | backgroundColor, 51 | }); 52 | 53 | // prepare background-image 54 | mc.background(image, { 55 | left: 0, 56 | top: 0, 57 | color: '#000000', 58 | type: 'origin', 59 | }) 60 | 61 | // prepare the image material, add into queue; 62 | .add('images/nose.png',{ 63 | width:183, 64 | pos:{ 65 | x:250, 66 | y:369, 67 | scale:0.84, 68 | rotate:1, 69 | }, 70 | }) 71 | 72 | // add text; 73 | .text('normal
smallsmall',{ 74 | width:'300px', 75 | align:'center', 76 | pos:{ 77 | x:0, 78 | y:0, 79 | }, 80 | }) 81 | 82 | // prepare watermark; 83 | .watermark(img ,{ 84 | width:'40%', 85 | pos:'rightBottom', 86 | }) 87 | 88 | // draw all material that prepared before, and get the base64-image 89 | .draw(b64 =>{ 90 | console.log(b64); 91 | }); 92 | ``` 93 | - `MImage` 94 | 95 | ```js 96 | // image handler 97 | // inclue crop / compress / common filter 98 | import { MImage } from 'mcanvas' 99 | 100 | const mi = new MImage('http://mtapplet.meitudata.com/596c72073971d86b5128.jpg') 101 | 102 | mi.filter('blur') 103 | // crop to area by 300 * 300 and center in origin image 104 | .crop({ 105 | x: 'center', 106 | y: 'center', 107 | width: 300, 108 | height: 300, 109 | radius: 10, 110 | }) 111 | // compress into a image that width is 200px and quality is 0.9 112 | .compress({ 113 | width: 200, 114 | quality: .9, 115 | }) 116 | // get the base64-image 117 | .draw(b64 => { 118 | console.log(b64) 119 | }) 120 | ``` 121 | 122 | ## License 123 | 124 | [MIT](https://opensource.org/licenses/MIT) 125 | -------------------------------------------------------------------------------- /README_EN.md: -------------------------------------------------------------------------------- 1 | # Document 2 | 3 | ## Create Instance 4 | 5 | #### `new MCanvas(options)`: 6 | 7 | create the canvas; 8 | 9 | usage: 10 | 11 | ```js 12 | const mc = new MCanvas(options: { 13 | width?: number, 14 | height?: number, 15 | backgroundColor?: string, 16 | }) 17 | ``` 18 | 19 | ## API 20 | 21 | #### 1、 `mc.background(image, options)`: 22 | 23 | prepare background-image; 24 | 25 | > if you use `mc.background(bg)` before , then you can use `mc.background()` to reset to the init background. 26 | 27 | usage: 28 | 29 | ```js 30 | mc.background( 31 | image: string | HTMLImageElement | HTMLCanvasElement, 32 | options?: { 33 | 34 | // type: origin / crop / contain 35 | // origin : the width and height of canvas will be same as the image naturalWidth and naturalHeight, the init width and height will be invalid; 36 | // crop : the image will covered with the canvas, can control crop by left and top; 37 | // contain : the same as background-size:contain; can control postion by left and top; 38 | 39 | type?: 'origin' | 'crop' | 'contain' , 40 | 41 | // the distance of leftTop corner of canvas; 42 | // 100 / '100%' / '100px' 43 | // can use 0% / 50% / 100% to crop by left / center / right; 44 | 45 | left?: number | string, 46 | top?: number | string, 47 | 48 | // the background-color of canvas; 49 | color?: string, 50 | } 51 | ) 52 | ``` 53 | 54 | #### 2、`add(image, options)`: 55 | 56 | prepare the source to draw on canvas; 57 | 58 | usage: 59 | 60 | ```js 61 | mc.add( 62 | image: string | HTMLImageElement | HTMLCanvasElement, 63 | options: { 64 | 65 | // eg. width: 100 / '100%' / '100px'; 66 | width?: number | string, 67 | 68 | crop?: { 69 | // the distance to leftTop corner of source-image 70 | x?: number | string, 71 | y?: number | string, 72 | 73 | // example: 100/'100%'/'100px'; 74 | // the width, height and radius of the the croped area; 75 | width?: number | string, 76 | height?: number | string, 77 | radius?: number | string 78 | }, 79 | 80 | // position of source image; 81 | pos?: { 82 | // eg. x: 250 / '250px' / '100%' / 'left:250' / 'center', 83 | x?: number | string, 84 | y?: number | string, 85 | 86 | // scale based on center point of source; 87 | scale?: number | string, 88 | 89 | rotate?: number | string, 90 | }, 91 | } 92 | ) 93 | ``` 94 | 95 | #### 3、`watermark(image,options)`: 96 | 97 | prepare the watermark; 98 | 99 | ```js 100 | mc.watermark( 101 | image: url/HTMLImageElement/HTMLCanvasElement, 102 | 103 | options: { 104 | // there must use percentage on canvas; 105 | // Default : '40%'; 106 | width?: string, 107 | 108 | // position 109 | // Default : 'rightBottom'; 110 | pos?: 'leftTop' | 'leftBottom' | 'rightTop' | 'rightBottom', 111 | 112 | // the margin to border of canvas; 113 | // Default : 20; 114 | margin?: number, 115 | } 116 | ) 117 | ``` 118 | 119 | #### 4、`text(context,options)`: 120 | 121 | prepare the text and you can use `//
`; 122 | 123 | ```js 124 | mc.text( 125 | // context,there provide 3 style of large/normal/small; 126 | // : largeStyle | small : smallStyle |
127 | context: '大字大字大字常规字体
换行小字小字小字', 128 | options: { 129 | // the width of text line; 130 | // example : width: 100 / '100%' / '100px'; 131 | width?: string | number, 132 | 133 | // align of text line; 134 | align?: 'left' | 'center' | 'right', 135 | 136 | // and you can set the style of text use smallStyle / normalStyle / largeStyle; 137 | 138 | // for example 139 | // the style of contained in 140 | normalStyle?: { 141 | 142 | // font style same as css font 143 | font?: string, 144 | 145 | // font color 146 | color?: string, 147 | 148 | lineheight?: number, 149 | 150 | type?: 'fill' | 'stroke', 151 | 152 | // the width of text's stroke; 153 | lineWidth?: number, 154 | 155 | // word break when changes line, default: true; 156 | wordBreak?: boolean, 157 | 158 | shadow?: { 159 | color?: string, 160 | blur?: number, 161 | offsetX?: number, 162 | offsetY?: number, 163 | }, 164 | 165 | gradient?: { 166 | type?: 1 | 2, // 1: horizontal 2: vertical 167 | colorStop?: string[] // eg. ['red','blue'], 168 | }, 169 | }, 170 | 171 | // the position of text on canvas; 172 | pos?: { 173 | x?: string | number, 174 | y?: string | number, 175 | rotate?: string | number, 176 | }, 177 | }, 178 | ) 179 | ``` 180 | 181 | #### 5、`mc.rect(ops)` 182 | 183 | draw a rectangle; 184 | 185 | ```js 186 | mc.rect( 187 | options: { 188 | // x: 250 / '250px' / '100%' / 'left:250' / 'center', 189 | x?: string | number, 190 | y?: string | number, 191 | 192 | width?: string | number, 193 | height?: string | number, 194 | 195 | strokeWidth?: number, 196 | strokeColor?: string, 197 | 198 | fillColor?: string, 199 | } 200 | ) 201 | ``` 202 | 203 | #### 6、`mc.circle(ops)` 204 | 205 | draw a circle; 206 | 207 | ```js 208 | mc.circle( 209 | options: { 210 | x?: string | number, 211 | y?: string | number, 212 | 213 | radius?: string | number , 214 | 215 | strokeWidth?: string | number, 216 | strokeColor?: string, 217 | 218 | fillColor?: string, 219 | } 220 | ) 221 | ``` 222 | 223 | #### 7、 `draw(fn)`: 224 | 225 | final function ,`add`/`watermark`/`text` must use the `draw()` on the end, and it will export a base64 image,now has supported Promise. 226 | 227 | - `Promise` 228 | 229 | ```js 230 | const base64 = await mc.draw(options) 231 | 232 | // or 233 | mc.draw(options).then(console.log) 234 | ``` 235 | 236 | - `Callback` 237 | 238 | ```js 239 | // 由于异步的缘故,使用回调,获取 base64; 240 | mc.draw( 241 | options: { 242 | // the type of export image; 243 | // default: jpg; 244 | // if you want to have transparent background, you should choose png 245 | type?: 'png' | 'jpg', 246 | 247 | // the quality of export image : 0~1; 248 | // it's invalid to png type; 249 | // default: .9; 250 | quality?: number, 251 | 252 | success?: (b64: string) => void, 253 | error?: (err: any) => void 254 | } 255 | ) 256 | ``` 257 | 258 | ## MImage: 图片处理 259 | 260 | MImage is a small image handle tool, contains compress、crop and filter. 261 | 262 | ```js 263 | import { MImage } from 'mcanvas' 264 | 265 | const mi = new MImage('imageUrl') 266 | ``` 267 | 268 | ### 1. mi.filter(type, data?) 269 | 270 | - `blur` 271 | 272 | ```js 273 | // blurValue: 1 ~ 10 274 | mi.filter('blur', blurValue: number = 6) 275 | ``` 276 | 277 | - `flip` 278 | 279 | ```js 280 | // dire 281 | // hor: horizontal 282 | // ver: vertical 283 | mi.filter('flip', dire: 'hor' | 'ver' = 'hor') 284 | ``` 285 | 286 | - `gray` 287 | 288 | ```js 289 | mi.filter('gray') 290 | ``` 291 | 292 | - `mosaic` 293 | 294 | ```js 295 | mi.filter('mosaic', blockSize: number = 10) 296 | ``` 297 | 298 | - `oil` 299 | 300 | ```js 301 | // range: 1 ~ 5 302 | // levels: 1 ~ 256 303 | mi.filter('oil', range: number = 2, levels: number = 32) 304 | ``` 305 | 306 | ### 2. mi.crop(options) 307 | 308 | ```js 309 | mi.crop( 310 | options: { 311 | x?: number | string 312 | y?: number | string 313 | 314 | width?: number | string 315 | height?: number | string 316 | 317 | radius?: number | string 318 | } 319 | ) 320 | ``` 321 | 322 | ### 3. mi.compress(options) 323 | 324 | ```js 325 | mi.compress( 326 | options: { 327 | quality?: number, 328 | 329 | width?: number, 330 | height?: number, 331 | } 332 | ) 333 | ``` 334 | 335 | ### 4. mi.draw(options) 336 | 337 | export image, the usage is same as the mcanvas.draw(). -------------------------------------------------------------------------------- /README_ZH.md: -------------------------------------------------------------------------------- 1 | # 文档 2 | 3 | ## 简介: 4 | 5 | 在业务中,经常遇到各种处理图片的需求,图片合成,添加文字,添加水印,压缩,裁剪,常用滤镜处理,因为这些业务经常需要进行各种位置,状态等参数的计算,写起来并不是那么方便。该插件就是为了解决这部分难点,封装底层 `API` 及各种计算,提供出使用更为简单的 `API`,减少项目上的重复工作,提高效率; 6 | 7 | ## 简单示例(详细功能请查询API): 8 | 9 | 由于图片的合成是异步且严格遵循顺序的,因此将使用的形式设计成了链式的方式,这种方式更为语义化且符合逻辑; 10 | 11 | 首先需要一系列的素材准备,将要绘制的素材 `add` 进 `queue` 队列中,最后再调用 `draw()` 进行绘制及导出; 12 | 13 | ### 图片合成: 14 | 15 | ```js 16 | import { MCanvas } from 'mcanvas' 17 | 18 | // 创建画布,初始化 canvas; 19 | const mc = new MCanvas({ 20 | width: 1000, 21 | height: 1000, 22 | backgroundColor: 'black', 23 | }); 24 | 25 | // background : 准备底图;提供多种模式 26 | mc.background('imagePath',{ 27 | left: 0, 28 | top: 0, 29 | color: '#000000', 30 | type: 'origin', 31 | }) 32 | 33 | // add 添加图片素材基础函数; 34 | mc.add('imagePath',{ 35 | width: 183, 36 | pos: { 37 | x: 250, 38 | y: 369, 39 | scale: 0.84, 40 | rotate: 1, 41 | }, 42 | }) 43 | 44 | // text 添加文字数据基础函数; 45 | mc.text('郭东东
啊啊啊',{ 46 | width: '300px', 47 | align: 'center', 48 | pos: { 49 | x:0, 50 | y:0, 51 | }, 52 | }) 53 | 54 | // watermark 添加水印函数,基于 add 函数封装; 55 | mc.watermark('imagePath' ,{ 56 | width: '40%', 57 | pos: 'rightBottom', 58 | }) 59 | 60 | // draw 最终绘制函数,用于最终的绘制; 61 | const b64 = await mc.draw(options); 62 | ``` 63 | 64 | ### 图片处理: 65 | 66 | ```js 67 | import { MImage } from 'mcanvas' 68 | 69 | // 图片处理, 具有裁剪、压缩、常用滤镜的功能 70 | const mi = new MImage('imageUrl') 71 | 72 | // 图片裁剪 73 | mi.crop({ 74 | x: 'center', 75 | y: 'center', 76 | width: 100, 77 | height: 100, 78 | radius: 10, 79 | }) 80 | 81 | // 图片滤镜 82 | mi.filter('blur') 83 | 84 | // 图片压缩 85 | mi.compress({ 86 | width: 200, 87 | quality: .9, 88 | }) 89 | 90 | mi.draw({ 91 | type: 'png', 92 | success(b64) { 93 | console.log(b64) 94 | } 95 | }) 96 | ``` 97 | 98 | ## MCanvas: 图片合成 99 | 100 | ### 参数说明 101 | 102 | 为了方便绘制,提供了各种形式的参数值,只要是涉及长度或者位置,皆可以使用多种形式, 下面说明均使用 TS 语法: number | string 表示; 103 | 104 | 例如: 105 | 106 | ```js 107 | // 长度单位包含三种形式: 数值 与 百分比 108 | width: 100 | '100px' | '100%', 109 | 110 | // 位置单位包含四种形式: 数值 / 百分比 / 绝对定位 / 关键字 111 | x: 100 | '100px' | '100%' | 'left/right/center: 100' | 'center' 112 | ``` 113 | 114 | ### 创建实例: 115 | 116 | #### `new MCanvas(options)`: 117 | 118 | 创建画布,初始化 `Canvas` ; 119 | 120 | ```js 121 | const mc = new MCanvas(options: { 122 | // 画布宽度; 123 | width?: number, 124 | // 画布高度; 125 | height?: number, 126 | // 画布背景颜色; 127 | backgroundColor?: string, 128 | }) 129 | ``` 130 | 131 | ### 方法: 132 | 133 | #### 1、 `mc.background(image, options)`: 134 | 135 | 绘制画布的底图; 136 | 137 | options: 初次必填,之后可选; 138 | 139 | > 每次绘制背景后,都会将参数储存为 默认值,因此可以通过不传值调用该函数来恢复背景图初始化; 140 | 141 | ```js 142 | mc.background( 143 | image: string | HTMLImageElement | HTMLCanvasElement, 144 | options?: { 145 | 146 | // 绘制方式: origin / crop / contain 147 | // origin : 原图模式,画布与背景图大小一致,忽略初始化传入的画布宽高;忽略 left / top值; 148 | // crop : 裁剪模式,背景图自适应铺满画布,多余部分裁剪;可通过 left / top 值控制裁剪部分; 149 | // contain : 包含模式, 类似于 background-size:contain; 可通过 left / top 值进行位置的控制; 150 | 151 | type?: 'origin' | 'crop' | 'contain' , 152 | 153 | // 背景图片距离画布左上角的距离, 154 | // 100 / '100%' / '100px' 155 | // 0%: 居左裁剪; 50% :代表居中裁剪; 100%: 代表居右裁剪 156 | 157 | left?: number | string, 158 | top?: number | string, 159 | 160 | // 除了背景图外的颜色,仅在 type:contain 时可见; 161 | color?: string, 162 | } 163 | ) 164 | ``` 165 | 166 | #### 2、`add(image, options)`: 167 | 168 | 通用的添加图层函数,可使用上面两种调用方式,添加多张或者单张素材; 169 | 170 | ```js 171 | mc.add( 172 | // 素材图 173 | image: string | HTMLImageElement | HTMLCanvasElement, 174 | // 配置参数: 175 | options: { 176 | // 素材的宽度值,相对于画布; 177 | // eg. width: 100 / '100%' / '100px'; 178 | width?: number | string, 179 | 180 | // 裁剪系数,相对于素材图; 181 | crop?: { 182 | // 相对于素材图的坐标点,原点为左上点; 183 | x?: number | string, 184 | y?: number | string, 185 | 186 | // 需要裁剪的宽高, eg. 100/'100%'/'100px'; 187 | width?: number | string, 188 | height?: number | string, 189 | 190 | // 裁剪圆角 191 | radius?: number | string 192 | }, 193 | 194 | // 位置系数,相对于画布; 195 | pos?: { 196 | // 相对于画布的坐标点,原点为左上点; 197 | // eg. x: 250 / '250px' / '100%' / 'left:250' / 'center', 198 | x?: number | string, 199 | y?: number | string, 200 | 201 | // 素材放大值,会叠加 width 值,进行进一步基于中心点放大; 202 | scale?: number | string, 203 | 204 | // 素材旋转角度; 205 | rotate?: number | string, 206 | }, 207 | } 208 | ) 209 | ``` 210 | 211 | #### 3、`watermark(image,options)`: 212 | 213 | 添加水印函数,基于 `add()` 封装,使用更便携方便; 214 | 215 | ```js 216 | mc.watermark( 217 | // 素材图 218 | image: url/HTMLImageElement/HTMLCanvasElement, 219 | 220 | // 配置参数: 221 | options: { 222 | // 水印大小,为了根据图片进行自适应,仅支持 百分比 的形式; 223 | // 宽度为 画布宽度 * 百分比; 224 | // Default: '40%'; 225 | width?: string, 226 | 227 | // 位置 228 | // Default : 'rightBottom'; 229 | pos?: 'leftTop' | 'leftBottom' | 'rightTop' | 'rightBottom', 230 | 231 | // 距离画布边界的 margin 值, 232 | // Default : 20; 233 | margin?: number, 234 | } 235 | ) 236 | ``` 237 | 238 | #### 4、`text(context,options)`: 239 | 240 | 添加文字函数;支持多样式,自动换行; 241 | 支持描边文字,渐变文字,文字阴影; 242 | 243 | 使用方式: 244 | 245 | ```js 246 | mc.text( 247 | // 文字内容,支持配置三种字体样式,big/normal/small; 248 | // : 大字 | 小字: 小字 |
:换行 249 | context: '大字大字大字常规字体
换行小字小字小字', 250 | options: { 251 | // 定义文字每行的宽度值,相对于画布; 252 | // eg. width: 100 / '100%' / '100px'; 253 | width?: string | number, 254 | 255 | // 每一行文字的对齐方式 256 | align?: 'left' | 'center' | 'right', 257 | 258 | // 可通过 smallStyle/normalStyle/largeStyle 分别配置不同标签中的字体; 259 | // 以 normalStyle 为栗,smallStyle/largeStyle 一致; 260 | normalStyle?: { 261 | 262 | // 文字样式,包含字体/字号等,使用方式与css font一致; 263 | font?: string, 264 | 265 | // 字体颜色; 266 | color?: string, 267 | 268 | // 行高 269 | lineheight?: number, 270 | 271 | // 类型, 实心字体或者描边字体; 272 | type?: 'fill' | 'stroke', 273 | 274 | // 当为描边时,该值控制描边宽度; 275 | lineWidth?: number, 276 | 277 | // 换行时是否断词,默认为 true; 278 | wordBreak?: boolean, 279 | 280 | // 文字阴影 281 | shadow?: { 282 | color?: string, 283 | blur?: number, 284 | offsetX?: number, 285 | offsetY?: number, 286 | }, 287 | 288 | // 文字渐变 289 | gradient?: { 290 | type?: 1 | 2, // 1: 横向渐变; 2: 纵向渐变; 291 | colorStop?: string[] // eg. ['red','blue'], 292 | }, 293 | }, 294 | 295 | // 位置系数,相对于画布; 296 | pos?: { 297 | // 文字起始点相对于画布的坐标点,原点为左上点; 298 | // 支持多种值: 299 | // x: 250 / '250px' / '100%' / 'left:250' / 'center', 300 | x?: string | number, 301 | y?: string | number, 302 | rotate?: string | number, 303 | }, 304 | }, 305 | ) 306 | ``` 307 | 308 | #### 5、`mc.rect(ops)` 309 | 310 | 与`add`函数相似,该方法用于直接绘制矩形; 311 | 312 | ```js 313 | mc.rect( 314 | options: { 315 | // 矩形左上角位置,支持多种值; 316 | // x: 250 / '250px' / '100%' / 'left:250' / 'center', 317 | x?: string | number, 318 | y?: string | number, 319 | 320 | // 矩形尺寸; 321 | width?: string | number, 322 | height?: string | number, 323 | 324 | // 矩形描边宽度 325 | strokeWidth?: number, 326 | // 矩形描边颜色 327 | strokeColor?: string, 328 | 329 | // 矩形填充颜色 330 | fillColor?: string, 331 | } 332 | ) 333 | ``` 334 | 335 | #### 6、`mc.circle(ops)` 336 | 337 | 与`rect`函数相似,该方法用于直接绘制圆形; 338 | 339 | ```js 340 | mc.circle( 341 | options: { 342 | // 圆形圆心位置,支持多种值; 343 | // x: 250 / '250px' / '100%' / 'left:250' / 'center', 344 | x?: string | number, 345 | y?: string | number, 346 | 347 | // 圆形半径; 100 / '100%' / '100px' 348 | radius?: string | number , 349 | 350 | // 圆形描边宽度 351 | strokeWidth?: string | number, 352 | // 圆形描边颜色 353 | strokeColor?: string, 354 | 355 | // 圆形填充颜色 356 | fillColor?: string, 357 | } 358 | ) 359 | ``` 360 | 361 | #### 6、 `mc.draw(ops)`: 362 | 363 | 绘制函数,`add`/`watermark`/`text` 方法都需要在末尾调用该方法进行绘制并导出图片,获取 `base64` ; 364 | 365 | 现已支持 Promise 366 | 367 | - `Promise` 368 | 369 | ```js 370 | // 由于异步的缘故,使用 async/await 或 Promise; 371 | const base64 = await mc.draw(options) 372 | 373 | // or 374 | mc.draw(options).then(console.log) 375 | ``` 376 | 377 | - `Callback` 378 | 379 | ```js 380 | // 由于异步的缘故,使用回调,获取 base64; 381 | mc.draw( 382 | options: { 383 | // 导出图片格式 384 | // default : jpg; 385 | // 当需要透明背景时,需要设置为 png; 386 | type?: 'png' | 'jpg', 387 | 388 | // 图片质量,对 png 格式无效; 0~1; 389 | // default: .9; 390 | quality?: number, 391 | 392 | // 成功回调; 393 | success?: (b64: string) => void, 394 | 395 | // 错误回调; 396 | error?: (err: any) => void 397 | } 398 | ) 399 | ``` 400 | 401 | #### 8、 `clear()`: 402 | 403 | 清空画布; 404 | 405 | ### 暴露属性 406 | 407 | ##### 1、`mc.canvas`: 画布使用的canvas type: HTMLCanvasElement; 408 | 409 | ##### 2、`mc.ctx`: 画布; 410 | 411 | ##### 3、`mc.textData`: 文字数据 type: object; 412 | 413 | 414 | ## MImage: 图片处理 415 | 416 | MImage 是轻量级的图片处理工具, 包含裁剪、压缩与常用滤镜,使用方法如下: 417 | 418 | ```js 419 | import { MImage } from 'mcanvas' 420 | 421 | const mi = new MImage('imageUrl') 422 | ``` 423 | 424 | ### 1. mi.filter(type, data?) 425 | 426 | 图片滤镜; 427 | 428 | - 模糊 429 | 430 | ```js 431 | // blurValue: 1 ~ 10: 模糊值 432 | mi.filter('blur', blurValue: number = 6) 433 | ``` 434 | 435 | - 翻转 436 | 437 | ```js 438 | // dire: 翻转方向 439 | // hor: 水平翻转 440 | // ver: 竖直翻转 441 | mi.filter('flip', dire: 'hor' | 'ver' = 'hor') 442 | ``` 443 | 444 | - 灰白 445 | 446 | ```js 447 | mi.filter('gray') 448 | ``` 449 | 450 | - 马赛克 451 | 452 | ```js 453 | // blockSize: 马赛克尺寸 454 | mi.filter('mosaic', blockSize: number = 10) 455 | ``` 456 | 457 | - 油画 458 | 459 | ```js 460 | // range: 1 ~ 5 461 | // levels: 1 ~ 256 462 | mi.filter('oil', range: number = 2, levels: number = 32) 463 | ``` 464 | 465 | ### 2. mi.crop(options) 466 | 467 | 图片裁剪; 468 | 469 | ```js 470 | mi.crop( 471 | options: { 472 | // 距离素材图左上角坐标 473 | x?: number | string 474 | y?: number | string 475 | 476 | // 裁剪尺寸 477 | width?: number | string 478 | height?: number | string 479 | 480 | // 裁剪圆角 481 | radius?: number | string 482 | } 483 | ) 484 | ``` 485 | 486 | ### 3. mi.compress(options) 487 | 488 | 压缩图片; 489 | 490 | ```js 491 | mi.compress( 492 | options: { 493 | // 压缩质量, defalut: .9 494 | quality?: number, 495 | 496 | // 压缩尺寸 497 | width?: number, 498 | height?: number, 499 | } 500 | ) 501 | ``` 502 | 503 | ### 4. mi.draw(options) 504 | 505 | 导出图片,与 mcanvas 使用方式一致; 506 | 507 | 508 | 509 | -------------------------------------------------------------------------------- /build/webpack.base.js: -------------------------------------------------------------------------------- 1 | const ExtractCss = require('mini-css-extract-plugin') 2 | const webpack = require('webpack') 3 | const path = require('path') 4 | const resolve = (fileName) => path.resolve(__dirname, fileName) 5 | 6 | const env = process.env.ENV 7 | module.exports = { 8 | resolve: { 9 | extensions: [".ts", ".tsx", ".js", ".jsx"], 10 | alias: { 11 | '@Src': resolve('../src'), 12 | }, 13 | }, 14 | module: { 15 | rules: [{ 16 | test: /\.node$/, 17 | use: 'node-loader' 18 | }, { 19 | test: /\.(ts|tsx)$/, 20 | use: [{ 21 | loader: require.resolve('ts-loader'), 22 | options: { 23 | transpileOnly: true, 24 | configFile: 'tsconfig.json' 25 | }, 26 | }] 27 | }, { 28 | test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/, /\.svg$/], 29 | loader: require.resolve('url-loader'), 30 | options: { 31 | limit: 5000, 32 | name: 'images/[name].[hash:8].[ext]', 33 | }, 34 | }, { 35 | test: /\.(woff(2)?|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/, 36 | use: [{ 37 | loader: 'file-loader', 38 | options: { 39 | name: '[name].[ext]', 40 | outputPath: 'fonts/' 41 | } 42 | }] 43 | }, { 44 | test: /\.s[ac]ss$/i, 45 | use: [ 46 | { 47 | loader: ExtractCss.loader, 48 | options: { 49 | publicPath: '../', 50 | } 51 | }, 52 | 'css-loader', 53 | 'sass-loader', 54 | ], 55 | }, { 56 | test: /\.css$/, 57 | use: [ 58 | require.resolve('css-loader'), 59 | ], 60 | }] 61 | }, 62 | plugins: [ 63 | new webpack.DefinePlugin({ 64 | 'ENV': JSON.stringify(env) 65 | }), 66 | new ExtractCss({ 67 | filename: `css/[name].css?[hash:8]`, 68 | }), 69 | ] 70 | } -------------------------------------------------------------------------------- /build/webpack.build.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const merge = require('webpack-merge'); 3 | const nodeExternals = require("webpack-node-externals"); 4 | const baseConfig = require('./webpack.base') 5 | const resolve = filePath => path.resolve(__dirname, filePath) 6 | 7 | const env = process.env.ENV 8 | const config = { 9 | mode: 'production', 10 | target: env, 11 | entry: [ 12 | resolve('../src/index.ts'), 13 | ], 14 | output: { 15 | path: resolve('../dist'), 16 | filename: `mcanvas.${env}.js`, 17 | library: 'MCanvas', 18 | libraryTarget: 'umd' 19 | }, 20 | } 21 | 22 | if (env === 'node') { 23 | config.externals = [nodeExternals()] 24 | } 25 | 26 | module.exports = merge(baseConfig, config) -------------------------------------------------------------------------------- /build/webpack.example.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const WebpackShellPlugin = require('webpack-shell-plugin'); 4 | const merge = require('webpack-merge'); 5 | const os = require('os'); 6 | const baseConfig = require('./webpack.base') 7 | const resolve = filePath => path.resolve(__dirname, filePath) 8 | const env = process.env.ENV 9 | 10 | // web-server 配置IP; 11 | const ifaces = os.networkInterfaces(); 12 | let devHost = '0.0.0.0'; 13 | let port = 9000; 14 | Object.keys(ifaces).forEach(key => { 15 | for (let i in ifaces[key]) { 16 | if (ifaces[key][i].family === 'IPv4' && 17 | /^(192|172)\./.test(ifaces[key][i].address)) { 18 | devHost = ifaces[key][i].address; 19 | } 20 | } 21 | }); 22 | 23 | const config = { 24 | mode: 'development', 25 | entry: [ 26 | resolve(`../example/${env}.ts`), 27 | ], 28 | output: { 29 | path: resolve('../example/dist'), 30 | filename: `${env}.js`, 31 | }, 32 | plugins: [], 33 | } 34 | 35 | if (env === 'web') { 36 | config.plugins.push(new HtmlWebpackPlugin({ 37 | inject: true, 38 | template: resolve('../example/index.html'), 39 | })) 40 | config.devServer = { 41 | contentBase: resolve('../example/dist'), 42 | compress: false, 43 | port: port, 44 | open: true, 45 | host: devHost, 46 | writeToDisk: true, 47 | } 48 | } else { 49 | config.target = 'node' 50 | config.node = { 51 | __filename: false, 52 | __dirname: true, 53 | } 54 | config.plugins.push(new WebpackShellPlugin({ 55 | onBuildEnd: [`nodemon ./example/dist/${env}.js --watch`] 56 | }),) 57 | } 58 | 59 | module.exports = merge(baseConfig, config) 60 | -------------------------------------------------------------------------------- /dist/mcanvas.node.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.MCanvas=e():t.MCanvas=e()}(global,(function(){return function(t){var e={};function r(i){if(e[i])return e[i].exports;var n=e[i]={i:i,l:!1,exports:{}};return t[i].call(n.exports,n,n.exports,r),n.l=!0,n.exports}return r.m=t,r.c=e,r.d=function(t,e,i){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(r.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)r.d(i,n,function(e){return t[e]}.bind(null,n));return i},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=3)}([function(t,e){t.exports=require("tslib")},function(t,e){t.exports=require("canvas")},function(t,e){t.exports=require("stackblur-canvas")},function(t,e,r){t.exports=r(5)},function(t,e,r){"use strict";r.r(e),r.d(e,"_Canvas",(function(){return n}));var i=r(1),n={name:"NodeCanvas",create:function(t,e){void 0===t&&(t=500),void 0===e&&(e=500);var r=Object(i.createCanvas)(t,e),n=r.getContext("2d");return[r,n]},loadImage:function(t){return Object(i.loadImage)(t)}}},function(t,e,r){"use strict";r.r(e),r.d(e,"MCanvas",(function(){return m})),r.d(e,"MImage",(function(){return k}));var i={};function n(t){var e=i.toString.call(t);return null===t?t+"":Number.isNaN(t)?"NaN":"object"==typeof t||"function"==typeof t?i[e]||"object":typeof t}"Boolean Number String Function Array Date RegExp Object Error Symbol".split(" ").forEach((function(t){i["[object "+t+"]"]=t.toLowerCase()}));var o={primitive:function(t){return["string","number"].includes(n(t))},array:function(t){return"array"===n(t)},str:function(t){return"string"===n(t)},obj:function(t){return"object"===n(t)},arr:function(t){return"array"===n(t)},num:function(t){return"number"===n(t)},fn:function(t){return"function"===n(t)},undef:function(t){return"undefined"===n(t)},def:function(t){return!o.undef(t)},null:function(t){return"null"===n(t)},empty:function(t){switch(n(t)){case"object":return 0===Object.keys(t).length;case"array":return 0===t.length;case"string":return!t.trim();case"undefined":case"null":case"NaN":case"boolean":return!0;default:return!1}},Nil:function(t){return["null","undefined"].includes(n(t))},NaN:function(t){return"NaN"===n(t)},promise:function(t){return!!t&&(o.obj(t)||o.fn(t))&&o.fn(t.then)}};function a(t){var e={},r=e.toString,i=e.hasOwnProperty,n=i.toString,o=n.call(Object);if(!t||"[object Object]"!==r.call(t))return!1;var a=Object.getPrototypeOf(t);if(!a)return!0;var s=i.call(a,"constructor")&&a.constructor;return"function"==typeof s&&n.call(s)===o}function s(){for(var t,e,r,i,n,o,c=[],u=0;u|\/|\?]/g.test(t)}var g=r(4)._Canvas,v={getImage:function(t){return"string"==typeof t?y.loadImage(t):"object"==typeof t?Promise.resolve(t):Promise.reject("getImage error, src="+t)},drawRoundRectPath:function(t,e,r,i){t.beginPath(),t.arc(e-i,r-i,i,0,Math.PI/2),t.lineTo(i,r),t.arc(i,r-i,i,Math.PI/2,Math.PI),t.lineTo(0,i),t.arc(i,i,i,Math.PI,3*Math.PI/2),t.lineTo(e-i,0),t.arc(e-i,i,i,3*Math.PI/2,2*Math.PI),t.lineTo(e,r-i),t.closePath()},drawRoundRect:function(t,e,r,i,n,o,a,s,c){void 0===o&&(o=0),void 0===a&&(a="#fff"),void 0===s&&(s=0),void 0===c&&(c="#000");var u=Math.min(i,n);2*o>u&&(o=u/2),t.save(),t.translate(e,r),v.drawRoundRectPath(t,i,n,o),t.fillStyle=a,t.fill(),t.lineWidth=s,t.strokeStyle=c,t.stroke(),t.restore()}},y=s(!0,g,v);function x(t,e){var r=u(t),i=r.iw,n=r.ih,o=s(!0,{x:0,y:0,width:"100%",height:"100%",radius:0},e),a=o.width,c=o.height,h=o.x,p=o.y,l=o.radius;(a=d(i,a))>i&&(a=i),(c=d(n,c))>n&&(c=n),h=f(i,a,h,"pos"),p=f(n,c,p,"pos"),l=d(i,l);var g=y.create(a,c),v=g[0],x=g[1];return v.width=a,v.height=c,y.drawRoundRect(x,0,0,a,c,l),x.globalCompositeOperation="source-in",x.drawImage(t,-h,-p,i,n),{cvs:v,ctx:x}}var m=function(){function t(t){void 0===t&&(t={}),this.queue=[],this.fn={success:function(){},error:function(t){}},this.data={textId:0,text:{},bgConfig:null},this._defaultFontFamily="helvetica neue,hiragino sans gb,Microsoft YaHei,arial,tahoma,sans-serif",this.ops=s({width:500,height:500,backgroundColor:""},t),this._init()}return t.prototype._init=function(){var t,e=this.ops,r=e.width,i=e.height,n=e.backgroundColor;t=y.create(r,i),this.cvs=t[0],this.ctx=t[1],n&&this._setBgColor(n)},t.prototype.background=function(t,e){var r=this;return void 0===e&&(e={type:"origin"}),t||this.data.bgConfig?(t?(e.image=t,this.data.bgConfig=e):this.data.bgConfig&&(e=this.data.bgConfig),this.queue.push((function(){e.color&&r._setBgColor(e.color),y.getImage(e.image).then((function(t){return r._background(t,e)})).catch(r.fn.error)})),this):(p("the init background must has a image."),this)},t.prototype._setBgColor=function(t){this.ctx.fillStyle=t,this.ctx.fillRect(0,0,this.cvs.width,this.cvs.height)},t.prototype._getBgAlign=function(t,e,r,i){var n;return o.str(t)?"50%"===t||"center"===t?n=Math.abs((e-r/i)/2):"100%"===t?n=Math.abs(e-r/i):"0%"===t&&(n=0):n=o.num(t)?t:0,n},t.prototype._background=function(t,e){var r,i,n,o,a,s,c,h,f,d=u(t),l=d.iw,g=d.ih,v=l/g,y=this.cvs.width/this.cvs.height;switch(e.type){case"crop":v>y?(n=g*y,o=g,f=this.cvs.height/g):(o=(n=l)/y,f=this.cvs.width/l),r=this._getBgAlign(e.left,l,this.cvs.width,f),i=this._getBgAlign(e.top,g,this.cvs.height,f),s=a=0,h=this.cvs.height,c=this.cvs.width;break;case"contain":i=r=0,n=l,o=g,v>y?(h=(c=this.cvs.width)/v,a=e.left||0,s=e.top||0===e.top?e.top:(this.cvs.height-h)/2):(c=(h=this.cvs.height)*v,s=e.top||0,a=e.left||0===e.left?e.left:(this.cvs.width-c)/2);break;case"origin":this.cvs.width=l,this.cvs.height=g,r=i=0,n=l,o=g,a=s=0,c=this.cvs.width,h=this.cvs.height;break;default:return void p("background type error!")}this.ctx.drawImage(t,r,i,n,o,a,s,c,h),this._next()},t.prototype.rect=function(t){var e=this;return void 0===t&&(t={}),this.queue.push((function(){var r=e.cvs,i=r.width,n=r.height,o=t.fillColor,a=void 0===o?"#fff":o,s=t.strokeColor,c=void 0===s?a:s,u=t.strokeWidth,d=void 0===u?0:u,p=t.radius,l=void 0===p?0:p,g=t.width,v=void 0===g?100:g,x=t.height,m=void 0===x?100:x,w=t.x,b=void 0===w?0:w,_=t.y,I=void 0===_?0:_;v=f(i,0,v,"pos")-2*d,m=f(n,0,m,"pos")-2*d,b=f(i,v,b,"pos")+(h(b,"right")?-d:d),I=f(n,m,I,"pos")+(h(I,"bottom")?-d:d),y.drawRoundRect(e.ctx,b,I,v,m,l,a,d,c),e._resetCtx()._next()})),this},t.prototype.circle=function(t){var e=this;return void 0===t&&(t={}),this.queue.push((function(){var r=t.fillColor,i=void 0===r?"#fff":r,n=t.strokeColor,o=void 0===n?i:n,a=t.strokeWidth,s=void 0===a?0:a,c=e.cvs,u=c.width,d=c.height,p=t.x,l=void 0===p?0:p,g=t.y,v=void 0===g?0:g,y=t.radius,x=f(u,0,void 0===y?100:y,"pos")-2*s;l=f(u,2*x,l,"pos")+x+(h(l,"right")?-s:s),v=f(d,2*x,v,"pos")+x+(h(v,"bottom")?-s:s),e.ctx.beginPath(),e.ctx.arc(l,v,x,0,2*Math.PI,!1),e.ctx.fillStyle=i,e.ctx.fill(),e.ctx.strokeStyle=o,e.ctx.lineWidth=s,e.ctx.stroke(),e.ctx.closePath(),e._resetCtx()._next()})),this},t.prototype._resetCtx=function(){return this.ctx.setTransform(1,0,0,1,0,0),this},t.prototype.watermark=function(t,e){if(void 0===e&&(e={}),!t)return p("there is not image of watermark."),this;var r=e.width,i=void 0===r?"40%":r,n=e.pos,o=void 0===n?"rightbottom":n,a=e.margin,s=void 0===a?20:a,c={x:0,y:0,scale:1,rotate:0};switch(o){case"leftTop":c.x="left:"+s,c.y="top:"+s;break;case"leftBottom":c.x="left:"+s,c.y="bottom:"+s;break;case"rightTop":c.x="right:"+s,c.y="top:"+s;break;case"rightBottom":c.x="right:"+s,c.y="bottom:"+s}return this.add(t,{width:i,pos:c}),this},t.prototype.add=function(t,e){var r=this,i={width:"100%",crop:{x:0,y:0,width:"100%",height:"100%",radius:0},pos:{x:0,y:0,scale:1,rotate:0}};return(o.arr(t)?t:[{image:t,options:e}]).map((function(t){var e=t.image,n=t.options;r.queue.push((function(){y.getImage(e).then((function(t){r._add(t,r._handleOps(t,s(!0,i,n)))})).catch(r.fn.error)}))})),this},t.prototype._add=function(t,e){var r,i=e.crop,n=e.pos,o=e.width;0===o&&function(t){throw new Error("[MCanvas WARN]: "+t)}("the width of mc-element is zero");var a,s,c,h,f=u(t),d=f.iw,p=f.ih,l=i.width,g=i.height,v=i.radius;(l!==d||g!==p||v>0)&&(t=x(t,i).cvs);var m,w,b,_,I,j,C=l/g,M=d>p?d/p:p/d,O=1.4*M>5?5:1.4*M,k=y.create(Math.round(l*O),Math.round(g*O)),q=k[0],P=k[1],S=C>1?4096/q.width:4096/q.height;m=-Math.round(l/2),w=-Math.round(g/2),b=l,_=Math.round(l/C),(q.width>4096||q.height>4096)&&S&&(r=[q.width,q.height,m,w,b,_].map((function(t){return Math.round(t*S)})),q.width=r[0],q.height=r[1],m=r[2],w=r[3],b=r[4],_=r[5]),P.translate(q.width/2,q.height/2),P.rotate(n.rotate),P.drawImage(t,m,w,b,_),c=Math.round(o*O),h=Math.round(c/C),j=(I=(O-1)*o/2)/C,a=Math.round(n.x+c*(1-n.scale)/2-I),s=Math.round(n.y+h*(1-n.scale)/2-j),c*=n.scale,h*=n.scale,this.ctx.drawImage(q,a,s,c,h),this._next()},t.prototype._getRotate=function(t){return o.str(t)?parseFloat(t)*Math.PI/180:o.num(t)?t*Math.PI/180:0},t.prototype._handleOps=function(t,e){var r,i,n=this.cvs,o=n.width,a=n.height,s=u(t),c=s.iw,h=s.ih,p=c/h,l=f(o,c,e.width,"pos"),g=f(o,c,e.crop.width,"crop"),v=f(a,h,e.crop.height,"crop"),y={width:g,height:v,x:f(c,g,e.crop.x,"crop"),y:f(h,v,e.crop.y,"crop"),radius:d(g,e.crop.radius)};y.x>c&&(y.x=c),y.y>h&&(y.y=h),r=c-y.x,i=h-y.y,y.width>r&&(y.width=r),y.height>i&&(y.height=i);var x=e.pos,m=x.x,w=x.y,b=x.rotate,_=x.scale,I=void 0===_?1:_;return{width:l,crop:y,pos:{x:f(o,l,m,"pos"),y:f(a,l/p,w,"pos"),scale:I,rotate:this._getRotate(b)}}},t.prototype._createStyle=function(t,e){return{font:t+"px "+this._defaultFontFamily,lineHeight:e,color:"#000",type:"fill",lineWidth:1,wordBreak:!0,shadow:{color:null,blur:0,offsetX:0,offsetY:0}}},t.prototype.text=function(t,e){var r=this;void 0===e&&(e={});var i=this.cvs.width/20;return this.queue.push((function(){var n,o=s(!0,{width:300,align:"left",smallStyle:r._createStyle(.8*i,.9*i),normalStyle:r._createStyle(i,1.1*i),largeStyle:r._createStyle(1.3*i,1.4*i),pos:{x:0,y:0,rotate:0}},e),a=r._parse(String(t)),c=0;a.map((function(t){t.size>c&&(c=t.size,n=t.type)}));var u=parseInt(o[n+"Style"].font);u&&o.width|/),r=[],i=0;i|<\/b>/.test(n)){var o=/<\/s>/.test(n)?"
":"",a=/<\/s>/.test(n)?"small":"large",s=e[i].split(o);r.push({type:a,text:s[0],size:"small"===a?0:2}),s[1]&&r.push({type:"normal",text:s[1],size:1})}else e[i]&&r.push({text:e[i],type:"normal",size:1})}return r},t.prototype._text=function(t,e){var r=this;this.data.textId++,this.data.text[this.data.textId]={};var i,n=e.width=f(this.cvs.width,0,e.width,"pos"),a=1,s=0,u=this._getLineHeight(t,e),d=f(this.cvs.width,n,0,"pos"),p=f(this.cvs.height,0,0,"pos")+u;this.data.text[this.data.textId][a]={data:[],lineWidth:0},t.map((function(t){i=e[t.type+"Style"],r.ctx.font=i.font;var c=r.ctx.measureText(t.text).width,f=t.text.replace(/
/g,"|");if(s+c>n||h(f,"|")){i.wordBreak||(f=function(t){if(!o.str(t))return[];var e,r=[],i=t.length,n="";for(e=0;en||"|"===y)||(d=s=0,p+=u,a+=1,r.data.text[r.data.textId][a]={data:[],lineWidth:0},"|"!==y))(x=r.data.text[r.data.textId][a]).data.push({context:y,x:d,y:p,style:i,width:c}),d+=c,x.lineWidth=s+=c}}else{var x;(x=r.data.text[r.data.textId][a]).data.push({context:f,x:d,y:p,style:i,width:c}),d+=c,x.lineWidth=s+=c}}));var g=y.create(n,this._getTextRectHeight(a)),v=g[0],x=g[1],m=v.height,w=v.width,b=f(this.cvs.width,w,e.pos.x,"pos"),_=f(this.cvs.height,m,e.pos.y,"pos");c(this.data.text[this.data.textId],(function(t,i){var o=0;i.lineWidthi&&(i=r)})),i},t.prototype._fillText=function(t,e){var r=e.context,i=e.x,n=e.y,o=e.style,a=o.align,s=o.lineWidth,u=o.shadow,h=o.font,f=o.gradient,d=o.lineHeight,p=u.color,l=u.blur,g=u.offsetX,v=u.offsetY;if(t.font=h,t.textAlign=a,t.textBaseline="alphabetic",t.lineWidth=s,t.shadowColor=p,t.shadowBlur=l,t.shadowOffsetX=g,t.shadowOffsetY=v,f){var y=f.type,x=f.colorStop,m=void 0,w=void 0,b=void 0,_=void 0;1===y?(m=i,w=n,b=i+e.width,_=n):(m=i,w=n-d,b=i,_=n);var I=t.createLinearGradient(m,w,b,_),j=x.length||0;c(x,(function(t,e){I.addColorStop(1/j*(+t+1),e)})),t[o.type+"Style"]=I}else t[o.type+"Style"]=o.color;t[o.type+"Text"](r,i,n),this._resetCtx()},t.prototype._getTextRectHeight=function(t){var e=this.data.text[this.data.textId][t].data[0];return e.y+e.style.lineHeight},t.prototype.draw=function(t){var e=this;return void 0===t&&(t={}),new Promise((function(r,i){var n={type:"jpeg",quality:.9,exportType:"base64",success:function(t){},error:function(t){}};o.fn(t)?n.success=t:"jpg"===(n=s(!0,n,t)).type&&(n.type="jpeg"),e.fn.error=function(t){n.error(t),i(t)},e.fn.success=function(){"canvas"===n.exportType?(n.success(e.cvs),r(e.cvs)):setTimeout((function(){var t=e.cvs.toDataURL("image/"+n.type,n.quality);n.success(t),r(t)}),0)},e._next()}))},t.prototype._next=function(){if(this.queue.length>0){this.ctx.save();var t=this.queue.shift();t&&t(),this.ctx.restore()}else this.fn.success()},t.prototype.clear=function(){return this.ctx.clearRect(0,0,this.cvs.width,this.cvs.height),this},t}(),w=r(0),b=function(){function t(t){var e=this;this.isFirst=!0,this.queue=[],this._next=function(t){if(e.isFirst||(t=e.after(t)),e.queue.length>0){var r=e.queue.shift();r&&(t=e.before(t),r(e._next,t),e.isFirst=!1)}else e.end(t),e.queue=[]};var r=t||{},i=r.before,n=void 0===i?function(t){return t}:i,o=r.after,a=void 0===o?function(t){return t}:o;this.before=n,this.after=a}return t.prototype.push=function(t){o.fn(t)&&this.queue.push(t)},t.prototype.perform=function(t,e){this.end=t,this._next(e)},t}(),_=(function(){function t(){this._queue=new b}t.prototype.line=function(t){var e=this;return this._queue.push((function(r,i){setTimeout((function(){return Object(w.__awaiter)(e,void 0,void 0,(function(){var e;return Object(w.__generator)(this,(function(n){return e=t(i),o.promise(e)?e.then(r):r(e),[2]}))}))}),0)})),this},t.prototype.start=function(t,e){this._queue.perform(t,e)}}(),r(2));function I(t,e){void 0===e&&(e=20);var r=t.width,i=t.height,n=t.getContext("2d");return _.canvasRGBA(t,0,0,r,i,e),[t,n]}function j(t,e){void 0===e&&(e="hor");var r,i,n,o,a=t.width,s=t.height,c=t.getContext("2d"),u=c.getImageData(0,0,a,s).data,h=c.createImageData(a,s),f=h.data;for(r=0;rx&&(s=x),i=0;iy&&(o=y),p=l=g=v=0,d=(o-n)*(s-a),u=a;u=_))for(o=i*b,f=-e;f<=e;f++)(n=s+f)<0||n>=b||(d=j[a=o+n<<2],v=(p=j[a+1])*r>>8,y=(l=j[a+2])*r>>8,P[g=d*r>>8]+=d,S[v]+=p,R[y]+=l,O[g]+=1,k[v]+=1,q[y]+=1);for(x=m=w=0,u=1;uO[x]&&(x=u),k[u]>k[m]&&(m=u),q[u]>q[w]&&(w=u);M[T]=P[x]/O[x]|0,M[T+1]=S[m]/k[m]|0,M[T+2]=R[w]/q[w]|0,M[T+3]=j[T+3],T+=4}return I.putImageData(C,0,0),[t,I]}var k=function(){function t(t){this._image=t,this._queue=new b,this.clear()}return t.prototype.clear=function(){return this._getImage()._drawCanvas(),this},t.prototype._drawCanvas=function(){return this._run((function(t){var e=u(t),r=e.iw,i=e.ih,n=y.create(r,i),o=n[0],a=n[1];return a.drawImage(t,0,0,r,i),{cvs:o,ctx:a}}))},t.prototype._getImage=function(){var t=this;return this._run((function(){return new Promise((function(e){return Object(w.__awaiter)(t,void 0,void 0,(function(){var t=this;return Object(w.__generator)(this,(function(r){return o.str(this._image)?y.getImage(this._image).then((function(r){return e(t._image=r)})).catch(this._error):e(this._image),[2]}))}))}))}))},t.prototype._run=function(t){var e=this;return this._queue.push((function(r,i){setTimeout((function(){return Object(w.__awaiter)(e,void 0,void 0,(function(){var e;return Object(w.__generator)(this,(function(n){return e=t(i),o.promise(e)?e.then(r):r(e),[2]}))}))}),0)})),this},t.prototype.draw=function(t){var e=this;return void 0===t&&(t={}),new Promise((function(r,i){var n={type:"jpeg",quality:1,exportType:"base64",success:function(t){},error:function(t){}};o.fn(t)?n.success=t:"jpg"===(n=s(!0,n,t)).type&&(n.type="jpeg");var a=n.exportType,c=n.type,u=n.quality,h=n.success,f=n.error,d=function(t){h(t),r(t),e.clear()};e._error=function(t){f(t),i(t)},e._queue.perform((function(t){var e=t.cvs,r=t.quality;"canvas"===a?d(e):setTimeout((function(){var t=e.toDataURL("image/"+c,r||u);d(t)}),0)}))}))},t.prototype.crop=function(t){return void 0===t&&(t={}),this._run((function(e){return x(e.cvs,t)}))},t.prototype.compress=function(t){return void 0===t&&(t={}),this._run((function(e){var r=e.cvs,i=e.ctx,n=t.quality,a=t.width,s=t.height,c=r.width/r.height;if(!o.num(a)&&!o.num(s))return{cvs:r,ctx:i,quality:n};o.num(a)||(a=s*c),o.num(s)||(s=a/c);var u=y.create(a,s),h=u[0],f=u[1];return f.drawImage(r,0,0,a,s),{cvs:h,ctx:f,quality:n}}))},t.prototype.filter=function(t){for(var e=[],r=1;r|\/|\?]/g.test(t)}function g(t){if(!n.a.str(t))return[];var e,r=[],i=t.length,o="";for(e=0;eh&&(a=h/2),t.save(),t.translate(e,r),o.drawRoundRectPath(t,n,i,a),t.fillStyle=c,t.fill(),t.lineWidth=u,t.strokeStyle=s,t.stroke(),t.restore()}},a=Object(n.b)(!0,i,o);function c(t,e){var r=Object(n.e)(t),i=r.iw,o=r.ih,c=Object(n.b)(!0,{x:0,y:0,width:"100%",height:"100%",radius:0},e),u=c.width,s=c.height,h=c.x,f=c.y,d=c.radius;(u=Object(n.d)(i,u))>i&&(u=i),(s=Object(n.d)(o,s))>o&&(s=o),h=Object(n.k)(i,u,h,"pos"),f=Object(n.k)(o,s,f,"pos"),d=Object(n.d)(i,d);var l=a.create(u,s),p=l[0],g=l[1];return p.width=u,p.height=s,a.drawRoundRect(g,0,0,u,s,d),g.globalCompositeOperation="source-in",g.drawImage(t,-h,-f,i,o),{cvs:p,ctx:g}}var u=function(){function t(t){void 0===t&&(t={}),this.queue=[],this.fn={success:function(){},error:function(t){}},this.data={textId:0,text:{},bgConfig:null},this._defaultFontFamily="helvetica neue,hiragino sans gb,Microsoft YaHei,arial,tahoma,sans-serif",this.ops=Object(n.b)({width:500,height:500,backgroundColor:""},t),this._init()}return t.prototype._init=function(){var t,e=this.ops,r=e.width,n=e.height,i=e.backgroundColor;t=a.create(r,n),this.cvs=t[0],this.ctx=t[1],i&&this._setBgColor(i)},t.prototype.background=function(t,e){var r=this;return void 0===e&&(e={type:"origin"}),t||this.data.bgConfig?(t?(e.image=t,this.data.bgConfig=e):this.data.bgConfig&&(e=this.data.bgConfig),this.queue.push((function(){e.color&&r._setBgColor(e.color),a.getImage(e.image).then((function(t){return r._background(t,e)})).catch(r.fn.error)})),this):(Object(n.i)("the init background must has a image."),this)},t.prototype._setBgColor=function(t){this.ctx.fillStyle=t,this.ctx.fillRect(0,0,this.cvs.width,this.cvs.height)},t.prototype._getBgAlign=function(t,e,r,i){var o;return n.g.str(t)?"50%"===t||"center"===t?o=Math.abs((e-r/i)/2):"100%"===t?o=Math.abs(e-r/i):"0%"===t&&(o=0):o=n.g.num(t)?t:0,o},t.prototype._background=function(t,e){var r,i,o,a,c,u,s,h,f,d=Object(n.e)(t),l=d.iw,p=d.ih,g=l/p,v=this.cvs.width/this.cvs.height;switch(e.type){case"crop":g>v?(o=p*v,a=p,f=this.cvs.height/p):(a=(o=l)/v,f=this.cvs.width/l),r=this._getBgAlign(e.left,l,this.cvs.width,f),i=this._getBgAlign(e.top,p,this.cvs.height,f),u=c=0,h=this.cvs.height,s=this.cvs.width;break;case"contain":i=r=0,o=l,a=p,g>v?(h=(s=this.cvs.width)/g,c=e.left||0,u=e.top||0===e.top?e.top:(this.cvs.height-h)/2):(s=(h=this.cvs.height)*g,u=e.top||0,c=e.left||0===e.left?e.left:(this.cvs.width-s)/2);break;case"origin":this.cvs.width=l,this.cvs.height=p,r=i=0,o=l,a=p,c=u=0,s=this.cvs.width,h=this.cvs.height;break;default:return void Object(n.i)("background type error!")}this.ctx.drawImage(t,r,i,o,a,c,u,s,h),this._next()},t.prototype.rect=function(t){var e=this;return void 0===t&&(t={}),this.queue.push((function(){var r=e.cvs,i=r.width,o=r.height,c=t.fillColor,u=void 0===c?"#fff":c,s=t.strokeColor,h=void 0===s?u:s,f=t.strokeWidth,d=void 0===f?0:f,l=t.radius,p=void 0===l?0:l,g=t.width,v=void 0===g?100:g,y=t.height,b=void 0===y?100:y,x=t.x,m=void 0===x?0:x,w=t.y,j=void 0===w?0:w;v=Object(n.k)(i,0,v,"pos")-2*d,b=Object(n.k)(o,0,b,"pos")-2*d,m=Object(n.k)(i,v,m,"pos")+(Object(n.f)(m,"right")?-d:d),j=Object(n.k)(o,b,j,"pos")+(Object(n.f)(j,"bottom")?-d:d),a.drawRoundRect(e.ctx,m,j,v,b,p,u,d,h),e._resetCtx()._next()})),this},t.prototype.circle=function(t){var e=this;return void 0===t&&(t={}),this.queue.push((function(){var r=t.fillColor,i=void 0===r?"#fff":r,o=t.strokeColor,a=void 0===o?i:o,c=t.strokeWidth,u=void 0===c?0:c,s=e.cvs,h=s.width,f=s.height,d=t.x,l=void 0===d?0:d,p=t.y,g=void 0===p?0:p,v=t.radius,y=void 0===v?100:v,b=Object(n.k)(h,0,y,"pos")-2*u;l=Object(n.k)(h,2*b,l,"pos")+b+(Object(n.f)(l,"right")?-u:u),g=Object(n.k)(f,2*b,g,"pos")+b+(Object(n.f)(g,"bottom")?-u:u),e.ctx.beginPath(),e.ctx.arc(l,g,b,0,2*Math.PI,!1),e.ctx.fillStyle=i,e.ctx.fill(),e.ctx.strokeStyle=a,e.ctx.lineWidth=u,e.ctx.stroke(),e.ctx.closePath(),e._resetCtx()._next()})),this},t.prototype._resetCtx=function(){return this.ctx.setTransform(1,0,0,1,0,0),this},t.prototype.watermark=function(t,e){if(void 0===e&&(e={}),!t)return Object(n.i)("there is not image of watermark."),this;var r=e.width,i=void 0===r?"40%":r,o=e.pos,a=void 0===o?"rightbottom":o,c=e.margin,u=void 0===c?20:c,s={x:0,y:0,scale:1,rotate:0};switch(a){case"leftTop":s.x="left:"+u,s.y="top:"+u;break;case"leftBottom":s.x="left:"+u,s.y="bottom:"+u;break;case"rightTop":s.x="right:"+u,s.y="top:"+u;break;case"rightBottom":s.x="right:"+u,s.y="bottom:"+u}return this.add(t,{width:i,pos:s}),this},t.prototype.add=function(t,e){var r=this,i={width:"100%",crop:{x:0,y:0,width:"100%",height:"100%",radius:0},pos:{x:0,y:0,scale:1,rotate:0}};return(n.g.arr(t)?t:[{image:t,options:e}]).map((function(t){var e=t.image,o=t.options;r.queue.push((function(){a.getImage(e).then((function(t){r._add(t,r._handleOps(t,Object(n.b)(!0,i,o)))})).catch(r.fn.error)}))})),this},t.prototype._add=function(t,e){var r,i=e.crop,o=e.pos,u=e.width;0===u&&Object(n.j)("the width of mc-element is zero");var s,h,f,d,l=Object(n.e)(t),p=l.iw,g=l.ih,v=i.width,y=i.height,b=i.radius;(v!==p||y!==g||b>0)&&(t=c(t,i).cvs);var x,m,w,j,_,O,k=v/y,I=p>g?p/g:g/p,C=1.4*I>5?5:1.4*I,M=a.create(Math.round(v*C),Math.round(y*C)),S=M[0],P=M[1],q=Object(n.a)()&&(S.width>2096||S.height>2096)?2096:4096,T=k>1?q/S.width:q/S.height;x=-Math.round(v/2),m=-Math.round(y/2),w=v,j=Math.round(v/k),(S.width>q||S.height>q)&&T&&(r=[S.width,S.height,x,m,w,j].map((function(t){return Math.round(t*T)})),S.width=r[0],S.height=r[1],x=r[2],m=r[3],w=r[4],j=r[5]),P.translate(S.width/2,S.height/2),P.rotate(o.rotate),P.drawImage(t,x,m,w,j),f=Math.round(u*C),d=Math.round(f/k),O=(_=(C-1)*u/2)/k,s=Math.round(o.x+f*(1-o.scale)/2-_),h=Math.round(o.y+d*(1-o.scale)/2-O),f*=o.scale,d*=o.scale,this.ctx.drawImage(S,s,h,f,d),this._next()},t.prototype._getRotate=function(t){return n.g.str(t)?parseFloat(t)*Math.PI/180:n.g.num(t)?t*Math.PI/180:0},t.prototype._handleOps=function(t,e){var r,i,o=this.cvs,a=o.width,c=o.height,u=Object(n.e)(t),s=u.iw,h=u.ih,f=s/h,d=Object(n.k)(a,s,e.width,"pos"),l=Object(n.k)(a,s,e.crop.width,"crop"),p=Object(n.k)(c,h,e.crop.height,"crop"),g={width:l,height:p,x:Object(n.k)(s,l,e.crop.x,"crop"),y:Object(n.k)(h,p,e.crop.y,"crop"),radius:Object(n.d)(l,e.crop.radius)};g.x>s&&(g.x=s),g.y>h&&(g.y=h),r=s-g.x,i=h-g.y,g.width>r&&(g.width=r),g.height>i&&(g.height=i);var v=e.pos,y=v.x,b=v.y,x=v.rotate,m=v.scale,w=void 0===m?1:m;return{width:d,crop:g,pos:{x:Object(n.k)(a,d,y,"pos"),y:Object(n.k)(c,d/f,b,"pos"),scale:w,rotate:this._getRotate(x)}}},t.prototype._createStyle=function(t,e){return{font:t+"px "+this._defaultFontFamily,lineHeight:e,color:"#000",type:"fill",lineWidth:1,wordBreak:!0,shadow:{color:null,blur:0,offsetX:0,offsetY:0}}},t.prototype.text=function(t,e){var r=this;void 0===e&&(e={});var i=this.cvs.width/20;return this.queue.push((function(){var o,a=Object(n.b)(!0,{width:300,align:"left",smallStyle:r._createStyle(.8*i,.9*i),normalStyle:r._createStyle(i,1.1*i),largeStyle:r._createStyle(1.3*i,1.4*i),pos:{x:0,y:0,rotate:0}},e),c=r._parse(String(t)),u=0;c.map((function(t){t.size>u&&(u=t.size,o=t.type)}));var s=parseInt(a[o+"Style"].font);s&&a.width|/),r=[],n=0;n|<\/b>/.test(i)){var o=/<\/s>/.test(i)?"":"",a=/<\/s>/.test(i)?"small":"large",c=e[n].split(o);r.push({type:a,text:c[0],size:"small"===a?0:2}),c[1]&&r.push({type:"normal",text:c[1],size:1})}else e[n]&&r.push({text:e[n],type:"normal",size:1})}return r},t.prototype._text=function(t,e){var r=this;this.data.textId++,this.data.text[this.data.textId]={};var i,o=e.width=Object(n.k)(this.cvs.width,0,e.width,"pos"),c=1,u=0,s=this._getLineHeight(t,e),h=Object(n.k)(this.cvs.width,o,0,"pos"),f=Object(n.k)(this.cvs.height,0,0,"pos")+s;this.data.text[this.data.textId][c]={data:[],lineWidth:0},t.map((function(t){i=e[t.type+"Style"],r.ctx.font=i.font;var a=r.ctx.measureText(t.text).width,d=t.text.replace(/
/g,"|");if(u+a>o||Object(n.f)(d,"|")){i.wordBreak||(d=Object(n.h)(d));for(var l=0,p=d.length;lo||"|"===g)||(h=u=0,f+=s,c+=1,r.data.text[r.data.textId][c]={data:[],lineWidth:0},"|"!==g))(v=r.data.text[r.data.textId][c]).data.push({context:g,x:h,y:f,style:i,width:a}),h+=a,v.lineWidth=u+=a}}else{var v;(v=r.data.text[r.data.textId][c]).data.push({context:d,x:h,y:f,style:i,width:a}),h+=a,v.lineWidth=u+=a}}));var d=a.create(o,this._getTextRectHeight(c)),l=d[0],p=d[1],g=l.height,v=l.width,y=Object(n.k)(this.cvs.width,v,e.pos.x,"pos"),b=Object(n.k)(this.cvs.height,g,e.pos.y,"pos");Object(n.c)(this.data.text[this.data.textId],(function(t,n){var i=0;n.lineWidthn&&(n=r)})),n},t.prototype._fillText=function(t,e){var r=e.context,i=e.x,o=e.y,a=e.style,c=a.align,u=a.lineWidth,s=a.shadow,h=a.font,f=a.gradient,d=a.lineHeight,l=s.color,p=s.blur,g=s.offsetX,v=s.offsetY;if(t.font=h,t.textAlign=c,t.textBaseline="alphabetic",t.lineWidth=u,t.shadowColor=l,t.shadowBlur=p,t.shadowOffsetX=g,t.shadowOffsetY=v,f){var y=f.type,b=f.colorStop,x=void 0,m=void 0,w=void 0,j=void 0;1===y?(x=i,m=o,w=i+e.width,j=o):(x=i,m=o-d,w=i,j=o);var _=t.createLinearGradient(x,m,w,j),O=b.length||0;Object(n.c)(b,(function(t,e){_.addColorStop(1/O*(+t+1),e)})),t[a.type+"Style"]=_}else t[a.type+"Style"]=a.color;t[a.type+"Text"](r,i,o),this._resetCtx()},t.prototype._getTextRectHeight=function(t){var e=this.data.text[this.data.textId][t].data[0];return e.y+e.style.lineHeight},t.prototype.draw=function(t){var e=this;return void 0===t&&(t={}),new Promise((function(r,i){var o={type:"jpeg",quality:.9,exportType:"base64",success:function(t){},error:function(t){}};n.g.fn(t)?o.success=t:"jpg"===(o=Object(n.b)(!0,o,t)).type&&(o.type="jpeg"),e.fn.error=function(t){o.error(t),i(t)},e.fn.success=function(){"canvas"===o.exportType?(o.success(e.cvs),r(e.cvs)):setTimeout((function(){var t=e.cvs.toDataURL("image/"+o.type,o.quality);o.success(t),r(t)}),0)},e._next()}))},t.prototype._next=function(){if(this.queue.length>0){this.ctx.save();var t=this.queue.shift();t&&t(),this.ctx.restore()}else this.fn.success()},t.prototype.clear=function(){return this.ctx.clearRect(0,0,this.cvs.width,this.cvs.height),this},t}(); 2 | /*! ***************************************************************************** 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 13 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | PERFORMANCE OF THIS SOFTWARE. 15 | ***************************************************************************** */function s(t,e,r,n){return new(r||(r=Promise))((function(i,o){function a(t){try{u(n.next(t))}catch(t){o(t)}}function c(t){try{u(n.throw(t))}catch(t){o(t)}}function u(t){var e;t.done?i(t.value):(e=t.value,e instanceof r?e:new r((function(t){t(e)}))).then(a,c)}u((n=n.apply(t,e||[])).next())}))}function h(t,e){var r,n,i,o,a={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function c(o){return function(c){return function(o){if(r)throw new TypeError("Generator is already executing.");for(;a;)try{if(r=1,n&&(i=2&o[0]?n.return:o[0]?n.throw||((i=n.return)&&i.call(n),0):n.next)&&!(i=i.call(n,o[1])).done)return i;switch(n=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return a.label++,{value:o[1],done:!1};case 5:a.label++,n=o[1],o=[0];continue;case 7:o=a.ops.pop(),a.trys.pop();continue;default:if(!(i=a.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]0){var r=e.queue.shift();r&&(t=e.before(t),r(e._next,t),e.isFirst=!1)}else e.end(t),e.queue=[]};var r=t||{},n=r.before,i=void 0===n?function(t){return t}:n,o=r.after,a=void 0===o?function(t){return t}:o;this.before=i,this.after=a}return t.prototype.push=function(t){d.a.fn(t)&&this.queue.push(t)},t.prototype.perform=function(t,e){this.end=t,this._next(e)},t}();!function(){function t(){this._queue=new l}t.prototype.line=function(t){var e=this;return this._queue.push((function(r,n){setTimeout((function(){return s(e,void 0,void 0,(function(){var e;return h(this,(function(i){return e=t(n),d.a.promise(e)?e.then(r):r(e),[2]}))}))}),0)})),this},t.prototype.start=function(t,e){this._queue.perform(t,e)}}();function p(t){return(p="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}var g=[512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,289,287,285,282,280,278,275,273,271,269,267,265,263,261,259],v=[9,11,12,13,13,14,14,15,15,15,15,16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24];function y(t,e,r,n,i){if("string"==typeof t&&(t=document.getElementById(t)),!t||"object"!==p(t)||!("getContext"in t))throw new TypeError("Expecting canvas with `getContext` method in processCanvasRGB(A) calls!");var o=t.getContext("2d");try{return o.getImageData(e,r,n,i)}catch(t){throw new Error("unable to access image data: "+t)}}function b(t,e,r,n,i,o){if(!(isNaN(o)||o<1)){o|=0;var a=y(t,e,r,n,i);a=function(t,e,r,n,i,o){var a,c,u,s,h,f,d,l,p,y,b,m,w,j,_,O,k,I,C,M,S,P,q,T,R,D=t.data,N=2*o+1,W=n-1,B=i-1,A=o+1,E=A*(A+1)/2,H=new x,F=H;for(u=1;u>Y,0!==q?(q=255/q,D[f]=(l*G>>Y)*q,D[f+1]=(p*G>>Y)*q,D[f+2]=(y*G>>Y)*q):D[f]=D[f+1]=D[f+2]=0,l-=m,p-=w,y-=j,b-=_,m-=z.r,w-=z.g,j-=z.b,_-=z.a,s=d+((s=a+o+1)>Y,q>0?(q=255/q,D[s]=(l*G>>Y)*q,D[s+1]=(p*G>>Y)*q,D[s+2]=(y*G>>Y)*q):D[s]=D[s+1]=D[s+2]=0,l-=m,p-=w,y-=j,b-=_,m-=z.r,w-=z.g,j-=z.b,_-=z.a,s=a+((s=c+A)b&&(c=b),n=0;n<_;n++){for((o=(i=n*e)+e)>y&&(o=y),l=p=g=v=0,d=(o-i)*(c-a),s=a;s=j))for(o=n*w,f=-e;f<=e;f++)(i=c+f)<0||i>=w||(d=O[a=o+i<<2],v=(l=O[a+1])*r>>8,y=(p=O[a+2])*r>>8,P[g=d*r>>8]+=d,q[v]+=l,T[y]+=p,C[g]+=1,M[v]+=1,S[y]+=1);for(b=x=m=0,s=1;sC[b]&&(b=s),M[s]>M[x]&&(x=s),S[s]>S[m]&&(m=s);I[R]=P[b]/C[b]|0,I[R+1]=q[x]/M[x]|0,I[R+2]=T[m]/S[m]|0,I[R+3]=O[R+3],R+=4}return _.putImageData(k,0,0),[t,_]}var k=function(){function t(t){this._image=t,this._queue=new l,this.clear()}return t.prototype.clear=function(){return this._getImage()._drawCanvas(),this},t.prototype._drawCanvas=function(){return this._run((function(t){var e=Object(n.e)(t),r=e.iw,i=e.ih,o=a.create(r,i),c=o[0],u=o[1];return u.drawImage(t,0,0,r,i),{cvs:c,ctx:u}}))},t.prototype._getImage=function(){var t=this;return this._run((function(){return new Promise((function(e){return s(t,void 0,void 0,(function(){var t=this;return h(this,(function(r){return n.g.str(this._image)?a.getImage(this._image).then((function(r){return e(t._image=r)})).catch(this._error):e(this._image),[2]}))}))}))}))},t.prototype._run=function(t){var e=this;return this._queue.push((function(r,i){setTimeout((function(){return s(e,void 0,void 0,(function(){var e;return h(this,(function(o){return e=t(i),n.g.promise(e)?e.then(r):r(e),[2]}))}))}),0)})),this},t.prototype.draw=function(t){var e=this;return void 0===t&&(t={}),new Promise((function(r,i){var o={type:"jpeg",quality:1,exportType:"base64",success:function(t){},error:function(t){}};n.g.fn(t)?o.success=t:"jpg"===(o=Object(n.b)(!0,o,t)).type&&(o.type="jpeg");var a=o.exportType,c=o.type,u=o.quality,s=o.success,h=o.error,f=function(t){s(t),r(t),e.clear()};e._error=function(t){h(t),i(t)},e._queue.perform((function(t){var e=t.cvs,r=t.quality;"canvas"===a?f(e):setTimeout((function(){var t=e.toDataURL("image/"+c,r||u);f(t)}),0)}))}))},t.prototype.crop=function(t){return void 0===t&&(t={}),this._run((function(e){return c(e.cvs,t)}))},t.prototype.compress=function(t){return void 0===t&&(t={}),this._run((function(e){var r=e.cvs,i=e.ctx,o=t.quality,c=t.width,u=t.height,s=r.width/r.height;if(!n.g.num(c)&&!n.g.num(u))return{cvs:r,ctx:i,quality:o};n.g.num(c)||(c=u*s),n.g.num(u)||(u=c/s);var h=a.create(c,u),f=h[0],d=h[1];return d.drawImage(r,0,0,c,u),{cvs:f,ctx:d,quality:o}}))},t.prototype.filter=function(t){for(var e=[],r=1;r .window { 213 | display: inline-block; 214 | position: absolute; 215 | padding: 20px 50px; 216 | left: 50%; 217 | top: 50%; 218 | transform: translate(-50%, -50%); 219 | -webkit-transform: translate(-50%, -50%); 220 | background: white; 221 | max-width: 400px; 222 | text-align: center; 223 | width: 70%; 224 | border-radius: 5px; } 225 | 226 | .window > .Button { 227 | width: 40%; 228 | min-width: 40px; } 229 | 230 | .params { 231 | text-align: left; 232 | margin: 20px 0; 233 | font-size: 20px; 234 | line-height: 34px; } 235 | .params li { 236 | position: relative; 237 | white-space: nowrap; } 238 | .params .input { 239 | position: absolute; 240 | border: 2px solid #eee; 241 | border-radius: 5px; 242 | right: 60px; 243 | width: 100px; 244 | height: 70%; 245 | box-sizing: border-box; 246 | padding: 12px; 247 | text-align: center; 248 | font-size: 16px; } 249 | .params .input.focus { 250 | border: 2px solid #FF9999; } 251 | .params .select { 252 | position: absolute; 253 | right: 60px; 254 | border-radius: 5px; 255 | padding: 5px 20px; 256 | box-sizing: border-box; 257 | background: white; 258 | border: 2px solid #eee; 259 | font-size: 16px; } 260 | 261 | .right__crop { 262 | text-align: left; } 263 | .right__crop__title { 264 | font-size: 26px; 265 | color: #8566aa; 266 | margin-bottom: 20px; } 267 | .right__crop__img { 268 | vertical-align: middle; 269 | display: inline-block; 270 | width: 200px; 271 | height: 200px; 272 | margin-right: 30px; 273 | border: 2px solid #8566aa; 274 | border-radius: 5px; 275 | background-size: contain; 276 | background-position: center; 277 | background-repeat: no-repeat; 278 | background-image: url("http://mtapplet.meitudata.com/596c72073971d86b5128.jpg"); } 279 | 280 | .right__filter { 281 | text-align: left; 282 | width: 80%; } 283 | .right__filter__title { 284 | font-size: 26px; 285 | color: #8566aa; 286 | margin-bottom: 20px; 287 | margin-top: 40px; } 288 | .right__filter__list { 289 | white-space: nowrap; 290 | overflow-x: scroll; } 291 | .right__filter__btn { 292 | display: inline-block; 293 | font-size: 20px; 294 | line-height: 40px; 295 | color: #8566aa; 296 | text-align: center; 297 | padding: 0 20px; 298 | border-radius: 5px; 299 | border: 2px solid #8566aa; 300 | margin-top: 10px; 301 | cursor: pointer; } 302 | .right__filter__btn:hover { 303 | color: white; 304 | background-color: #8566aa; } 305 | .right__filter__img { 306 | text-align: center; 307 | display: inline-block; 308 | width: 250px; 309 | height: 360px; 310 | border: 2px solid #8566aa; 311 | border-radius: 5px; 312 | background-size: 90% auto; 313 | background-position: center bottom; 314 | background-repeat: no-repeat; 315 | background-image: url("http://mtapplet.meitudata.com/596c72073971d86b5128.jpg"); 316 | margin: 0 20px 0 0; } 317 | 318 | -------------------------------------------------------------------------------- /example/dist/images/1.7a9a8328.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xd-tayde/mcanvas/ce4108309c070aa2183d32fb918851c089e0f93e/example/dist/images/1.7a9a8328.jpg -------------------------------------------------------------------------------- /example/dist/images/3.f15f13f2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xd-tayde/mcanvas/ce4108309c070aa2183d32fb918851c089e0f93e/example/dist/images/3.f15f13f2.jpg -------------------------------------------------------------------------------- /example/dist/images/ear.c5b725cb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xd-tayde/mcanvas/ce4108309c070aa2183d32fb918851c089e0f93e/example/dist/images/ear.c5b725cb.png -------------------------------------------------------------------------------- /example/dist/images/watermark.c925393e.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xd-tayde/mcanvas/ce4108309c070aa2183d32fb918851c089e0f93e/example/dist/images/watermark.c925393e.jpg -------------------------------------------------------------------------------- /example/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | MCanvas 8 | 35 | 36 | 37 |

MCanvas

38 |

mcanvas is a image handler plugin that can easily merge, crop, compress,
filter the image and export a image of base64 finally. DOCS

39 |
40 |
MERGE IMAGE:
41 |
42 | 43 |
44 |
45 | Add Image 46 | Watermark 47 | Add Text 48 | Add Rect 49 | Add Circle 50 | Reset 51 |
52 |
53 |
54 |
55 |
CROP IMAGE:
56 |
57 | Crop Image 58 |
59 |
60 |
FILTER IMAGE:
61 |
62 |
63 |
Blur
64 |
65 |
66 |
Flip
67 |
68 |
69 |
Gray
70 |
71 |
72 |
Mosaic
73 |
74 | 77 |
78 |
79 |
80 | 83 |
84 |
85 |

Params:

86 |
    87 | Cancel 88 | OK 89 |
    90 |
    91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /example/dist/zepto.min.js: -------------------------------------------------------------------------------- 1 | /* Zepto v1.2.0 - zepto event ajax form ie - zeptojs.com/license */ 2 | !function(t,e){"function"==typeof define&&define.amd?define(function(){return e(t)}):e(t)}(this,function(t){var e=function(){function $(t){return null==t?String(t):S[C.call(t)]||"object"}function F(t){return"function"==$(t)}function k(t){return null!=t&&t==t.window}function M(t){return null!=t&&t.nodeType==t.DOCUMENT_NODE}function R(t){return"object"==$(t)}function Z(t){return R(t)&&!k(t)&&Object.getPrototypeOf(t)==Object.prototype}function z(t){var e=!!t&&"length"in t&&t.length,n=r.type(t);return"function"!=n&&!k(t)&&("array"==n||0===e||"number"==typeof e&&e>0&&e-1 in t)}function q(t){return a.call(t,function(t){return null!=t})}function H(t){return t.length>0?r.fn.concat.apply([],t):t}function I(t){return t.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()}function V(t){return t in l?l[t]:l[t]=new RegExp("(^|\\s)"+t+"(\\s|$)")}function _(t,e){return"number"!=typeof e||h[I(t)]?e:e+"px"}function B(t){var e,n;return c[t]||(e=f.createElement(t),f.body.appendChild(e),n=getComputedStyle(e,"").getPropertyValue("display"),e.parentNode.removeChild(e),"none"==n&&(n="block"),c[t]=n),c[t]}function U(t){return"children"in t?u.call(t.children):r.map(t.childNodes,function(t){return 1==t.nodeType?t:void 0})}function X(t,e){var n,r=t?t.length:0;for(n=0;r>n;n++)this[n]=t[n];this.length=r,this.selector=e||""}function J(t,r,i){for(n in r)i&&(Z(r[n])||L(r[n]))?(Z(r[n])&&!Z(t[n])&&(t[n]={}),L(r[n])&&!L(t[n])&&(t[n]=[]),J(t[n],r[n],i)):r[n]!==e&&(t[n]=r[n])}function W(t,e){return null==e?r(t):r(t).filter(e)}function Y(t,e,n,r){return F(e)?e.call(t,n,r):e}function G(t,e,n){null==n?t.removeAttribute(e):t.setAttribute(e,n)}function K(t,n){var r=t.className||"",i=r&&r.baseVal!==e;return n===e?i?r.baseVal:r:void(i?r.baseVal=n:t.className=n)}function Q(t){try{return t?"true"==t||("false"==t?!1:"null"==t?null:+t+""==t?+t:/^[\[\{]/.test(t)?r.parseJSON(t):t):t}catch(e){return t}}function tt(t,e){e(t);for(var n=0,r=t.childNodes.length;r>n;n++)tt(t.childNodes[n],e)}var e,n,r,i,O,P,o=[],s=o.concat,a=o.filter,u=o.slice,f=t.document,c={},l={},h={"column-count":1,columns:1,"font-weight":1,"line-height":1,opacity:1,"z-index":1,zoom:1},p=/^\s*<(\w+|!)[^>]*>/,d=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,m=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,g=/^(?:body|html)$/i,v=/([A-Z])/g,y=["val","css","html","text","data","width","height","offset"],x=["after","prepend","before","append"],b=f.createElement("table"),E=f.createElement("tr"),j={tr:f.createElement("tbody"),tbody:b,thead:b,tfoot:b,td:E,th:E,"*":f.createElement("div")},w=/complete|loaded|interactive/,T=/^[\w-]*$/,S={},C=S.toString,N={},A=f.createElement("div"),D={tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},L=Array.isArray||function(t){return t instanceof Array};return N.matches=function(t,e){if(!e||!t||1!==t.nodeType)return!1;var n=t.matches||t.webkitMatchesSelector||t.mozMatchesSelector||t.oMatchesSelector||t.matchesSelector;if(n)return n.call(t,e);var r,i=t.parentNode,o=!i;return o&&(i=A).appendChild(t),r=~N.qsa(i,e).indexOf(t),o&&A.removeChild(t),r},O=function(t){return t.replace(/-+(.)?/g,function(t,e){return e?e.toUpperCase():""})},P=function(t){return a.call(t,function(e,n){return t.indexOf(e)==n})},N.fragment=function(t,n,i){var o,s,a;return d.test(t)&&(o=r(f.createElement(RegExp.$1))),o||(t.replace&&(t=t.replace(m,"<$1>")),n===e&&(n=p.test(t)&&RegExp.$1),n in j||(n="*"),a=j[n],a.innerHTML=""+t,o=r.each(u.call(a.childNodes),function(){a.removeChild(this)})),Z(i)&&(s=r(o),r.each(i,function(t,e){y.indexOf(t)>-1?s[t](e):s.attr(t,e)})),o},N.Z=function(t,e){return new X(t,e)},N.isZ=function(t){return t instanceof N.Z},N.init=function(t,n){var i;if(!t)return N.Z();if("string"==typeof t)if(t=t.trim(),"<"==t[0]&&p.test(t))i=N.fragment(t,RegExp.$1,n),t=null;else{if(n!==e)return r(n).find(t);i=N.qsa(f,t)}else{if(F(t))return r(f).ready(t);if(N.isZ(t))return t;if(L(t))i=q(t);else if(R(t))i=[t],t=null;else if(p.test(t))i=N.fragment(t.trim(),RegExp.$1,n),t=null;else{if(n!==e)return r(n).find(t);i=N.qsa(f,t)}}return N.Z(i,t)},r=function(t,e){return N.init(t,e)},r.extend=function(t){var e,n=u.call(arguments,1);return"boolean"==typeof t&&(e=t,t=n.shift()),n.forEach(function(n){J(t,n,e)}),t},N.qsa=function(t,e){var n,r="#"==e[0],i=!r&&"."==e[0],o=r||i?e.slice(1):e,s=T.test(o);return t.getElementById&&s&&r?(n=t.getElementById(o))?[n]:[]:1!==t.nodeType&&9!==t.nodeType&&11!==t.nodeType?[]:u.call(s&&!r&&t.getElementsByClassName?i?t.getElementsByClassName(o):t.getElementsByTagName(e):t.querySelectorAll(e))},r.contains=f.documentElement.contains?function(t,e){return t!==e&&t.contains(e)}:function(t,e){for(;e&&(e=e.parentNode);)if(e===t)return!0;return!1},r.type=$,r.isFunction=F,r.isWindow=k,r.isArray=L,r.isPlainObject=Z,r.isEmptyObject=function(t){var e;for(e in t)return!1;return!0},r.isNumeric=function(t){var e=Number(t),n=typeof t;return null!=t&&"boolean"!=n&&("string"!=n||t.length)&&!isNaN(e)&&isFinite(e)||!1},r.inArray=function(t,e,n){return o.indexOf.call(e,t,n)},r.camelCase=O,r.trim=function(t){return null==t?"":String.prototype.trim.call(t)},r.uuid=0,r.support={},r.expr={},r.noop=function(){},r.map=function(t,e){var n,i,o,r=[];if(z(t))for(i=0;i=0?t:t+this.length]},toArray:function(){return this.get()},size:function(){return this.length},remove:function(){return this.each(function(){null!=this.parentNode&&this.parentNode.removeChild(this)})},each:function(t){return o.every.call(this,function(e,n){return t.call(e,n,e)!==!1}),this},filter:function(t){return F(t)?this.not(this.not(t)):r(a.call(this,function(e){return N.matches(e,t)}))},add:function(t,e){return r(P(this.concat(r(t,e))))},is:function(t){return this.length>0&&N.matches(this[0],t)},not:function(t){var n=[];if(F(t)&&t.call!==e)this.each(function(e){t.call(this,e)||n.push(this)});else{var i="string"==typeof t?this.filter(t):z(t)&&F(t.item)?u.call(t):r(t);this.forEach(function(t){i.indexOf(t)<0&&n.push(t)})}return r(n)},has:function(t){return this.filter(function(){return R(t)?r.contains(this,t):r(this).find(t).size()})},eq:function(t){return-1===t?this.slice(t):this.slice(t,+t+1)},first:function(){var t=this[0];return t&&!R(t)?t:r(t)},last:function(){var t=this[this.length-1];return t&&!R(t)?t:r(t)},find:function(t){var e,n=this;return e=t?"object"==typeof t?r(t).filter(function(){var t=this;return o.some.call(n,function(e){return r.contains(e,t)})}):1==this.length?r(N.qsa(this[0],t)):this.map(function(){return N.qsa(this,t)}):r()},closest:function(t,e){var n=[],i="object"==typeof t&&r(t);return this.each(function(r,o){for(;o&&!(i?i.indexOf(o)>=0:N.matches(o,t));)o=o!==e&&!M(o)&&o.parentNode;o&&n.indexOf(o)<0&&n.push(o)}),r(n)},parents:function(t){for(var e=[],n=this;n.length>0;)n=r.map(n,function(t){return(t=t.parentNode)&&!M(t)&&e.indexOf(t)<0?(e.push(t),t):void 0});return W(e,t)},parent:function(t){return W(P(this.pluck("parentNode")),t)},children:function(t){return W(this.map(function(){return U(this)}),t)},contents:function(){return this.map(function(){return this.contentDocument||u.call(this.childNodes)})},siblings:function(t){return W(this.map(function(t,e){return a.call(U(e.parentNode),function(t){return t!==e})}),t)},empty:function(){return this.each(function(){this.innerHTML=""})},pluck:function(t){return r.map(this,function(e){return e[t]})},show:function(){return this.each(function(){"none"==this.style.display&&(this.style.display=""),"none"==getComputedStyle(this,"").getPropertyValue("display")&&(this.style.display=B(this.nodeName))})},replaceWith:function(t){return this.before(t).remove()},wrap:function(t){var e=F(t);if(this[0]&&!e)var n=r(t).get(0),i=n.parentNode||this.length>1;return this.each(function(o){r(this).wrapAll(e?t.call(this,o):i?n.cloneNode(!0):n)})},wrapAll:function(t){if(this[0]){r(this[0]).before(t=r(t));for(var e;(e=t.children()).length;)t=e.first();r(t).append(this)}return this},wrapInner:function(t){var e=F(t);return this.each(function(n){var i=r(this),o=i.contents(),s=e?t.call(this,n):t;o.length?o.wrapAll(s):i.append(s)})},unwrap:function(){return this.parent().each(function(){r(this).replaceWith(r(this).children())}),this},clone:function(){return this.map(function(){return this.cloneNode(!0)})},hide:function(){return this.css("display","none")},toggle:function(t){return this.each(function(){var n=r(this);(t===e?"none"==n.css("display"):t)?n.show():n.hide()})},prev:function(t){return r(this.pluck("previousElementSibling")).filter(t||"*")},next:function(t){return r(this.pluck("nextElementSibling")).filter(t||"*")},html:function(t){return 0 in arguments?this.each(function(e){var n=this.innerHTML;r(this).empty().append(Y(this,t,e,n))}):0 in this?this[0].innerHTML:null},text:function(t){return 0 in arguments?this.each(function(e){var n=Y(this,t,e,this.textContent);this.textContent=null==n?"":""+n}):0 in this?this.pluck("textContent").join(""):null},attr:function(t,r){var i;return"string"!=typeof t||1 in arguments?this.each(function(e){if(1===this.nodeType)if(R(t))for(n in t)G(this,n,t[n]);else G(this,t,Y(this,r,e,this.getAttribute(t)))}):0 in this&&1==this[0].nodeType&&null!=(i=this[0].getAttribute(t))?i:e},removeAttr:function(t){return this.each(function(){1===this.nodeType&&t.split(" ").forEach(function(t){G(this,t)},this)})},prop:function(t,e){return t=D[t]||t,1 in arguments?this.each(function(n){this[t]=Y(this,e,n,this[t])}):this[0]&&this[0][t]},removeProp:function(t){return t=D[t]||t,this.each(function(){delete this[t]})},data:function(t,n){var r="data-"+t.replace(v,"-$1").toLowerCase(),i=1 in arguments?this.attr(r,n):this.attr(r);return null!==i?Q(i):e},val:function(t){return 0 in arguments?(null==t&&(t=""),this.each(function(e){this.value=Y(this,t,e,this.value)})):this[0]&&(this[0].multiple?r(this[0]).find("option").filter(function(){return this.selected}).pluck("value"):this[0].value)},offset:function(e){if(e)return this.each(function(t){var n=r(this),i=Y(this,e,t,n.offset()),o=n.offsetParent().offset(),s={top:i.top-o.top,left:i.left-o.left};"static"==n.css("position")&&(s.position="relative"),n.css(s)});if(!this.length)return null;if(f.documentElement!==this[0]&&!r.contains(f.documentElement,this[0]))return{top:0,left:0};var n=this[0].getBoundingClientRect();return{left:n.left+t.pageXOffset,top:n.top+t.pageYOffset,width:Math.round(n.width),height:Math.round(n.height)}},css:function(t,e){if(arguments.length<2){var i=this[0];if("string"==typeof t){if(!i)return;return i.style[O(t)]||getComputedStyle(i,"").getPropertyValue(t)}if(L(t)){if(!i)return;var o={},s=getComputedStyle(i,"");return r.each(t,function(t,e){o[e]=i.style[O(e)]||s.getPropertyValue(e)}),o}}var a="";if("string"==$(t))e||0===e?a=I(t)+":"+_(t,e):this.each(function(){this.style.removeProperty(I(t))});else for(n in t)t[n]||0===t[n]?a+=I(n)+":"+_(n,t[n])+";":this.each(function(){this.style.removeProperty(I(n))});return this.each(function(){this.style.cssText+=";"+a})},index:function(t){return t?this.indexOf(r(t)[0]):this.parent().children().indexOf(this[0])},hasClass:function(t){return t?o.some.call(this,function(t){return this.test(K(t))},V(t)):!1},addClass:function(t){return t?this.each(function(e){if("className"in this){i=[];var n=K(this),o=Y(this,t,e,n);o.split(/\s+/g).forEach(function(t){r(this).hasClass(t)||i.push(t)},this),i.length&&K(this,n+(n?" ":"")+i.join(" "))}}):this},removeClass:function(t){return this.each(function(n){if("className"in this){if(t===e)return K(this,"");i=K(this),Y(this,t,n,i).split(/\s+/g).forEach(function(t){i=i.replace(V(t)," ")}),K(this,i.trim())}})},toggleClass:function(t,n){return t?this.each(function(i){var o=r(this),s=Y(this,t,i,K(this));s.split(/\s+/g).forEach(function(t){(n===e?!o.hasClass(t):n)?o.addClass(t):o.removeClass(t)})}):this},scrollTop:function(t){if(this.length){var n="scrollTop"in this[0];return t===e?n?this[0].scrollTop:this[0].pageYOffset:this.each(n?function(){this.scrollTop=t}:function(){this.scrollTo(this.scrollX,t)})}},scrollLeft:function(t){if(this.length){var n="scrollLeft"in this[0];return t===e?n?this[0].scrollLeft:this[0].pageXOffset:this.each(n?function(){this.scrollLeft=t}:function(){this.scrollTo(t,this.scrollY)})}},position:function(){if(this.length){var t=this[0],e=this.offsetParent(),n=this.offset(),i=g.test(e[0].nodeName)?{top:0,left:0}:e.offset();return n.top-=parseFloat(r(t).css("margin-top"))||0,n.left-=parseFloat(r(t).css("margin-left"))||0,i.top+=parseFloat(r(e[0]).css("border-top-width"))||0,i.left+=parseFloat(r(e[0]).css("border-left-width"))||0,{top:n.top-i.top,left:n.left-i.left}}},offsetParent:function(){return this.map(function(){for(var t=this.offsetParent||f.body;t&&!g.test(t.nodeName)&&"static"==r(t).css("position");)t=t.offsetParent;return t})}},r.fn.detach=r.fn.remove,["width","height"].forEach(function(t){var n=t.replace(/./,function(t){return t[0].toUpperCase()});r.fn[t]=function(i){var o,s=this[0];return i===e?k(s)?s["inner"+n]:M(s)?s.documentElement["scroll"+n]:(o=this.offset())&&o[t]:this.each(function(e){s=r(this),s.css(t,Y(this,i,e,s[t]()))})}}),x.forEach(function(n,i){var o=i%2;r.fn[n]=function(){var n,a,s=r.map(arguments,function(t){var i=[];return n=$(t),"array"==n?(t.forEach(function(t){return t.nodeType!==e?i.push(t):r.zepto.isZ(t)?i=i.concat(t.get()):void(i=i.concat(N.fragment(t)))}),i):"object"==n||null==t?t:N.fragment(t)}),u=this.length>1;return s.length<1?this:this.each(function(e,n){a=o?n:n.parentNode,n=0==i?n.nextSibling:1==i?n.firstChild:2==i?n:null;var c=r.contains(f.documentElement,a);s.forEach(function(e){if(u)e=e.cloneNode(!0);else if(!a)return r(e).remove();a.insertBefore(e,n),c&&tt(e,function(e){if(!(null==e.nodeName||"SCRIPT"!==e.nodeName.toUpperCase()||e.type&&"text/javascript"!==e.type||e.src)){var n=e.ownerDocument?e.ownerDocument.defaultView:t;n.eval.call(n,e.innerHTML)}})})})},r.fn[o?n+"To":"insert"+(i?"Before":"After")]=function(t){return r(t)[n](this),this}}),N.Z.prototype=X.prototype=r.fn,N.uniq=P,N.deserializeValue=Q,r.zepto=N,r}();return t.Zepto=e,void 0===t.$&&(t.$=e),function(e){function h(t){return t._zid||(t._zid=n++)}function p(t,e,n,r){if(e=d(e),e.ns)var i=m(e.ns);return(a[h(t)]||[]).filter(function(t){return t&&(!e.e||t.e==e.e)&&(!e.ns||i.test(t.ns))&&(!n||h(t.fn)===h(n))&&(!r||t.sel==r)})}function d(t){var e=(""+t).split(".");return{e:e[0],ns:e.slice(1).sort().join(" ")}}function m(t){return new RegExp("(?:^| )"+t.replace(" "," .* ?")+"(?: |$)")}function g(t,e){return t.del&&!f&&t.e in c||!!e}function v(t){return l[t]||f&&c[t]||t}function y(t,n,i,o,s,u,f){var c=h(t),p=a[c]||(a[c]=[]);n.split(/\s/).forEach(function(n){if("ready"==n)return e(document).ready(i);var a=d(n);a.fn=i,a.sel=s,a.e in l&&(i=function(t){var n=t.relatedTarget;return!n||n!==this&&!e.contains(this,n)?a.fn.apply(this,arguments):void 0}),a.del=u;var c=u||i;a.proxy=function(e){if(e=T(e),!e.isImmediatePropagationStopped()){e.data=o;var n=c.apply(t,e._args==r?[e]:[e].concat(e._args));return n===!1&&(e.preventDefault(),e.stopPropagation()),n}},a.i=p.length,p.push(a),"addEventListener"in t&&t.addEventListener(v(a.e),a.proxy,g(a,f))})}function x(t,e,n,r,i){var o=h(t);(e||"").split(/\s/).forEach(function(e){p(t,e,n,r).forEach(function(e){delete a[o][e.i],"removeEventListener"in t&&t.removeEventListener(v(e.e),e.proxy,g(e,i))})})}function T(t,n){return(n||!t.isDefaultPrevented)&&(n||(n=t),e.each(w,function(e,r){var i=n[e];t[e]=function(){return this[r]=b,i&&i.apply(n,arguments)},t[r]=E}),t.timeStamp||(t.timeStamp=Date.now()),(n.defaultPrevented!==r?n.defaultPrevented:"returnValue"in n?n.returnValue===!1:n.getPreventDefault&&n.getPreventDefault())&&(t.isDefaultPrevented=b)),t}function S(t){var e,n={originalEvent:t};for(e in t)j.test(e)||t[e]===r||(n[e]=t[e]);return T(n,t)}var r,n=1,i=Array.prototype.slice,o=e.isFunction,s=function(t){return"string"==typeof t},a={},u={},f="onfocusin"in t,c={focus:"focusin",blur:"focusout"},l={mouseenter:"mouseover",mouseleave:"mouseout"};u.click=u.mousedown=u.mouseup=u.mousemove="MouseEvents",e.event={add:y,remove:x},e.proxy=function(t,n){var r=2 in arguments&&i.call(arguments,2);if(o(t)){var a=function(){return t.apply(n,r?r.concat(i.call(arguments)):arguments)};return a._zid=h(t),a}if(s(n))return r?(r.unshift(t[n],t),e.proxy.apply(null,r)):e.proxy(t[n],t);throw new TypeError("expected function")},e.fn.bind=function(t,e,n){return this.on(t,e,n)},e.fn.unbind=function(t,e){return this.off(t,e)},e.fn.one=function(t,e,n,r){return this.on(t,e,n,r,1)};var b=function(){return!0},E=function(){return!1},j=/^([A-Z]|returnValue$|layer[XY]$|webkitMovement[XY]$)/,w={preventDefault:"isDefaultPrevented",stopImmediatePropagation:"isImmediatePropagationStopped",stopPropagation:"isPropagationStopped"};e.fn.delegate=function(t,e,n){return this.on(e,t,n)},e.fn.undelegate=function(t,e,n){return this.off(e,t,n)},e.fn.live=function(t,n){return e(document.body).delegate(this.selector,t,n),this},e.fn.die=function(t,n){return e(document.body).undelegate(this.selector,t,n),this},e.fn.on=function(t,n,a,u,f){var c,l,h=this;return t&&!s(t)?(e.each(t,function(t,e){h.on(t,n,a,e,f)}),h):(s(n)||o(u)||u===!1||(u=a,a=n,n=r),(u===r||a===!1)&&(u=a,a=r),u===!1&&(u=E),h.each(function(r,o){f&&(c=function(t){return x(o,t.type,u),u.apply(this,arguments)}),n&&(l=function(t){var r,s=e(t.target).closest(n,o).get(0);return s&&s!==o?(r=e.extend(S(t),{currentTarget:s,liveFired:o}),(c||u).apply(s,[r].concat(i.call(arguments,1)))):void 0}),y(o,t,u,a,n,l||c)}))},e.fn.off=function(t,n,i){var a=this;return t&&!s(t)?(e.each(t,function(t,e){a.off(t,n,e)}),a):(s(n)||o(i)||i===!1||(i=n,n=r),i===!1&&(i=E),a.each(function(){x(this,t,i,n)}))},e.fn.trigger=function(t,n){return t=s(t)||e.isPlainObject(t)?e.Event(t):T(t),t._args=n,this.each(function(){t.type in c&&"function"==typeof this[t.type]?this[t.type]():"dispatchEvent"in this?this.dispatchEvent(t):e(this).triggerHandler(t,n)})},e.fn.triggerHandler=function(t,n){var r,i;return this.each(function(o,a){r=S(s(t)?e.Event(t):t),r._args=n,r.target=a,e.each(p(a,t.type||t),function(t,e){return i=e.proxy(r),r.isImmediatePropagationStopped()?!1:void 0})}),i},"focusin focusout focus blur load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select keydown keypress keyup error".split(" ").forEach(function(t){e.fn[t]=function(e){return 0 in arguments?this.bind(t,e):this.trigger(t)}}),e.Event=function(t,e){s(t)||(e=t,t=e.type);var n=document.createEvent(u[t]||"Events"),r=!0;if(e)for(var i in e)"bubbles"==i?r=!!e[i]:n[i]=e[i];return n.initEvent(t,r,!0),T(n)}}(e),function(e){function p(t,n,r){var i=e.Event(n);return e(t).trigger(i,r),!i.isDefaultPrevented()}function d(t,e,n,i){return t.global?p(e||r,n,i):void 0}function m(t){t.global&&0===e.active++&&d(t,null,"ajaxStart")}function g(t){t.global&&!--e.active&&d(t,null,"ajaxStop")}function v(t,e){var n=e.context;return e.beforeSend.call(n,t,e)===!1||d(e,n,"ajaxBeforeSend",[t,e])===!1?!1:void d(e,n,"ajaxSend",[t,e])}function y(t,e,n,r){var i=n.context,o="success";n.success.call(i,t,o,e),r&&r.resolveWith(i,[t,o,e]),d(n,i,"ajaxSuccess",[e,n,t]),b(o,e,n)}function x(t,e,n,r,i){var o=r.context;r.error.call(o,n,e,t),i&&i.rejectWith(o,[n,e,t]),d(r,o,"ajaxError",[n,r,t||e]),b(e,n,r)}function b(t,e,n){var r=n.context;n.complete.call(r,e,t),d(n,r,"ajaxComplete",[e,n]),g(n)}function E(t,e,n){if(n.dataFilter==j)return t;var r=n.context;return n.dataFilter.call(r,t,e)}function j(){}function w(t){return t&&(t=t.split(";",2)[0]),t&&(t==c?"html":t==f?"json":a.test(t)?"script":u.test(t)&&"xml")||"text"}function T(t,e){return""==e?t:(t+"&"+e).replace(/[&?]{1,2}/,"?")}function S(t){t.processData&&t.data&&"string"!=e.type(t.data)&&(t.data=e.param(t.data,t.traditional)),!t.data||t.type&&"GET"!=t.type.toUpperCase()&&"jsonp"!=t.dataType||(t.url=T(t.url,t.data),t.data=void 0)}function C(t,n,r,i){return e.isFunction(n)&&(i=r,r=n,n=void 0),e.isFunction(r)||(i=r,r=void 0),{url:t,data:n,success:r,dataType:i}}function O(t,n,r,i){var o,s=e.isArray(n),a=e.isPlainObject(n);e.each(n,function(n,u){o=e.type(u),i&&(n=r?i:i+"["+(a||"object"==o||"array"==o?n:"")+"]"),!i&&s?t.add(u.name,u.value):"array"==o||!r&&"object"==o?O(t,u,r,n):t.add(n,u)})}var i,o,n=+new Date,r=t.document,s=/)<[^<]*)*<\/script>/gi,a=/^(?:text|application)\/javascript/i,u=/^(?:text|application)\/xml/i,f="application/json",c="text/html",l=/^\s*$/,h=r.createElement("a");h.href=t.location.href,e.active=0,e.ajaxJSONP=function(i,o){if(!("type"in i))return e.ajax(i);var c,p,s=i.jsonpCallback,a=(e.isFunction(s)?s():s)||"Zepto"+n++,u=r.createElement("script"),f=t[a],l=function(t){e(u).triggerHandler("error",t||"abort")},h={abort:l};return o&&o.promise(h),e(u).on("load error",function(n,r){clearTimeout(p),e(u).off().remove(),"error"!=n.type&&c?y(c[0],h,i,o):x(null,r||"error",h,i,o),t[a]=f,c&&e.isFunction(f)&&f(c[0]),f=c=void 0}),v(h,i)===!1?(l("abort"),h):(t[a]=function(){c=arguments},u.src=i.url.replace(/\?(.+)=\?/,"?$1="+a),r.head.appendChild(u),i.timeout>0&&(p=setTimeout(function(){l("timeout")},i.timeout)),h)},e.ajaxSettings={type:"GET",beforeSend:j,success:j,error:j,complete:j,context:null,global:!0,xhr:function(){return new t.XMLHttpRequest},accepts:{script:"text/javascript, application/javascript, application/x-javascript",json:f,xml:"application/xml, text/xml",html:c,text:"text/plain"},crossDomain:!1,timeout:0,processData:!0,cache:!0,dataFilter:j},e.ajax=function(n){var u,f,s=e.extend({},n||{}),a=e.Deferred&&e.Deferred();for(i in e.ajaxSettings)void 0===s[i]&&(s[i]=e.ajaxSettings[i]);m(s),s.crossDomain||(u=r.createElement("a"),u.href=s.url,u.href=u.href,s.crossDomain=h.protocol+"//"+h.host!=u.protocol+"//"+u.host),s.url||(s.url=t.location.toString()),(f=s.url.indexOf("#"))>-1&&(s.url=s.url.slice(0,f)),S(s);var c=s.dataType,p=/\?.+=\?/.test(s.url);if(p&&(c="jsonp"),s.cache!==!1&&(n&&n.cache===!0||"script"!=c&&"jsonp"!=c)||(s.url=T(s.url,"_="+Date.now())),"jsonp"==c)return p||(s.url=T(s.url,s.jsonp?s.jsonp+"=?":s.jsonp===!1?"":"callback=?")),e.ajaxJSONP(s,a);var P,d=s.accepts[c],g={},b=function(t,e){g[t.toLowerCase()]=[t,e]},C=/^([\w-]+:)\/\//.test(s.url)?RegExp.$1:t.location.protocol,N=s.xhr(),O=N.setRequestHeader;if(a&&a.promise(N),s.crossDomain||b("X-Requested-With","XMLHttpRequest"),b("Accept",d||"*/*"),(d=s.mimeType||d)&&(d.indexOf(",")>-1&&(d=d.split(",",2)[0]),N.overrideMimeType&&N.overrideMimeType(d)),(s.contentType||s.contentType!==!1&&s.data&&"GET"!=s.type.toUpperCase())&&b("Content-Type",s.contentType||"application/x-www-form-urlencoded"),s.headers)for(o in s.headers)b(o,s.headers[o]);if(N.setRequestHeader=b,N.onreadystatechange=function(){if(4==N.readyState){N.onreadystatechange=j,clearTimeout(P);var t,n=!1;if(N.status>=200&&N.status<300||304==N.status||0==N.status&&"file:"==C){if(c=c||w(s.mimeType||N.getResponseHeader("content-type")),"arraybuffer"==N.responseType||"blob"==N.responseType)t=N.response;else{t=N.responseText;try{t=E(t,c,s),"script"==c?(1,eval)(t):"xml"==c?t=N.responseXML:"json"==c&&(t=l.test(t)?null:e.parseJSON(t))}catch(r){n=r}if(n)return x(n,"parsererror",N,s,a)}y(t,N,s,a)}else x(N.statusText||null,N.status?"error":"abort",N,s,a)}},v(N,s)===!1)return N.abort(),x(null,"abort",N,s,a),N;var A="async"in s?s.async:!0;if(N.open(s.type,s.url,A,s.username,s.password),s.xhrFields)for(o in s.xhrFields)N[o]=s.xhrFields[o];for(o in g)O.apply(N,g[o]);return s.timeout>0&&(P=setTimeout(function(){N.onreadystatechange=j,N.abort(),x(null,"timeout",N,s,a)},s.timeout)),N.send(s.data?s.data:null),N},e.get=function(){return e.ajax(C.apply(null,arguments))},e.post=function(){var t=C.apply(null,arguments);return t.type="POST",e.ajax(t)},e.getJSON=function(){var t=C.apply(null,arguments);return t.dataType="json",e.ajax(t)},e.fn.load=function(t,n,r){if(!this.length)return this;var a,i=this,o=t.split(/\s/),u=C(t,n,r),f=u.success;return o.length>1&&(u.url=o[0],a=o[1]),u.success=function(t){i.html(a?e("
    ").html(t.replace(s,"")).find(a):t),f&&f.apply(i,arguments)},e.ajax(u),this};var N=encodeURIComponent;e.param=function(t,n){var r=[];return r.add=function(t,n){e.isFunction(n)&&(n=n()),null==n&&(n=""),this.push(N(t)+"="+N(n))},O(r,t,n),r.join("&").replace(/%20/g,"+")}}(e),function(t){t.fn.serializeArray=function(){var e,n,r=[],i=function(t){return t.forEach?t.forEach(i):void r.push({name:e,value:t})};return this[0]&&t.each(this[0].elements,function(r,o){n=o.type,e=o.name,e&&"fieldset"!=o.nodeName.toLowerCase()&&!o.disabled&&"submit"!=n&&"reset"!=n&&"button"!=n&&"file"!=n&&("radio"!=n&&"checkbox"!=n||o.checked)&&i(t(o).val())}),r},t.fn.serialize=function(){var t=[];return this.serializeArray().forEach(function(e){t.push(encodeURIComponent(e.name)+"="+encodeURIComponent(e.value))}),t.join("&")},t.fn.submit=function(e){if(0 in arguments)this.bind("submit",e);else if(this.length){var n=t.Event("submit");this.eq(0).trigger(n),n.isDefaultPrevented()||this.get(0).submit()}return this}}(e),function(){try{getComputedStyle(void 0)}catch(e){var n=getComputedStyle;t.getComputedStyle=function(t,e){try{return n(t,e)}catch(r){return null}}}}(),e}); 3 | -------------------------------------------------------------------------------- /example/images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xd-tayde/mcanvas/ce4108309c070aa2183d32fb918851c089e0f93e/example/images/1.jpg -------------------------------------------------------------------------------- /example/images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xd-tayde/mcanvas/ce4108309c070aa2183d32fb918851c089e0f93e/example/images/1.png -------------------------------------------------------------------------------- /example/images/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xd-tayde/mcanvas/ce4108309c070aa2183d32fb918851c089e0f93e/example/images/2.jpg -------------------------------------------------------------------------------- /example/images/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xd-tayde/mcanvas/ce4108309c070aa2183d32fb918851c089e0f93e/example/images/3.jpg -------------------------------------------------------------------------------- /example/images/ear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xd-tayde/mcanvas/ce4108309c070aa2183d32fb918851c089e0f93e/example/images/ear.png -------------------------------------------------------------------------------- /example/images/icon_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xd-tayde/mcanvas/ce4108309c070aa2183d32fb918851c089e0f93e/example/images/icon_download.png -------------------------------------------------------------------------------- /example/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xd-tayde/mcanvas/ce4108309c070aa2183d32fb918851c089e0f93e/example/images/loading.gif -------------------------------------------------------------------------------- /example/images/mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xd-tayde/mcanvas/ce4108309c070aa2183d32fb918851c089e0f93e/example/images/mask.png -------------------------------------------------------------------------------- /example/images/neck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xd-tayde/mcanvas/ce4108309c070aa2183d32fb918851c089e0f93e/example/images/neck.png -------------------------------------------------------------------------------- /example/images/nose.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xd-tayde/mcanvas/ce4108309c070aa2183d32fb918851c089e0f93e/example/images/nose.png -------------------------------------------------------------------------------- /example/images/p.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xd-tayde/mcanvas/ce4108309c070aa2183d32fb918851c089e0f93e/example/images/p.jpg -------------------------------------------------------------------------------- /example/images/p2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xd-tayde/mcanvas/ce4108309c070aa2183d32fb918851c089e0f93e/example/images/p2.jpg -------------------------------------------------------------------------------- /example/images/pinch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xd-tayde/mcanvas/ce4108309c070aa2183d32fb918851c089e0f93e/example/images/pinch.png -------------------------------------------------------------------------------- /example/images/transparent.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xd-tayde/mcanvas/ce4108309c070aa2183d32fb918851c089e0f93e/example/images/transparent.gif -------------------------------------------------------------------------------- /example/images/watermark.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xd-tayde/mcanvas/ce4108309c070aa2183d32fb918851c089e0f93e/example/images/watermark.jpg -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | MCanvas 8 | 35 | 36 | 37 |

    MCanvas

    38 |

    mcanvas is a image handler plugin that can easily merge, crop, compress,
    filter the image and export a image of base64 finally. DOCS

    39 |
    40 |
    MERGE IMAGE:
    41 |
    42 | 43 |
    44 |
    45 | Add Image 46 | Watermark 47 | Add Text 48 | Add Rect 49 | Add Circle 50 | Reset 51 |
    52 |
    53 |
    54 |
    55 |
    CROP IMAGE:
    56 |
    57 | Crop Image 58 |
    59 |
    60 |
    FILTER IMAGE:
    61 |
    62 |
    63 |
    Blur
    64 |
    65 |
    66 |
    Flip
    67 |
    68 |
    69 |
    Gray
    70 |
    71 |
    72 |
    Mosaic
    73 |
    74 | 77 |
    78 |
    79 |
    80 | 83 |
    84 |
    85 |

    Params:

    86 |
      87 | Cancel 88 | OK 89 |
      90 |
      91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /example/main.scss: -------------------------------------------------------------------------------- 1 | /* reset */ 2 | body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, fieldset, legend, input, button, textarea, p, blockquote, th, td ,header, section, aside, nav, menu, hgroup, audio, video, figure, figcaption, footer, article{ margin:0; padding:0; } 3 | table { border-collapse:collapse; border-spacing:0; } 4 | em { font-style:normal; } 5 | fieldset, img { border:0; vertical-align: middle;} 6 | li { list-style:none; } 7 | caption, th { text-align:left; } 8 | h1, h2, h3, h4, h5, h6 { font-size:100%;} 9 | body,input, button, textarea, select {*font-size:100%;outline:none; -webkit-appearance:none; } 10 | textarea { resize: none; outline:none; } 11 | i,cite{font-style:normal;} 12 | a {text-decoration:none; } 13 | a:focus{outline: none;} 14 | html{font-size:20px;} 15 | html,body{-webkit-touch-callout:none;height:100%;} 16 | .clearfix { zoom:1; } 17 | .clearfix:after { content:""; display:block;clear:both;height: 0;visibility: hidden;} 18 | .hide{display:none} .fr{float:right} .fl{float:left;}.block{display: block;} 19 | .bold{font-weight: bold;} .center{position:absolute;left: 50%;top: 50%;transform:translate(-50%,-50%);} .auto{margin-left:auto;margin-right:auto} 20 | 21 | @media only screen and (max-width: 360px) and (min-width: 321px){ 22 | html{font-size: 22.5px} 23 | } 24 | @media only screen and (max-width: 375px) and (min-width: 361px){ 25 | html{font-size: 23.4px} 26 | } 27 | @media only screen and (max-width: 390px) and (min-width: 376px){ 28 | html{font-size: 24px;} 29 | } 30 | @media only screen and (max-width: 416px) and (min-width: 391px){/*iphone6 plus*/ 31 | html{font-size: 25.9px;} 32 | } 33 | 34 | /**/ 35 | body{font:normal 18px/1.5 'helvetica neue','hiragino sans gb','Microsoft YaHei',arial,tahoma,sans-serif;background-color: #fff;} 36 | a { 37 | -webkit-touch-callout: none; 38 | -webkit-user-select: none; 39 | -moz-user-select: moz-none; 40 | -ms-user-select: none; 41 | user-select: none; 42 | } 43 | a,button,input,textarea{-webkit-tap-highlight-color:rgba(255,0,0,0);} 44 | 45 | *{ 46 | padding: 0; 47 | margin: 0; 48 | } 49 | html,body{ 50 | width: 100%; 51 | /*height: 100%;*/ 52 | overflow-x: hidden; 53 | background-color: #f4f4f4; 54 | } 55 | 56 | body{ 57 | padding-bottom: 50px; 58 | font-size: 0; 59 | } 60 | 61 | .Button{ 62 | text-align: center; 63 | display: inline-block; 64 | width: 150px; 65 | height: 55px; 66 | background:rgba(243, 198, 35, .7); 67 | color: #844685; 68 | font-size: 18px; 69 | font-weight: 600; 70 | line-height: 55px; 71 | border-radius: 5px; 72 | margin: 10px 10px; 73 | cursor: pointer; 74 | &:hover { 75 | background:rgba(243, 198, 35, 1); 76 | } 77 | } 78 | 79 | .title{ 80 | text-align: center; 81 | color: #8566aa; 82 | font-size: 50px; 83 | line-height: 100px; 84 | margin: 10px 0; 85 | } 86 | .sub-title{ 87 | text-align: center; 88 | color: #8566aa; 89 | font-size: 20px; 90 | line-height: 40px; 91 | margin-bottom: 40px; 92 | padding: 0 20px; 93 | a{ 94 | color: rgba(243, 212, 105, .8); 95 | &:hover { 96 | color: rgba(255, 198, 0, 1); 97 | text-decoration: underline; 98 | } 99 | } 100 | } 101 | 102 | .left, .right{ 103 | display: inline-block; 104 | width: 50%; 105 | text-align: center; 106 | vertical-align: top; 107 | } 108 | 109 | .left { 110 | &__title{ 111 | font-size: 26px; 112 | color: #8566aa; 113 | margin-bottom: 20px; 114 | } 115 | &__img{ 116 | display: inline-block; 117 | border: 2px solid #8566aa; 118 | width: 60%; 119 | max-width: 400px; 120 | padding: 5px; 121 | box-sizing: border-box; 122 | border-radius: 5px; 123 | 124 | .origin{ 125 | width: 100%; 126 | } 127 | .result{ 128 | width: 100%; 129 | } 130 | } 131 | 132 | &__btns{ 133 | margin-top: 20px; 134 | display: inline-block; 135 | width: 70%; 136 | } 137 | } 138 | 139 | .dialog{ 140 | position: absolute; 141 | left: 0; 142 | top: 0; 143 | width: 100%; 144 | height: 100%; 145 | background: rgba(0, 0, 0, 0.3); 146 | text-align: left; 147 | display: none; 148 | } 149 | .dialog h3{ 150 | color:#8566aa; 151 | text-align: left; 152 | font-size: 20px; 153 | } 154 | .dialog>.window{ 155 | display: inline-block; 156 | position: absolute; 157 | padding: 20px 50px; 158 | left: 50%; 159 | top: 50%; 160 | transform: translate(-50%,-50%); 161 | -webkit-transform: translate(-50%,-50%); 162 | background: white; 163 | max-width: 400px; 164 | text-align: center; 165 | width: 70%; 166 | border-radius: 5px; 167 | } 168 | .window>.Button{ 169 | width: 40%; 170 | min-width: 40px; 171 | } 172 | .params{ 173 | text-align: left; 174 | margin: 20px 0; 175 | font-size: 20px; 176 | line-height: 34px; 177 | li { 178 | position: relative; 179 | white-space: nowrap; 180 | } 181 | .input{ 182 | position: absolute; 183 | border: 2px solid #eee; 184 | border-radius: 5px; 185 | right: 60px; 186 | width: 100px; 187 | height: 70%; 188 | box-sizing: border-box; 189 | padding: 12px; 190 | text-align: center; 191 | font-size: 16px; 192 | &.focus{ 193 | border: 2px solid #FF9999; 194 | } 195 | } 196 | 197 | .select{ 198 | position: absolute; 199 | right: 60px; 200 | border-radius: 5px; 201 | padding: 5px 20px; 202 | box-sizing: border-box; 203 | background: white; 204 | border: 2px solid #eee; 205 | font-size: 16px; 206 | } 207 | } 208 | 209 | .right{ 210 | &__crop{ 211 | text-align: left; 212 | &__title{ 213 | font-size: 26px; 214 | color: #8566aa; 215 | margin-bottom: 20px; 216 | } 217 | &__img { 218 | vertical-align: middle; 219 | display: inline-block; 220 | width: 200px; 221 | height: 200px; 222 | margin-right: 30px; 223 | border: 2px solid #8566aa; 224 | border-radius: 5px; 225 | background-size: contain; 226 | background-position: center; 227 | background-repeat: no-repeat; 228 | background-image: url('http://mtapplet.meitudata.com/596c72073971d86b5128.jpg'); 229 | } 230 | } 231 | 232 | &__filter{ 233 | text-align: left; 234 | width: 80%; 235 | &__title{ 236 | font-size: 26px; 237 | color: #8566aa; 238 | margin-bottom: 20px; 239 | margin-top: 40px; 240 | } 241 | &__list{ 242 | white-space: nowrap; 243 | overflow-x: scroll; 244 | } 245 | &__btn{ 246 | display: inline-block; 247 | font-size: 20px; 248 | line-height: 40px; 249 | color: #8566aa; 250 | text-align: center; 251 | padding: 0 20px; 252 | border-radius: 5px; 253 | border: 2px solid #8566aa; 254 | margin-top: 10px; 255 | cursor: pointer; 256 | &:hover { 257 | color: white; 258 | background-color: #8566aa; 259 | } 260 | } 261 | &__img{ 262 | text-align: center; 263 | display: inline-block; 264 | width: 250px; 265 | height: 360px; 266 | border: 2px solid #8566aa; 267 | border-radius: 5px; 268 | background-size: 90% auto; 269 | background-position: center bottom; 270 | background-repeat: no-repeat; 271 | background-image: url('http://mtapplet.meitudata.com/596c72073971d86b5128.jpg'); 272 | margin: 0 20px 0 0; 273 | } 274 | 275 | } 276 | } -------------------------------------------------------------------------------- /example/node.ts: -------------------------------------------------------------------------------- 1 | import { MImage, MCanvas } from '@Src/index' 2 | const path = require('path'); 3 | 4 | (async () => { 5 | // const imgSrc = path.resolve(__dirname, './images/1.jpg') 6 | // const mc = new MImage(imgSrc) 7 | // const b64 = await mc.crop({ 8 | // x: 100, 9 | // y: 100, 10 | // width: 300, 11 | // height: 300, 12 | // radius: 0, 13 | // }).filter('blur').draw() 14 | // console.log(b64) 15 | 16 | const ear = path.resolve(__dirname, './images/ear.png') 17 | const water = path.resolve(__dirname, './images/watermark.jpg') 18 | const mc = new MCanvas() 19 | mc.background('http://mtapplet.meitudata.com/596c72073971d86b5128.jpg', { 20 | type: 'origin', 21 | }) 22 | mc.add(ear, { 23 | width: 482, 24 | // crop: { 25 | // x: 192, 26 | // y: 84, 27 | // width: 365, 28 | // height: 365, 29 | // radius: '50%', 30 | // }, 31 | pos: { 32 | x: 150, 33 | y: 50, 34 | scale: 1, 35 | rotate: 0, 36 | }, 37 | }) 38 | mc.watermark(water, { 39 | width: '40%', 40 | pos: 'rightBottom', 41 | }) 42 | mc.rect({ 43 | x: 0, 44 | y: 'bottom:0', 45 | width: '100%', 46 | height: 300, 47 | radius: 30, 48 | strokeWidth : 5, 49 | strokeColor: '#996699', 50 | fillColor: 'rgba(0,0,0,.5)', 51 | }) 52 | mc.circle({ 53 | x: 'center', 54 | y: 'center', 55 | radius: 100, 56 | strokeWidth : 5, 57 | strokeColor: '#996699', 58 | fillColor: 'rgba(0,0,0,.5)', 59 | }) 60 | mc.text('Large/Stroke
      Normal/Gradient
      Small/Shadow', { 61 | width: '600', 62 | align: 'center', 63 | largeStyle: { 64 | color: 'red', 65 | font: '90px Microsoft YaHei,sans-serif', 66 | type: 'stroke', 67 | lineWidth: 2, 68 | lineHeight : 90, 69 | }, 70 | normalStyle: { 71 | color: 'blue', 72 | font: '70px Microsoft YaHei,sans-serif', 73 | lineHeight : 70, 74 | // shadow:{ 75 | // color: 'red', 76 | // blur: 4, 77 | // offsetX: 2, 78 | // offsetY: 2, 79 | // }, 80 | gradient: { 81 | type: 2, // 1: 横向渐变; 2: 纵向渐变; 82 | colorStop: ['red', 'blue'], 83 | }, 84 | }, 85 | smallStyle: { 86 | color: 'yellow', 87 | font: '50px Microsoft YaHei,sans-serif', 88 | lineHeight : 50, 89 | shadow: { 90 | color: 'red', 91 | blur: 10, 92 | offsetX: 5, 93 | offsetY: 5, 94 | }, 95 | }, 96 | pos: { 97 | x: 'center', 98 | y: 'bottom', 99 | rotate: 0, 100 | }, 101 | }) 102 | const b64 = await mc.draw() 103 | console.log(b64) 104 | })() -------------------------------------------------------------------------------- /example/type.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.svg' 2 | declare module '*.png' 3 | declare module '*.jpg' 4 | declare module '*.jpeg' 5 | declare module '*.gif' 6 | declare module '*.bmp' 7 | declare module '*.tiff' -------------------------------------------------------------------------------- /example/web.ts: -------------------------------------------------------------------------------- 1 | import { MCanvas, MImage } from '../src/index' 2 | import ear from './images/ear.png' 3 | import watermark from './images/watermark.jpg' 4 | import imgTest from './images/1.jpg' 5 | import './main.scss' 6 | 7 | // (async () => { 8 | // const mc = new MImage('http://mtapplet.meitudata.com/596c72073971d86b5128.jpg') 9 | // const b64 = await mc.crop({ 10 | // width: 300, 11 | // height: 300, 12 | // x: 0, 13 | // y: 0, 14 | // }).filter('blur').compress({ 15 | // width: 100, 16 | // height: 100, 17 | // }).draw() 18 | // $('.js-result').attr('src', b64) 19 | // })() 20 | 21 | let $sure = $('.js-sure') 22 | let $params = $('.js-params') 23 | let $dialog = $('.js-dialog') 24 | let $result = $('.js-result') 25 | let $cancel = $('.js-cancel') 26 | let $clear = $('.js-clear') 27 | 28 | let data = { 29 | addImageOps : { 30 | image: ear, 31 | options: { 32 | width: 482, 33 | // crop: { 34 | // x: 192, 35 | // y: 84, 36 | // width: 365, 37 | // height: 365, 38 | // radius: '50%', 39 | // }, 40 | pos: { 41 | x: 150, 42 | y: 50, 43 | scale: 1, 44 | rotate: 0, 45 | }, 46 | }, 47 | }, 48 | addWmOps : { 49 | image: watermark, 50 | options: { 51 | width: '40%', 52 | pos: 'rightBottom', 53 | }, 54 | }, 55 | addRectOps: { 56 | x: 0, 57 | y: 'bottom:0', 58 | width: '100%', 59 | height: 300, 60 | radius: 30, 61 | strokeWidth : 5, 62 | strokeColor: '#996699', 63 | fillColor: 'rgba(0,0,0,.5)', 64 | }, 65 | addCircleOps: { 66 | x: 'center', 67 | y: 'center', 68 | r: 100, 69 | strokeWidth : 5, 70 | strokeColor: '#996699', 71 | fillColor: 'rgba(0,0,0,.5)', 72 | }, 73 | addTextOps : { 74 | // text:'ABBBBBMCanvas.js', 75 | text: 'Large/Stroke
      Normal/Gradient
      Small/Shadow', 76 | options: { 77 | width: '600', 78 | align: 'center', 79 | largeStyle: { 80 | color: 'red', 81 | font: '90px Microsoft YaHei,sans-serif', 82 | type: 'stroke', 83 | lineWidth: 2, 84 | lineHeight : 90, 85 | }, 86 | normalStyle: { 87 | color: 'blue', 88 | font: '70px Microsoft YaHei,sans-serif', 89 | lineHeight : 70, 90 | // shadow:{ 91 | // color: 'red', 92 | // blur: 4, 93 | // offsetX: 2, 94 | // offsetY: 2, 95 | // }, 96 | gradient: { 97 | type: 2, // 1: 横向渐变; 2: 纵向渐变; 98 | colorStop: ['red', 'blue'], 99 | }, 100 | }, 101 | smallStyle: { 102 | color: 'yellow', 103 | font: '50px Microsoft YaHei,sans-serif', 104 | lineHeight : 50, 105 | shadow: { 106 | color: 'red', 107 | blur: 10, 108 | offsetX: 5, 109 | offsetY: 5, 110 | }, 111 | }, 112 | pos: { 113 | x: 'center', 114 | y: 'bottom', 115 | rotate: 0, 116 | }, 117 | }, 118 | }, 119 | crop: { 120 | x: 'center', 121 | y: 'center', 122 | width: 300, 123 | height: 300, 124 | radius: 150, 125 | } 126 | } 127 | let mc = new MCanvas({ 128 | width: 1000, 129 | height: 1500, 130 | // backgroundColor: 'black', 131 | }) 132 | mc.background('http://mtapplet.meitudata.com/596c72073971d86b5128.jpg', { 133 | // mc.background('http://mtapplet.meitudata.com/59e8765b6492c541.jpg',{ 134 | type: 'origin', 135 | left: '50%', 136 | top: '50%', 137 | }) 138 | 139 | const mi = new MImage('http://mtapplet.meitudata.com/596c72073971d86b5128.jpg') 140 | 141 | let timer 142 | $('.Button').on('touchstart', function(this: any) { 143 | $(this).addClass('taped') 144 | timer = setTimeout(() => { 145 | $(this).removeClass('taped') 146 | }, 2000) 147 | }) 148 | $('.Button').on('touchend', function(this: any) { 149 | $(this).removeClass('taped') 150 | clearTimeout(timer) 151 | }) 152 | 153 | $cancel.on('click', () => { 154 | $dialog.hide() 155 | }) 156 | 157 | $clear.on('click', () => { 158 | // mc.clear(); 159 | mc.background().clear().draw(b64 => { 160 | $result.attr('src', b64) 161 | }) 162 | }) 163 | 164 | $('.js-addImage').on('click', () => { 165 | showDialog(`image`, data.addImageOps) 166 | }) 167 | 168 | $('.js-addWm').on('click', () => { 169 | showDialog(`watermark`, data.addWmOps) 170 | }) 171 | 172 | $('.js-addText').on('click', () => { 173 | showDialog(`text`, data.addTextOps) 174 | }) 175 | 176 | $('.js-addRect').on('click', () => { 177 | mcDraw(data.addRectOps, 'rect') 178 | }) 179 | 180 | $('.js-addCircle').on('click', () => { 181 | mcDraw(data.addCircleOps, 'circle') 182 | }) 183 | 184 | $sure.on('click', function(this: any) { 185 | let ops = $(this).data('ops') 186 | let type = $(this).data('type') 187 | 188 | mcDraw(ops, type) 189 | }) 190 | 191 | function mcDraw(ops, type) { 192 | let img 193 | switch (type) { 194 | case `image`: 195 | img = new Image() 196 | img.crossOrigin = '*' 197 | img.onload = async () => { 198 | const b64 = await mc.add(img, ops.options).draw({ 199 | type: 'jpg', 200 | quality: .9, 201 | }) 202 | $result.attr('src', b64) 203 | $dialog.hide() 204 | } 205 | img.src = ops.image 206 | break 207 | case `watermark`: 208 | mc.watermark(ops.image, ops.options).draw(b64 => { 209 | $result.attr('src', b64) 210 | $dialog.hide() 211 | }) 212 | break 213 | case `text`: 214 | mc.text(ops.text, ops.options).draw(b64 => { 215 | $result.attr('src', b64) 216 | $dialog.hide() 217 | }) 218 | break 219 | case `rect`: 220 | mc.rect(ops).draw(b64 => { 221 | $result.attr('src', b64) 222 | $dialog.hide() 223 | }) 224 | break 225 | case `circle`: 226 | mc.circle(ops).draw(b64 => { 227 | $result.attr('src', b64) 228 | $dialog.hide() 229 | }) 230 | break 231 | case `crop`: 232 | mi.crop(ops).draw({ 233 | type: 'png', 234 | success(b64) { 235 | $('.js-cropResult').css('background-image', `url(${b64})`) 236 | $dialog.hide() 237 | } 238 | }) 239 | break 240 | default: 241 | } 242 | 243 | } 244 | 245 | function showDialog(type, ops) { 246 | let tab = `    ` 247 | let html 248 | switch (type) { 249 | case 'image': 250 | html = `
    • options: {
    • 251 |
    • ${tab + tab}width:
    • 252 |
    • ${tab + tab}pos: {
    • 253 |
    • ${tab + tab + tab + tab}x:
    • 254 |
    • ${tab + tab + tab + tab}y:
    • 255 |
    • ${tab + tab + tab + tab}scale:
    • 256 |
    • ${tab + tab + tab + tab}rotate:
    • 257 |
    • ${tab + tab}}
    • 258 |
    • }
    • ` 259 | break 260 | case 'watermark': 261 | html = `
    • options: {
    • 262 |
    • ${tab + tab}width:
    • 263 |
    • ${tab + tab}pos: 264 | 270 |
    • }
    • ` 271 | break 272 | case 'text': 273 | html = `
    • text: ''<b>Large/Stroke</b>Normal/Gradient<s>Small/Shadow</s>''
    • 274 |
    • options:{
    • 275 |
    • ${tab + tab}width:
    • 276 |
    • ${tab + tab}align: 277 | 282 |
    • 283 |
    • ${tab + tab}pos:{ 284 |
    • ${tab + tab + tab + tab}x:
    • 285 |
    • ${tab + tab + tab + tab}y:
    • 286 |
    • ${tab + tab}}
    • 287 |
    • }
    • ` 288 | break 289 | case 'crop': 290 | html = `
    • options: {
    • 291 |
    • ${tab + tab}width:
    • 292 |
    • ${tab + tab}height:
    • 293 |
    • ${tab + tab}x:
    • 294 |
    • ${tab + tab}y:
    • 295 |
    • ${tab + tab}radius:
    • 296 |
    • }
    • ` 297 | break 298 | default: 299 | } 300 | $params.html(html) 301 | $sure.data('ops', JSON.stringify(ops)).data('type', type) 302 | $dialog.show() 303 | } 304 | 305 | $(window).on('input', '.js-input', function(this: any) { 306 | let $this = $(this) 307 | let v = $this.val() 308 | let type = $this.data('type') 309 | let ops = $sure.data('ops') 310 | const cls = $this.data('class') 311 | 312 | if (cls === 'crop') { 313 | switch (type) { 314 | case 'width': 315 | ops.width = v 316 | break 317 | case 'height': 318 | ops.height = v 319 | break 320 | case 'x': 321 | ops.x = v 322 | break 323 | case 'y': 324 | ops.y = v 325 | break 326 | case 'radius': 327 | ops.radius = v 328 | break 329 | default: 330 | break 331 | } 332 | } else { 333 | switch (type) { 334 | case 'width': 335 | ops.options.width = v 336 | break 337 | case 'x': 338 | ops.options.pos.x = v 339 | break 340 | case 'y': 341 | ops.options.pos.y = v 342 | break 343 | case 'scale': 344 | ops.options.pos.scale = v 345 | break 346 | case 'rotate': 347 | ops.options.pos.rotate = v 348 | break 349 | case 'align': 350 | ops.options.align = v 351 | break 352 | default: 353 | } 354 | } 355 | 356 | $sure.data('ops', JSON.stringify(ops)) 357 | }) 358 | 359 | $(window).on('focus', '.js-input', function(this: any) { 360 | $(this).addClass('focus') 361 | }) 362 | $(window).on('blur', '.js-input', function(this: any) { 363 | $(this).removeClass('focus') 364 | }) 365 | 366 | $(window).on('change', '.js-select', function(this: any) { 367 | let ops = $sure.data('ops') 368 | let type = $(this).data('type') 369 | ops.options[type] = $(this).val() 370 | $sure.data('ops', JSON.stringify(ops)) 371 | }) 372 | 373 | $('.js-cropImage').on('click', () => { 374 | showDialog(`crop`, data.crop) 375 | }) 376 | 377 | $('.js-filter').on('click', function(this: any) { 378 | const $btn = $(this) 379 | const $par = $btn.parent() 380 | const type = $btn.data('type') 381 | mi.filter(type).draw(b64 => { 382 | $par.css('background-image', `url(${b64})`) 383 | }) 384 | }) 385 | 386 | -------------------------------------------------------------------------------- /example/zepto.min.js: -------------------------------------------------------------------------------- 1 | /* Zepto v1.2.0 - zepto event ajax form ie - zeptojs.com/license */ 2 | !function(t,e){"function"==typeof define&&define.amd?define(function(){return e(t)}):e(t)}(this,function(t){var e=function(){function $(t){return null==t?String(t):S[C.call(t)]||"object"}function F(t){return"function"==$(t)}function k(t){return null!=t&&t==t.window}function M(t){return null!=t&&t.nodeType==t.DOCUMENT_NODE}function R(t){return"object"==$(t)}function Z(t){return R(t)&&!k(t)&&Object.getPrototypeOf(t)==Object.prototype}function z(t){var e=!!t&&"length"in t&&t.length,n=r.type(t);return"function"!=n&&!k(t)&&("array"==n||0===e||"number"==typeof e&&e>0&&e-1 in t)}function q(t){return a.call(t,function(t){return null!=t})}function H(t){return t.length>0?r.fn.concat.apply([],t):t}function I(t){return t.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()}function V(t){return t in l?l[t]:l[t]=new RegExp("(^|\\s)"+t+"(\\s|$)")}function _(t,e){return"number"!=typeof e||h[I(t)]?e:e+"px"}function B(t){var e,n;return c[t]||(e=f.createElement(t),f.body.appendChild(e),n=getComputedStyle(e,"").getPropertyValue("display"),e.parentNode.removeChild(e),"none"==n&&(n="block"),c[t]=n),c[t]}function U(t){return"children"in t?u.call(t.children):r.map(t.childNodes,function(t){return 1==t.nodeType?t:void 0})}function X(t,e){var n,r=t?t.length:0;for(n=0;r>n;n++)this[n]=t[n];this.length=r,this.selector=e||""}function J(t,r,i){for(n in r)i&&(Z(r[n])||L(r[n]))?(Z(r[n])&&!Z(t[n])&&(t[n]={}),L(r[n])&&!L(t[n])&&(t[n]=[]),J(t[n],r[n],i)):r[n]!==e&&(t[n]=r[n])}function W(t,e){return null==e?r(t):r(t).filter(e)}function Y(t,e,n,r){return F(e)?e.call(t,n,r):e}function G(t,e,n){null==n?t.removeAttribute(e):t.setAttribute(e,n)}function K(t,n){var r=t.className||"",i=r&&r.baseVal!==e;return n===e?i?r.baseVal:r:void(i?r.baseVal=n:t.className=n)}function Q(t){try{return t?"true"==t||("false"==t?!1:"null"==t?null:+t+""==t?+t:/^[\[\{]/.test(t)?r.parseJSON(t):t):t}catch(e){return t}}function tt(t,e){e(t);for(var n=0,r=t.childNodes.length;r>n;n++)tt(t.childNodes[n],e)}var e,n,r,i,O,P,o=[],s=o.concat,a=o.filter,u=o.slice,f=t.document,c={},l={},h={"column-count":1,columns:1,"font-weight":1,"line-height":1,opacity:1,"z-index":1,zoom:1},p=/^\s*<(\w+|!)[^>]*>/,d=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,m=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,g=/^(?:body|html)$/i,v=/([A-Z])/g,y=["val","css","html","text","data","width","height","offset"],x=["after","prepend","before","append"],b=f.createElement("table"),E=f.createElement("tr"),j={tr:f.createElement("tbody"),tbody:b,thead:b,tfoot:b,td:E,th:E,"*":f.createElement("div")},w=/complete|loaded|interactive/,T=/^[\w-]*$/,S={},C=S.toString,N={},A=f.createElement("div"),D={tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},L=Array.isArray||function(t){return t instanceof Array};return N.matches=function(t,e){if(!e||!t||1!==t.nodeType)return!1;var n=t.matches||t.webkitMatchesSelector||t.mozMatchesSelector||t.oMatchesSelector||t.matchesSelector;if(n)return n.call(t,e);var r,i=t.parentNode,o=!i;return o&&(i=A).appendChild(t),r=~N.qsa(i,e).indexOf(t),o&&A.removeChild(t),r},O=function(t){return t.replace(/-+(.)?/g,function(t,e){return e?e.toUpperCase():""})},P=function(t){return a.call(t,function(e,n){return t.indexOf(e)==n})},N.fragment=function(t,n,i){var o,s,a;return d.test(t)&&(o=r(f.createElement(RegExp.$1))),o||(t.replace&&(t=t.replace(m,"<$1>")),n===e&&(n=p.test(t)&&RegExp.$1),n in j||(n="*"),a=j[n],a.innerHTML=""+t,o=r.each(u.call(a.childNodes),function(){a.removeChild(this)})),Z(i)&&(s=r(o),r.each(i,function(t,e){y.indexOf(t)>-1?s[t](e):s.attr(t,e)})),o},N.Z=function(t,e){return new X(t,e)},N.isZ=function(t){return t instanceof N.Z},N.init=function(t,n){var i;if(!t)return N.Z();if("string"==typeof t)if(t=t.trim(),"<"==t[0]&&p.test(t))i=N.fragment(t,RegExp.$1,n),t=null;else{if(n!==e)return r(n).find(t);i=N.qsa(f,t)}else{if(F(t))return r(f).ready(t);if(N.isZ(t))return t;if(L(t))i=q(t);else if(R(t))i=[t],t=null;else if(p.test(t))i=N.fragment(t.trim(),RegExp.$1,n),t=null;else{if(n!==e)return r(n).find(t);i=N.qsa(f,t)}}return N.Z(i,t)},r=function(t,e){return N.init(t,e)},r.extend=function(t){var e,n=u.call(arguments,1);return"boolean"==typeof t&&(e=t,t=n.shift()),n.forEach(function(n){J(t,n,e)}),t},N.qsa=function(t,e){var n,r="#"==e[0],i=!r&&"."==e[0],o=r||i?e.slice(1):e,s=T.test(o);return t.getElementById&&s&&r?(n=t.getElementById(o))?[n]:[]:1!==t.nodeType&&9!==t.nodeType&&11!==t.nodeType?[]:u.call(s&&!r&&t.getElementsByClassName?i?t.getElementsByClassName(o):t.getElementsByTagName(e):t.querySelectorAll(e))},r.contains=f.documentElement.contains?function(t,e){return t!==e&&t.contains(e)}:function(t,e){for(;e&&(e=e.parentNode);)if(e===t)return!0;return!1},r.type=$,r.isFunction=F,r.isWindow=k,r.isArray=L,r.isPlainObject=Z,r.isEmptyObject=function(t){var e;for(e in t)return!1;return!0},r.isNumeric=function(t){var e=Number(t),n=typeof t;return null!=t&&"boolean"!=n&&("string"!=n||t.length)&&!isNaN(e)&&isFinite(e)||!1},r.inArray=function(t,e,n){return o.indexOf.call(e,t,n)},r.camelCase=O,r.trim=function(t){return null==t?"":String.prototype.trim.call(t)},r.uuid=0,r.support={},r.expr={},r.noop=function(){},r.map=function(t,e){var n,i,o,r=[];if(z(t))for(i=0;i=0?t:t+this.length]},toArray:function(){return this.get()},size:function(){return this.length},remove:function(){return this.each(function(){null!=this.parentNode&&this.parentNode.removeChild(this)})},each:function(t){return o.every.call(this,function(e,n){return t.call(e,n,e)!==!1}),this},filter:function(t){return F(t)?this.not(this.not(t)):r(a.call(this,function(e){return N.matches(e,t)}))},add:function(t,e){return r(P(this.concat(r(t,e))))},is:function(t){return this.length>0&&N.matches(this[0],t)},not:function(t){var n=[];if(F(t)&&t.call!==e)this.each(function(e){t.call(this,e)||n.push(this)});else{var i="string"==typeof t?this.filter(t):z(t)&&F(t.item)?u.call(t):r(t);this.forEach(function(t){i.indexOf(t)<0&&n.push(t)})}return r(n)},has:function(t){return this.filter(function(){return R(t)?r.contains(this,t):r(this).find(t).size()})},eq:function(t){return-1===t?this.slice(t):this.slice(t,+t+1)},first:function(){var t=this[0];return t&&!R(t)?t:r(t)},last:function(){var t=this[this.length-1];return t&&!R(t)?t:r(t)},find:function(t){var e,n=this;return e=t?"object"==typeof t?r(t).filter(function(){var t=this;return o.some.call(n,function(e){return r.contains(e,t)})}):1==this.length?r(N.qsa(this[0],t)):this.map(function(){return N.qsa(this,t)}):r()},closest:function(t,e){var n=[],i="object"==typeof t&&r(t);return this.each(function(r,o){for(;o&&!(i?i.indexOf(o)>=0:N.matches(o,t));)o=o!==e&&!M(o)&&o.parentNode;o&&n.indexOf(o)<0&&n.push(o)}),r(n)},parents:function(t){for(var e=[],n=this;n.length>0;)n=r.map(n,function(t){return(t=t.parentNode)&&!M(t)&&e.indexOf(t)<0?(e.push(t),t):void 0});return W(e,t)},parent:function(t){return W(P(this.pluck("parentNode")),t)},children:function(t){return W(this.map(function(){return U(this)}),t)},contents:function(){return this.map(function(){return this.contentDocument||u.call(this.childNodes)})},siblings:function(t){return W(this.map(function(t,e){return a.call(U(e.parentNode),function(t){return t!==e})}),t)},empty:function(){return this.each(function(){this.innerHTML=""})},pluck:function(t){return r.map(this,function(e){return e[t]})},show:function(){return this.each(function(){"none"==this.style.display&&(this.style.display=""),"none"==getComputedStyle(this,"").getPropertyValue("display")&&(this.style.display=B(this.nodeName))})},replaceWith:function(t){return this.before(t).remove()},wrap:function(t){var e=F(t);if(this[0]&&!e)var n=r(t).get(0),i=n.parentNode||this.length>1;return this.each(function(o){r(this).wrapAll(e?t.call(this,o):i?n.cloneNode(!0):n)})},wrapAll:function(t){if(this[0]){r(this[0]).before(t=r(t));for(var e;(e=t.children()).length;)t=e.first();r(t).append(this)}return this},wrapInner:function(t){var e=F(t);return this.each(function(n){var i=r(this),o=i.contents(),s=e?t.call(this,n):t;o.length?o.wrapAll(s):i.append(s)})},unwrap:function(){return this.parent().each(function(){r(this).replaceWith(r(this).children())}),this},clone:function(){return this.map(function(){return this.cloneNode(!0)})},hide:function(){return this.css("display","none")},toggle:function(t){return this.each(function(){var n=r(this);(t===e?"none"==n.css("display"):t)?n.show():n.hide()})},prev:function(t){return r(this.pluck("previousElementSibling")).filter(t||"*")},next:function(t){return r(this.pluck("nextElementSibling")).filter(t||"*")},html:function(t){return 0 in arguments?this.each(function(e){var n=this.innerHTML;r(this).empty().append(Y(this,t,e,n))}):0 in this?this[0].innerHTML:null},text:function(t){return 0 in arguments?this.each(function(e){var n=Y(this,t,e,this.textContent);this.textContent=null==n?"":""+n}):0 in this?this.pluck("textContent").join(""):null},attr:function(t,r){var i;return"string"!=typeof t||1 in arguments?this.each(function(e){if(1===this.nodeType)if(R(t))for(n in t)G(this,n,t[n]);else G(this,t,Y(this,r,e,this.getAttribute(t)))}):0 in this&&1==this[0].nodeType&&null!=(i=this[0].getAttribute(t))?i:e},removeAttr:function(t){return this.each(function(){1===this.nodeType&&t.split(" ").forEach(function(t){G(this,t)},this)})},prop:function(t,e){return t=D[t]||t,1 in arguments?this.each(function(n){this[t]=Y(this,e,n,this[t])}):this[0]&&this[0][t]},removeProp:function(t){return t=D[t]||t,this.each(function(){delete this[t]})},data:function(t,n){var r="data-"+t.replace(v,"-$1").toLowerCase(),i=1 in arguments?this.attr(r,n):this.attr(r);return null!==i?Q(i):e},val:function(t){return 0 in arguments?(null==t&&(t=""),this.each(function(e){this.value=Y(this,t,e,this.value)})):this[0]&&(this[0].multiple?r(this[0]).find("option").filter(function(){return this.selected}).pluck("value"):this[0].value)},offset:function(e){if(e)return this.each(function(t){var n=r(this),i=Y(this,e,t,n.offset()),o=n.offsetParent().offset(),s={top:i.top-o.top,left:i.left-o.left};"static"==n.css("position")&&(s.position="relative"),n.css(s)});if(!this.length)return null;if(f.documentElement!==this[0]&&!r.contains(f.documentElement,this[0]))return{top:0,left:0};var n=this[0].getBoundingClientRect();return{left:n.left+t.pageXOffset,top:n.top+t.pageYOffset,width:Math.round(n.width),height:Math.round(n.height)}},css:function(t,e){if(arguments.length<2){var i=this[0];if("string"==typeof t){if(!i)return;return i.style[O(t)]||getComputedStyle(i,"").getPropertyValue(t)}if(L(t)){if(!i)return;var o={},s=getComputedStyle(i,"");return r.each(t,function(t,e){o[e]=i.style[O(e)]||s.getPropertyValue(e)}),o}}var a="";if("string"==$(t))e||0===e?a=I(t)+":"+_(t,e):this.each(function(){this.style.removeProperty(I(t))});else for(n in t)t[n]||0===t[n]?a+=I(n)+":"+_(n,t[n])+";":this.each(function(){this.style.removeProperty(I(n))});return this.each(function(){this.style.cssText+=";"+a})},index:function(t){return t?this.indexOf(r(t)[0]):this.parent().children().indexOf(this[0])},hasClass:function(t){return t?o.some.call(this,function(t){return this.test(K(t))},V(t)):!1},addClass:function(t){return t?this.each(function(e){if("className"in this){i=[];var n=K(this),o=Y(this,t,e,n);o.split(/\s+/g).forEach(function(t){r(this).hasClass(t)||i.push(t)},this),i.length&&K(this,n+(n?" ":"")+i.join(" "))}}):this},removeClass:function(t){return this.each(function(n){if("className"in this){if(t===e)return K(this,"");i=K(this),Y(this,t,n,i).split(/\s+/g).forEach(function(t){i=i.replace(V(t)," ")}),K(this,i.trim())}})},toggleClass:function(t,n){return t?this.each(function(i){var o=r(this),s=Y(this,t,i,K(this));s.split(/\s+/g).forEach(function(t){(n===e?!o.hasClass(t):n)?o.addClass(t):o.removeClass(t)})}):this},scrollTop:function(t){if(this.length){var n="scrollTop"in this[0];return t===e?n?this[0].scrollTop:this[0].pageYOffset:this.each(n?function(){this.scrollTop=t}:function(){this.scrollTo(this.scrollX,t)})}},scrollLeft:function(t){if(this.length){var n="scrollLeft"in this[0];return t===e?n?this[0].scrollLeft:this[0].pageXOffset:this.each(n?function(){this.scrollLeft=t}:function(){this.scrollTo(t,this.scrollY)})}},position:function(){if(this.length){var t=this[0],e=this.offsetParent(),n=this.offset(),i=g.test(e[0].nodeName)?{top:0,left:0}:e.offset();return n.top-=parseFloat(r(t).css("margin-top"))||0,n.left-=parseFloat(r(t).css("margin-left"))||0,i.top+=parseFloat(r(e[0]).css("border-top-width"))||0,i.left+=parseFloat(r(e[0]).css("border-left-width"))||0,{top:n.top-i.top,left:n.left-i.left}}},offsetParent:function(){return this.map(function(){for(var t=this.offsetParent||f.body;t&&!g.test(t.nodeName)&&"static"==r(t).css("position");)t=t.offsetParent;return t})}},r.fn.detach=r.fn.remove,["width","height"].forEach(function(t){var n=t.replace(/./,function(t){return t[0].toUpperCase()});r.fn[t]=function(i){var o,s=this[0];return i===e?k(s)?s["inner"+n]:M(s)?s.documentElement["scroll"+n]:(o=this.offset())&&o[t]:this.each(function(e){s=r(this),s.css(t,Y(this,i,e,s[t]()))})}}),x.forEach(function(n,i){var o=i%2;r.fn[n]=function(){var n,a,s=r.map(arguments,function(t){var i=[];return n=$(t),"array"==n?(t.forEach(function(t){return t.nodeType!==e?i.push(t):r.zepto.isZ(t)?i=i.concat(t.get()):void(i=i.concat(N.fragment(t)))}),i):"object"==n||null==t?t:N.fragment(t)}),u=this.length>1;return s.length<1?this:this.each(function(e,n){a=o?n:n.parentNode,n=0==i?n.nextSibling:1==i?n.firstChild:2==i?n:null;var c=r.contains(f.documentElement,a);s.forEach(function(e){if(u)e=e.cloneNode(!0);else if(!a)return r(e).remove();a.insertBefore(e,n),c&&tt(e,function(e){if(!(null==e.nodeName||"SCRIPT"!==e.nodeName.toUpperCase()||e.type&&"text/javascript"!==e.type||e.src)){var n=e.ownerDocument?e.ownerDocument.defaultView:t;n.eval.call(n,e.innerHTML)}})})})},r.fn[o?n+"To":"insert"+(i?"Before":"After")]=function(t){return r(t)[n](this),this}}),N.Z.prototype=X.prototype=r.fn,N.uniq=P,N.deserializeValue=Q,r.zepto=N,r}();return t.Zepto=e,void 0===t.$&&(t.$=e),function(e){function h(t){return t._zid||(t._zid=n++)}function p(t,e,n,r){if(e=d(e),e.ns)var i=m(e.ns);return(a[h(t)]||[]).filter(function(t){return t&&(!e.e||t.e==e.e)&&(!e.ns||i.test(t.ns))&&(!n||h(t.fn)===h(n))&&(!r||t.sel==r)})}function d(t){var e=(""+t).split(".");return{e:e[0],ns:e.slice(1).sort().join(" ")}}function m(t){return new RegExp("(?:^| )"+t.replace(" "," .* ?")+"(?: |$)")}function g(t,e){return t.del&&!f&&t.e in c||!!e}function v(t){return l[t]||f&&c[t]||t}function y(t,n,i,o,s,u,f){var c=h(t),p=a[c]||(a[c]=[]);n.split(/\s/).forEach(function(n){if("ready"==n)return e(document).ready(i);var a=d(n);a.fn=i,a.sel=s,a.e in l&&(i=function(t){var n=t.relatedTarget;return!n||n!==this&&!e.contains(this,n)?a.fn.apply(this,arguments):void 0}),a.del=u;var c=u||i;a.proxy=function(e){if(e=T(e),!e.isImmediatePropagationStopped()){e.data=o;var n=c.apply(t,e._args==r?[e]:[e].concat(e._args));return n===!1&&(e.preventDefault(),e.stopPropagation()),n}},a.i=p.length,p.push(a),"addEventListener"in t&&t.addEventListener(v(a.e),a.proxy,g(a,f))})}function x(t,e,n,r,i){var o=h(t);(e||"").split(/\s/).forEach(function(e){p(t,e,n,r).forEach(function(e){delete a[o][e.i],"removeEventListener"in t&&t.removeEventListener(v(e.e),e.proxy,g(e,i))})})}function T(t,n){return(n||!t.isDefaultPrevented)&&(n||(n=t),e.each(w,function(e,r){var i=n[e];t[e]=function(){return this[r]=b,i&&i.apply(n,arguments)},t[r]=E}),t.timeStamp||(t.timeStamp=Date.now()),(n.defaultPrevented!==r?n.defaultPrevented:"returnValue"in n?n.returnValue===!1:n.getPreventDefault&&n.getPreventDefault())&&(t.isDefaultPrevented=b)),t}function S(t){var e,n={originalEvent:t};for(e in t)j.test(e)||t[e]===r||(n[e]=t[e]);return T(n,t)}var r,n=1,i=Array.prototype.slice,o=e.isFunction,s=function(t){return"string"==typeof t},a={},u={},f="onfocusin"in t,c={focus:"focusin",blur:"focusout"},l={mouseenter:"mouseover",mouseleave:"mouseout"};u.click=u.mousedown=u.mouseup=u.mousemove="MouseEvents",e.event={add:y,remove:x},e.proxy=function(t,n){var r=2 in arguments&&i.call(arguments,2);if(o(t)){var a=function(){return t.apply(n,r?r.concat(i.call(arguments)):arguments)};return a._zid=h(t),a}if(s(n))return r?(r.unshift(t[n],t),e.proxy.apply(null,r)):e.proxy(t[n],t);throw new TypeError("expected function")},e.fn.bind=function(t,e,n){return this.on(t,e,n)},e.fn.unbind=function(t,e){return this.off(t,e)},e.fn.one=function(t,e,n,r){return this.on(t,e,n,r,1)};var b=function(){return!0},E=function(){return!1},j=/^([A-Z]|returnValue$|layer[XY]$|webkitMovement[XY]$)/,w={preventDefault:"isDefaultPrevented",stopImmediatePropagation:"isImmediatePropagationStopped",stopPropagation:"isPropagationStopped"};e.fn.delegate=function(t,e,n){return this.on(e,t,n)},e.fn.undelegate=function(t,e,n){return this.off(e,t,n)},e.fn.live=function(t,n){return e(document.body).delegate(this.selector,t,n),this},e.fn.die=function(t,n){return e(document.body).undelegate(this.selector,t,n),this},e.fn.on=function(t,n,a,u,f){var c,l,h=this;return t&&!s(t)?(e.each(t,function(t,e){h.on(t,n,a,e,f)}),h):(s(n)||o(u)||u===!1||(u=a,a=n,n=r),(u===r||a===!1)&&(u=a,a=r),u===!1&&(u=E),h.each(function(r,o){f&&(c=function(t){return x(o,t.type,u),u.apply(this,arguments)}),n&&(l=function(t){var r,s=e(t.target).closest(n,o).get(0);return s&&s!==o?(r=e.extend(S(t),{currentTarget:s,liveFired:o}),(c||u).apply(s,[r].concat(i.call(arguments,1)))):void 0}),y(o,t,u,a,n,l||c)}))},e.fn.off=function(t,n,i){var a=this;return t&&!s(t)?(e.each(t,function(t,e){a.off(t,n,e)}),a):(s(n)||o(i)||i===!1||(i=n,n=r),i===!1&&(i=E),a.each(function(){x(this,t,i,n)}))},e.fn.trigger=function(t,n){return t=s(t)||e.isPlainObject(t)?e.Event(t):T(t),t._args=n,this.each(function(){t.type in c&&"function"==typeof this[t.type]?this[t.type]():"dispatchEvent"in this?this.dispatchEvent(t):e(this).triggerHandler(t,n)})},e.fn.triggerHandler=function(t,n){var r,i;return this.each(function(o,a){r=S(s(t)?e.Event(t):t),r._args=n,r.target=a,e.each(p(a,t.type||t),function(t,e){return i=e.proxy(r),r.isImmediatePropagationStopped()?!1:void 0})}),i},"focusin focusout focus blur load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select keydown keypress keyup error".split(" ").forEach(function(t){e.fn[t]=function(e){return 0 in arguments?this.bind(t,e):this.trigger(t)}}),e.Event=function(t,e){s(t)||(e=t,t=e.type);var n=document.createEvent(u[t]||"Events"),r=!0;if(e)for(var i in e)"bubbles"==i?r=!!e[i]:n[i]=e[i];return n.initEvent(t,r,!0),T(n)}}(e),function(e){function p(t,n,r){var i=e.Event(n);return e(t).trigger(i,r),!i.isDefaultPrevented()}function d(t,e,n,i){return t.global?p(e||r,n,i):void 0}function m(t){t.global&&0===e.active++&&d(t,null,"ajaxStart")}function g(t){t.global&&!--e.active&&d(t,null,"ajaxStop")}function v(t,e){var n=e.context;return e.beforeSend.call(n,t,e)===!1||d(e,n,"ajaxBeforeSend",[t,e])===!1?!1:void d(e,n,"ajaxSend",[t,e])}function y(t,e,n,r){var i=n.context,o="success";n.success.call(i,t,o,e),r&&r.resolveWith(i,[t,o,e]),d(n,i,"ajaxSuccess",[e,n,t]),b(o,e,n)}function x(t,e,n,r,i){var o=r.context;r.error.call(o,n,e,t),i&&i.rejectWith(o,[n,e,t]),d(r,o,"ajaxError",[n,r,t||e]),b(e,n,r)}function b(t,e,n){var r=n.context;n.complete.call(r,e,t),d(n,r,"ajaxComplete",[e,n]),g(n)}function E(t,e,n){if(n.dataFilter==j)return t;var r=n.context;return n.dataFilter.call(r,t,e)}function j(){}function w(t){return t&&(t=t.split(";",2)[0]),t&&(t==c?"html":t==f?"json":a.test(t)?"script":u.test(t)&&"xml")||"text"}function T(t,e){return""==e?t:(t+"&"+e).replace(/[&?]{1,2}/,"?")}function S(t){t.processData&&t.data&&"string"!=e.type(t.data)&&(t.data=e.param(t.data,t.traditional)),!t.data||t.type&&"GET"!=t.type.toUpperCase()&&"jsonp"!=t.dataType||(t.url=T(t.url,t.data),t.data=void 0)}function C(t,n,r,i){return e.isFunction(n)&&(i=r,r=n,n=void 0),e.isFunction(r)||(i=r,r=void 0),{url:t,data:n,success:r,dataType:i}}function O(t,n,r,i){var o,s=e.isArray(n),a=e.isPlainObject(n);e.each(n,function(n,u){o=e.type(u),i&&(n=r?i:i+"["+(a||"object"==o||"array"==o?n:"")+"]"),!i&&s?t.add(u.name,u.value):"array"==o||!r&&"object"==o?O(t,u,r,n):t.add(n,u)})}var i,o,n=+new Date,r=t.document,s=/)<[^<]*)*<\/script>/gi,a=/^(?:text|application)\/javascript/i,u=/^(?:text|application)\/xml/i,f="application/json",c="text/html",l=/^\s*$/,h=r.createElement("a");h.href=t.location.href,e.active=0,e.ajaxJSONP=function(i,o){if(!("type"in i))return e.ajax(i);var c,p,s=i.jsonpCallback,a=(e.isFunction(s)?s():s)||"Zepto"+n++,u=r.createElement("script"),f=t[a],l=function(t){e(u).triggerHandler("error",t||"abort")},h={abort:l};return o&&o.promise(h),e(u).on("load error",function(n,r){clearTimeout(p),e(u).off().remove(),"error"!=n.type&&c?y(c[0],h,i,o):x(null,r||"error",h,i,o),t[a]=f,c&&e.isFunction(f)&&f(c[0]),f=c=void 0}),v(h,i)===!1?(l("abort"),h):(t[a]=function(){c=arguments},u.src=i.url.replace(/\?(.+)=\?/,"?$1="+a),r.head.appendChild(u),i.timeout>0&&(p=setTimeout(function(){l("timeout")},i.timeout)),h)},e.ajaxSettings={type:"GET",beforeSend:j,success:j,error:j,complete:j,context:null,global:!0,xhr:function(){return new t.XMLHttpRequest},accepts:{script:"text/javascript, application/javascript, application/x-javascript",json:f,xml:"application/xml, text/xml",html:c,text:"text/plain"},crossDomain:!1,timeout:0,processData:!0,cache:!0,dataFilter:j},e.ajax=function(n){var u,f,s=e.extend({},n||{}),a=e.Deferred&&e.Deferred();for(i in e.ajaxSettings)void 0===s[i]&&(s[i]=e.ajaxSettings[i]);m(s),s.crossDomain||(u=r.createElement("a"),u.href=s.url,u.href=u.href,s.crossDomain=h.protocol+"//"+h.host!=u.protocol+"//"+u.host),s.url||(s.url=t.location.toString()),(f=s.url.indexOf("#"))>-1&&(s.url=s.url.slice(0,f)),S(s);var c=s.dataType,p=/\?.+=\?/.test(s.url);if(p&&(c="jsonp"),s.cache!==!1&&(n&&n.cache===!0||"script"!=c&&"jsonp"!=c)||(s.url=T(s.url,"_="+Date.now())),"jsonp"==c)return p||(s.url=T(s.url,s.jsonp?s.jsonp+"=?":s.jsonp===!1?"":"callback=?")),e.ajaxJSONP(s,a);var P,d=s.accepts[c],g={},b=function(t,e){g[t.toLowerCase()]=[t,e]},C=/^([\w-]+:)\/\//.test(s.url)?RegExp.$1:t.location.protocol,N=s.xhr(),O=N.setRequestHeader;if(a&&a.promise(N),s.crossDomain||b("X-Requested-With","XMLHttpRequest"),b("Accept",d||"*/*"),(d=s.mimeType||d)&&(d.indexOf(",")>-1&&(d=d.split(",",2)[0]),N.overrideMimeType&&N.overrideMimeType(d)),(s.contentType||s.contentType!==!1&&s.data&&"GET"!=s.type.toUpperCase())&&b("Content-Type",s.contentType||"application/x-www-form-urlencoded"),s.headers)for(o in s.headers)b(o,s.headers[o]);if(N.setRequestHeader=b,N.onreadystatechange=function(){if(4==N.readyState){N.onreadystatechange=j,clearTimeout(P);var t,n=!1;if(N.status>=200&&N.status<300||304==N.status||0==N.status&&"file:"==C){if(c=c||w(s.mimeType||N.getResponseHeader("content-type")),"arraybuffer"==N.responseType||"blob"==N.responseType)t=N.response;else{t=N.responseText;try{t=E(t,c,s),"script"==c?(1,eval)(t):"xml"==c?t=N.responseXML:"json"==c&&(t=l.test(t)?null:e.parseJSON(t))}catch(r){n=r}if(n)return x(n,"parsererror",N,s,a)}y(t,N,s,a)}else x(N.statusText||null,N.status?"error":"abort",N,s,a)}},v(N,s)===!1)return N.abort(),x(null,"abort",N,s,a),N;var A="async"in s?s.async:!0;if(N.open(s.type,s.url,A,s.username,s.password),s.xhrFields)for(o in s.xhrFields)N[o]=s.xhrFields[o];for(o in g)O.apply(N,g[o]);return s.timeout>0&&(P=setTimeout(function(){N.onreadystatechange=j,N.abort(),x(null,"timeout",N,s,a)},s.timeout)),N.send(s.data?s.data:null),N},e.get=function(){return e.ajax(C.apply(null,arguments))},e.post=function(){var t=C.apply(null,arguments);return t.type="POST",e.ajax(t)},e.getJSON=function(){var t=C.apply(null,arguments);return t.dataType="json",e.ajax(t)},e.fn.load=function(t,n,r){if(!this.length)return this;var a,i=this,o=t.split(/\s/),u=C(t,n,r),f=u.success;return o.length>1&&(u.url=o[0],a=o[1]),u.success=function(t){i.html(a?e("
      ").html(t.replace(s,"")).find(a):t),f&&f.apply(i,arguments)},e.ajax(u),this};var N=encodeURIComponent;e.param=function(t,n){var r=[];return r.add=function(t,n){e.isFunction(n)&&(n=n()),null==n&&(n=""),this.push(N(t)+"="+N(n))},O(r,t,n),r.join("&").replace(/%20/g,"+")}}(e),function(t){t.fn.serializeArray=function(){var e,n,r=[],i=function(t){return t.forEach?t.forEach(i):void r.push({name:e,value:t})};return this[0]&&t.each(this[0].elements,function(r,o){n=o.type,e=o.name,e&&"fieldset"!=o.nodeName.toLowerCase()&&!o.disabled&&"submit"!=n&&"reset"!=n&&"button"!=n&&"file"!=n&&("radio"!=n&&"checkbox"!=n||o.checked)&&i(t(o).val())}),r},t.fn.serialize=function(){var t=[];return this.serializeArray().forEach(function(e){t.push(encodeURIComponent(e.name)+"="+encodeURIComponent(e.value))}),t.join("&")},t.fn.submit=function(e){if(0 in arguments)this.bind("submit",e);else if(this.length){var n=t.Event("submit");this.eq(0).trigger(n),n.isDefaultPrevented()||this.get(0).submit()}return this}}(e),function(){try{getComputedStyle(void 0)}catch(e){var n=getComputedStyle;t.getComputedStyle=function(t,e){try{return n(t,e)}catch(r){return null}}}}(),e}); 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mcanvas", 3 | "description": "the image-composer or image-croper that can draw image/text/watermark or crop the image.", 4 | "version": "2.0.8", 5 | "keywords": [ 6 | "canvas", 7 | "image", 8 | "watermark", 9 | "image-merge", 10 | "image-compose", 11 | "image-text", 12 | "image-crop", 13 | "image-filter", 14 | "image-compress", 15 | "image-blur" 16 | ], 17 | "main": "dist/mcanvas.node.js", 18 | "broswer": "dist/mcanvas.web.js", 19 | "module": "dist/mcanvas.web.js", 20 | "moduleName": "MCanvas", 21 | "types": "src/lib.d.ts", 22 | "scripts": { 23 | "dev:web": "NODE_ENV=development ENV=web webpack-dev-server --config build/webpack.example.js", 24 | "dev:node": "NODE_ENV=development ENV=node webpack -w --config build/webpack.example.js", 25 | "build:web": "NODE_ENV=production ENV=web webpack -p --config build/webpack.build.js", 26 | "build:node": "NODE_ENV=production ENV=node webpack -p --config build/webpack.build.js", 27 | "build": "rm -rf dist && npm-run-all --parallel build:node build:web", 28 | "start": "npm run dev:web" 29 | }, 30 | "author": "gxd(159042708@qq.com)", 31 | "license": "MIT", 32 | "repository": { 33 | "url": "https://github.com/xd-tayde/mcanvas", 34 | "type": "git" 35 | }, 36 | "devDependencies": { 37 | "@types/zepto": "^1.0.30", 38 | "@typescript-eslint/eslint-plugin": "^2.33.0", 39 | "@typescript-eslint/parser": "^2.33.0", 40 | "babel-eslint": "^10.1.0", 41 | "babel-polyfill": "^6.26.0", 42 | "clean-webpack-plugin": "^3.0.0", 43 | "css-loader": "^3.5.1", 44 | "eslint": "^7.0.0", 45 | "file-loader": "^6.0.0", 46 | "html-webpack-plugin": "^4.0.4", 47 | "i": "^0.3.6", 48 | "mini-css-extract-plugin": "^0.9.0", 49 | "node-loader": "^0.6.0", 50 | "node-sass": "^4.13.1", 51 | "nodemon": "^2.0.3", 52 | "npm": "^6.14.5", 53 | "npm-run-all": "^4.1.5", 54 | "promise-polyfill": "^8.1.3", 55 | "sass-loader": "^8.0.2", 56 | "style-loader": "^1.1.3", 57 | "ts-loader": "^6.2.2", 58 | "ts-node": "^8.10.1", 59 | "tslint": "^6.1.2", 60 | "typescript": "^3.9.2", 61 | "url-loader": "^4.1.0", 62 | "webpack": "^4.42.1", 63 | "webpack-cli": "^3.3.11", 64 | "webpack-dev-server": "^3.10.3", 65 | "webpack-merge": "^4.2.2", 66 | "webpack-node-externals": "^1.7.2", 67 | "webpack-shell-plugin": "^0.5.0" 68 | }, 69 | "dependencies": { 70 | "canvas": "^2.6.1", 71 | "stackblur-canvas": "^2.3.0" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/canvas/index.ts: -------------------------------------------------------------------------------- 1 | import { extend } from '@Src/utils' 2 | 3 | const diff = ENV === 'node' ? 4 | require('./node')._Canvas : 5 | require('./web')._Canvas 6 | 7 | const extra = { 8 | getImage(image) { 9 | if (typeof image === 'string') { 10 | return Canvas.loadImage(image) 11 | } else if (typeof image === 'object') { 12 | return Promise.resolve(image) 13 | } else { 14 | return Promise.reject(`getImage error, src=${image}`) 15 | } 16 | }, 17 | drawRoundRectPath( 18 | ctx: CanvasRenderingContext2D, 19 | width: number, 20 | height: number, 21 | radius: number 22 | ) { 23 | ctx.beginPath() 24 | ctx.arc(width - radius, height - radius, radius, 0, Math.PI / 2) 25 | ctx.lineTo(radius, height) 26 | ctx.arc(radius, height - radius, radius, Math.PI / 2, Math.PI) 27 | ctx.lineTo(0, radius) 28 | ctx.arc(radius, radius, radius, Math.PI, Math.PI * 3 / 2) 29 | ctx.lineTo(width - radius, 0) 30 | ctx.arc(width - radius, radius, radius, Math.PI * 3 / 2, Math.PI * 2) 31 | ctx.lineTo(width, height - radius) 32 | ctx.closePath() 33 | }, 34 | drawRoundRect( 35 | ctx: CanvasRenderingContext2D, 36 | x: number, 37 | y: number, 38 | width: number, 39 | height: number, 40 | radius: number = 0, 41 | fillColor: string = '#fff', 42 | strokeWidth: number = 0, 43 | strokeColor: string = '#000', 44 | ) { 45 | // 圆的直径必然要小于矩形的宽高 46 | const minL = Math.min(width, height) 47 | if (2 * radius > minL) radius = minL / 2 48 | 49 | ctx.save() 50 | ctx.translate(x, y) 51 | // 绘制圆角矩形的各个边 52 | extra.drawRoundRectPath(ctx, width , height, radius) 53 | ctx.fillStyle = fillColor 54 | ctx.fill() 55 | ctx.lineWidth = strokeWidth 56 | ctx.strokeStyle = strokeColor 57 | ctx.stroke() 58 | ctx.restore() 59 | } 60 | } 61 | 62 | export const Canvas = extend(true, diff, extra) as { 63 | create(width: number, height: number): [HTMLCanvasElement, CanvasRenderingContext2D] 64 | loadImage(image: string): Promise 65 | getImage(image: any): Promise 66 | drawRoundRect( 67 | ctx: CanvasRenderingContext2D, 68 | x: number, 69 | y: number, 70 | width: number, 71 | height: number, 72 | radius?: number, 73 | fillColor?: string, 74 | strokeWidth?: number, 75 | strokeColor?: string, 76 | ): void 77 | } 78 | 79 | -------------------------------------------------------------------------------- /src/canvas/node.ts: -------------------------------------------------------------------------------- 1 | import { createCanvas, loadImage } from 'canvas' 2 | 3 | // 使用 node-canvas, 兼容 Node 环境 4 | export const _Canvas = { 5 | name: 'NodeCanvas', 6 | create(width: number = 500, height: number = 500) { 7 | const cvs = createCanvas(width, height) 8 | const ctx = cvs.getContext('2d') 9 | return [cvs, ctx] 10 | }, 11 | loadImage(image: string) { 12 | return loadImage(image) 13 | }, 14 | } -------------------------------------------------------------------------------- /src/canvas/web.ts: -------------------------------------------------------------------------------- 1 | import { is } from '../utils' 2 | 3 | // Web环境 4 | export const _Canvas = { 5 | name: 'WebCanvas', 6 | create(width: number = 500, height: number = 500) { 7 | const cvs = document.createElement('canvas') 8 | const ctx = cvs.getContext('2d') 9 | if (is.num(width)) cvs.width = width 10 | if (is.num(height)) cvs.height = height 11 | return [cvs, ctx] as [HTMLCanvasElement, CanvasRenderingContext2D] 12 | }, 13 | loadImage(image: string) { 14 | return new Promise((resolve, reject) => { 15 | const img = new Image() 16 | if (image.indexOf('http') === 0) img.crossOrigin = '*' 17 | img.onload = () => resolve(img) 18 | img.onerror = () => reject(`img load error.url: ${image}`) 19 | img.src = image 20 | }) 21 | }, 22 | } -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { MCanvas } from './modules/mcanvas' 2 | export { MCanvas } from './modules/mcanvas' 3 | export { MImage } from './modules/mimage' 4 | export default MCanvas -------------------------------------------------------------------------------- /src/lib.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | 5 | declare module 'mcanvas' { 6 | export class MCanvas { 7 | constructor(options?: TCanvas.options) 8 | background(image?: TCommon.image, bg?: TCanvas.backgroundOptions): MCanvas 9 | rect(ops?: TCanvas.rectOptions): MCanvas 10 | circle(ops?: TCanvas.circleOptions): MCanvas 11 | watermark(image: TCommon.image, ops?: TCanvas.watermarkOptions): MCanvas 12 | add(image: TCanvas.addData[] | TCommon.image, options?: TCanvas.addOptions): MCanvas 13 | text(context: string, ops?: TCanvas.textOptions): MCanvas 14 | draw(ops?: TCommon.drawOptions | ((b64: string) => void)): Promise 15 | } 16 | 17 | export class MImage { 18 | constructor(image: TCommon.image) 19 | crop(options?: TImage.cropOptions): MImage 20 | compress(options?: TImage.compressOptions): MImage 21 | filter(type: TImage.ftype, ...data: any): MImage 22 | clear(): MImage 23 | draw(ops?: TCommon.drawOptions | ((b64: string) => void)): Promise 24 | } 25 | 26 | export default MCanvas 27 | } -------------------------------------------------------------------------------- /src/modules/mimage.ts: -------------------------------------------------------------------------------- 1 | import { Queue } from '@Src/utils/queue' 2 | import { getSize, is, extend } from '@Src/utils' 3 | import { crop } from '@Src/utils/crop' 4 | import { blur, flip, gray, mosaic, oil } from '@Src/utils/filter' 5 | import { Canvas } from '@Src/canvas' 6 | 7 | export class MImage { 8 | private _image: TCommon.image 9 | private _queue: Queue 10 | private _error: (error) => any 11 | constructor(image: TCommon.image) { 12 | this._image = image 13 | this._queue = new Queue() 14 | this.clear() 15 | } 16 | public clear() { 17 | this._getImage()._drawCanvas() 18 | return this 19 | } 20 | private _drawCanvas() { 21 | return this._run((img) => { 22 | const { iw, ih } = getSize(img) 23 | const [cvs, ctx] = Canvas.create(iw, ih) 24 | ctx.drawImage(img, 0, 0, iw, ih) 25 | return { cvs, ctx } 26 | }) 27 | } 28 | 29 | private _getImage() { 30 | // 加载图片 31 | return this._run(() => ( 32 | new Promise(async (resolve) => { 33 | if (is.str(this._image)) { 34 | Canvas.getImage(this._image) 35 | .then(img => resolve(this._image = img)) 36 | .catch(this._error) 37 | } else { 38 | resolve(this._image) 39 | } 40 | }) 41 | )) 42 | } 43 | 44 | private _run(fn: (data: any) => any) { 45 | this._queue.push((next, data) => { 46 | setTimeout(async () => { 47 | const nextData = fn(data) 48 | if (is.promise(nextData)) { 49 | nextData.then(next) 50 | } else { 51 | next(nextData) 52 | } 53 | }, 0) 54 | }) 55 | return this 56 | } 57 | 58 | public draw(exportConfig: TCommon.drawOptions | ((b64: string) => void) = {}) { 59 | return new Promise((resolve, reject) => { 60 | let config = { 61 | type: 'jpeg', 62 | quality: 1, 63 | exportType: 'base64', 64 | success(result) {}, 65 | error(err) {}, 66 | } 67 | 68 | if (is.fn(exportConfig)) { 69 | config.success = exportConfig 70 | } else { 71 | config = extend(true, config, exportConfig) 72 | if (config.type === 'jpg') config.type = 'jpeg' 73 | } 74 | 75 | const { exportType, type, quality, success, error } = config 76 | const _success = (result) => { 77 | success(result) 78 | resolve(result) 79 | this.clear() 80 | } 81 | 82 | this._error = (err) => { 83 | error(err) 84 | reject(err) 85 | } 86 | 87 | this._queue.perform(({ cvs, quality: _quailty }) => { 88 | if (exportType === 'canvas') { 89 | _success(cvs) 90 | } else { 91 | setTimeout(() => { 92 | const b64 = cvs.toDataURL(`image/${type}`, _quailty || quality) 93 | _success(b64) 94 | }, 0) 95 | } 96 | }) 97 | 98 | }) 99 | } 100 | // 裁剪 101 | public crop(options: TImage.cropOptions = {}) { 102 | return this._run(({ cvs }) => crop(cvs, options)) 103 | } 104 | // 压缩质量,修改尺寸 105 | public compress(options: TImage.compressOptions = {}) { 106 | return this._run(({ cvs, ctx }) => { 107 | let { quality, width, height } = options 108 | const ratio = cvs.width / cvs.height 109 | 110 | if (!is.num(width) && !is.num(height)) return { cvs, ctx, quality } 111 | if (!is.num(width)) width = height! * ratio 112 | if (!is.num(height)) height = width / ratio 113 | 114 | const [nCvs, nCtx] = Canvas.create(width, height) 115 | nCtx.drawImage(cvs, 0, 0, width, height) 116 | 117 | return { cvs: nCvs, ctx: nCtx, quality } 118 | }) 119 | } 120 | // 滤镜 121 | public filter(type: TImage.ftype, ...data) { 122 | return this._run(({ cvs, ctx }) => { 123 | let fcvs = cvs, fctx = ctx 124 | switch (type) { 125 | case 'blur': 126 | [fcvs, fctx] = blur(cvs, ...data) 127 | break 128 | case 'flip': 129 | [fcvs, fctx] = flip(cvs, ...data) 130 | break 131 | case 'gray': 132 | [fcvs, fctx] = gray(cvs) 133 | break 134 | case 'mosaic': 135 | [fcvs, fctx] = mosaic(cvs, ...data) 136 | break 137 | case 'oil': 138 | [fcvs, fctx] = oil(cvs, ...data) 139 | break 140 | } 141 | 142 | return { 143 | cvs: fcvs, 144 | ctx: fctx, 145 | } 146 | }) 147 | } 148 | } -------------------------------------------------------------------------------- /src/types/commom.d.ts: -------------------------------------------------------------------------------- 1 | declare var ENV: 'node' | 'web' 2 | 3 | declare namespace TCommon { 4 | type numstr = number | string 5 | type image = string | HTMLImageElement | HTMLCanvasElement 6 | interface drawOptions { 7 | type?: 'png' | 'jpg' | 'jpeg', 8 | quality?: number, 9 | exportType?: 'base64' | 'canvas', 10 | success?: (b64: string | HTMLCanvasElement) => void 11 | error?: (err: any) => void 12 | } 13 | } -------------------------------------------------------------------------------- /src/types/mcanvas.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace TCanvas { 2 | interface options { 3 | width?: number, 4 | height?: number, 5 | backgroundColor?: string, 6 | } 7 | interface data { 8 | textId: number, 9 | text : any, 10 | bgConfig: null | backgroundOptions, 11 | } 12 | interface backgroundOptions { 13 | type: 'crop' | 'contain' | 'origin', 14 | image?: any, 15 | color?: string, 16 | left?: TCommon.numstr, 17 | top?: TCommon.numstr, 18 | } 19 | type queue = Array<() => void> 20 | interface position { 21 | x?: TCommon.numstr, 22 | y?: TCommon.numstr, 23 | scale?: number, 24 | rotate?: number, 25 | } 26 | interface crop { 27 | x?: TCommon.numstr, 28 | y?: TCommon.numstr, 29 | width?: TCommon.numstr, 30 | height?: TCommon.numstr, 31 | radius?: TCommon.numstr, 32 | } 33 | interface addOptions { 34 | width?: TCommon.numstr, 35 | crop?: crop, 36 | pos?: position 37 | } 38 | interface addData { 39 | image: TCommon.image, 40 | options: addOptions, 41 | } 42 | 43 | interface watermarkOptions { 44 | width?: string | number 45 | pos?: 'leftTop' | 'leftBottom' | 'rightTop' | 'rightBottom' 46 | margin?: number 47 | } 48 | interface textStyle { 49 | font?: string, 50 | color?: string, 51 | lineHeight?: number, 52 | type?: 'fill' | 'stroke', 53 | lineWidth?: number, 54 | wordBreak?: boolean, 55 | shadow?: { 56 | color?: string | null, 57 | blur?: number, 58 | offsetX?: number, 59 | offsetY: number, 60 | }, 61 | gradient?:{ 62 | type?: 1 | 2, 63 | colorStop?: string[], 64 | }, 65 | } 66 | interface textOptions { 67 | width?: number | string, 68 | align?: 'left' | 'center' | 'right', 69 | normalStyle?: textStyle, 70 | smallStyle?: textStyle, 71 | largeStyle?: textStyle, 72 | pos?: { 73 | x?: TCommon.numstr, 74 | y?: TCommon.numstr, 75 | rotate?: number, 76 | } 77 | } 78 | 79 | interface rectOptions { 80 | x?: TCommon.numstr, 81 | y?: TCommon.numstr, 82 | width?: TCommon.numstr, 83 | height?: TCommon.numstr, 84 | radius?: number, 85 | strokeWidth?: number, 86 | strokeColor?: string, 87 | fillColor?: string, 88 | } 89 | interface circleOptions { 90 | x?: TCommon.numstr, 91 | y?: TCommon.numstr, 92 | radius?: TCommon.numstr, 93 | strokeWidth?: number, 94 | strokeColor?: string, 95 | fillColor?: string, 96 | } 97 | } -------------------------------------------------------------------------------- /src/types/mimage.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace TImage { 2 | type ftype = 'blur' | 'flip' | 'gray' | 'mosaic' | 'oil' 3 | interface cropOptions { 4 | x?: number | string 5 | y?: number | string 6 | width?: number | string 7 | height?: number | string 8 | radius?: number | string 9 | } 10 | interface compressOptions { 11 | quality?: number, 12 | width?: number, 13 | height?: number, 14 | } 15 | } -------------------------------------------------------------------------------- /src/utils/crop.ts: -------------------------------------------------------------------------------- 1 | import { 2 | getSize, 3 | getLength, 4 | extend, 5 | transValue, 6 | } from './index' 7 | 8 | import { Canvas } from '../canvas' 9 | 10 | export function crop(img: HTMLCanvasElement | HTMLImageElement, options: TImage.cropOptions) { 11 | const { iw, ih } = getSize(img) 12 | const ops = extend(true, { 13 | x: 0, 14 | y: 0, 15 | width: '100%', 16 | height: '100%', 17 | radius: 0, 18 | }, options) 19 | 20 | let { width, height, x, y, radius } = ops 21 | width = getLength(iw, width) 22 | height = getLength(ih, height) 23 | if (width > iw) width = iw 24 | if (height > ih) height = ih 25 | x = transValue(iw, width, x, 'pos') 26 | y = transValue(ih, height, y, 'pos') 27 | radius = getLength(iw, radius) 28 | 29 | const [cCvs, cCtx] = Canvas.create(width, height) 30 | cCvs.width = width 31 | cCvs.height = height 32 | Canvas.drawRoundRect(cCtx, 0, 0, width, height, radius) 33 | cCtx.globalCompositeOperation = 'source-in' 34 | cCtx.drawImage(img, -x, -y, iw, ih) 35 | 36 | return { 37 | cvs: cCvs, 38 | ctx: cCtx, 39 | } 40 | } -------------------------------------------------------------------------------- /src/utils/extend.ts: -------------------------------------------------------------------------------- 1 | function isPlainObject(object) { 2 | const class2type = {}, 3 | toString = class2type.toString, 4 | hasOwn = class2type.hasOwnProperty, 5 | fnToString = hasOwn.toString, 6 | ObjectFunctionString = fnToString.call(Object) 7 | 8 | if (!object || toString.call(object) !== '[object Object]') return false 9 | 10 | const proto = Object.getPrototypeOf(object) 11 | if (!proto) return true 12 | 13 | const ctor = hasOwn.call(proto, 'constructor') && proto.constructor 14 | return typeof ctor === 'function' && fnToString.call(ctor) === ObjectFunctionString 15 | } 16 | 17 | export function extend(this: any, ...args) { 18 | let options 19 | let name 20 | let clone 21 | let copy 22 | let source 23 | let copyIsArray 24 | let target = arguments[0] || {} 25 | let i = 1 26 | let length = arguments.length 27 | let deep = false 28 | 29 | if (typeof target === 'boolean') { 30 | deep = target 31 | target = arguments[i] || {} 32 | i++ 33 | } 34 | 35 | if (typeof target !== 'object' && typeof target !== 'function') { 36 | target = {} 37 | } 38 | 39 | if (i === length) { 40 | target = this 41 | i-- 42 | } 43 | 44 | for (; i < length; i++) { 45 | if ((options = arguments[i]) !== null) { 46 | for (name in options) { 47 | if (options.hasOwnProperty(name)) { 48 | source = target[name] 49 | copy = options[name] 50 | 51 | if (target === copy) { 52 | continue 53 | } 54 | 55 | if (deep && copy && (isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) { 56 | if (copyIsArray) { 57 | copyIsArray = false 58 | clone = source && Array.isArray(source) ? source : [] 59 | } else { 60 | clone = source && isPlainObject(source) ? source : {} 61 | } 62 | 63 | target[name] = extend(deep, clone, copy) 64 | } else if (copy !== undefined) { 65 | target[name] = copy 66 | } 67 | } 68 | } 69 | } 70 | } 71 | 72 | return target 73 | } 74 | -------------------------------------------------------------------------------- /src/utils/filter.ts: -------------------------------------------------------------------------------- 1 | import * as StackBlur from 'stackblur-canvas' 2 | 3 | export function blur(cvs: HTMLCanvasElement, value: number = 20) { 4 | const { width, height } = cvs 5 | const ctx = cvs.getContext('2d') as CanvasRenderingContext2D 6 | StackBlur.canvasRGBA(cvs, 0, 0, width, height, value) 7 | return [cvs, ctx] 8 | } 9 | 10 | // 翻转 11 | export function flip(cvs: HTMLCanvasElement, dire: 'hor' | 'ver' = 'hor') { 12 | const { width, height } = cvs 13 | const ctx = cvs.getContext('2d') as CanvasRenderingContext2D 14 | const { data: oPixels } = ctx.getImageData(0, 0, width, height) 15 | const imageData = ctx.createImageData(width, height) 16 | const dstPixels = imageData.data 17 | 18 | let y, x, off, dstOff 19 | for (y = 0; y < height; y++) { 20 | for (x = 0; x < width; x++) { 21 | off = (y * width + x) * 4 22 | dstOff = dire === 'hor' ? 23 | (y * width + (width - x - 1)) * 4 : 24 | ((height - y - 1) * width + x) * 4 25 | dstPixels[dstOff] = oPixels[off] 26 | dstPixels[dstOff + 1] = oPixels[off + 1] 27 | dstPixels[dstOff + 2] = oPixels[off + 2] 28 | dstPixels[dstOff + 3] = oPixels[off + 3] 29 | } 30 | } 31 | ctx.putImageData(imageData, 0, 0) 32 | return [cvs, ctx] 33 | } 34 | // 黑白 35 | export function gray(cvs: HTMLCanvasElement) { 36 | const { width, height } = cvs 37 | const ctx = cvs.getContext('2d') as CanvasRenderingContext2D 38 | const { data: oPixels } = ctx.getImageData(0, 0, width, height) 39 | const imageData = ctx.createImageData(width, height) 40 | const dstPixels = imageData.data 41 | 42 | let i, r, g, b, v 43 | for (i = 0; i < oPixels.length; i += 4) { 44 | r = oPixels[i] 45 | g = oPixels[i + 1] 46 | b = oPixels[i + 2] 47 | v = 0.3 * r + 0.59 * g + 0.11 * b 48 | dstPixels[i] = dstPixels[i + 1] = dstPixels[i + 2] = v 49 | dstPixels[i + 3] = oPixels[i + 3] 50 | } 51 | ctx.putImageData(imageData, 0, 0) 52 | return [cvs, ctx] 53 | } 54 | // 马赛克 55 | export function mosaic(cvs: HTMLCanvasElement, block: number = 10) { 56 | const { width, height } = cvs 57 | const ctx = cvs.getContext('2d') as CanvasRenderingContext2D 58 | const { data: oPixels } = ctx.getImageData(0, 0, width, height) 59 | const imageData = ctx.createImageData(width, height) 60 | const dstPixels = imageData.data 61 | 62 | let cols = Math.ceil(width / block), 63 | rows = Math.ceil(height / block), 64 | row, col, x_start, x_end, y_start, y_end, 65 | x, y, yIndex, index, size, r, g, b, a 66 | 67 | for (row = 0; row < rows; row++) { 68 | y_start = row * block 69 | y_end = y_start + block 70 | if (y_end > height) y_end = height 71 | 72 | for (col = 0; col < cols; col++) { 73 | x_start = col * block 74 | x_end = x_start + block 75 | if (x_end > width) x_end = width 76 | r = g = b = a = 0 77 | size = (x_end - x_start) * (y_end - y_start) 78 | 79 | for (y = y_start; y < y_end; y += 1) { 80 | yIndex = y * width 81 | for (x = x_start; x < x_end; x += 1) { 82 | index = (yIndex + x) << 2 83 | r += oPixels[index] 84 | g += oPixels[index + 1] 85 | b += oPixels[index + 2] 86 | a += oPixels[index + 3] 87 | } 88 | } 89 | 90 | r = (r / size) + 0.5 | 0 91 | g = (g / size) + 0.5 | 0 92 | b = (b / size) + 0.5 | 0 93 | a = (a / size) + 0.5 | 0 94 | 95 | for (y = y_start; y < y_end; y++) { 96 | yIndex = y * width 97 | for (x = x_start; x < x_end; x++) { 98 | index = (yIndex + x) << 2 99 | dstPixels[index] = r 100 | dstPixels[index + 1] = g 101 | dstPixels[index + 2] = b 102 | dstPixels[index + 3] = a 103 | } 104 | } 105 | } 106 | } 107 | ctx.putImageData(imageData, 0, 0) 108 | return [cvs, ctx] 109 | } 110 | // range: 1 - 5 111 | // levels: 1 - 256 112 | export function oil(cvs: HTMLCanvasElement, range: number = 2, levels: number = 32) { 113 | const { width, height } = cvs 114 | const ctx = cvs.getContext('2d') as CanvasRenderingContext2D 115 | const { data: oPixels } = ctx.getImageData(0, 0, width, height) 116 | const imageData = ctx.createImageData(width, height) 117 | const dstPixels = imageData.data 118 | 119 | const rh: number[] = [], 120 | gh: number[] = [], 121 | bh: number[] = [], 122 | rt: number[] = [], 123 | gt: number[] = [], 124 | bt: number[] = [] 125 | let index = 0, rowIndex, colIndex, offset, srcIndex, 126 | x, y, z, row, col, sr, sg, sb, ri, gi, bi, r, g, b 127 | 128 | for (y = 0; y < height; y++) { 129 | for (x = 0; x < width; x++) { 130 | for (z = 0; z < levels; z++) { 131 | rh[z] = gh[z] = bh[z] = rt[z] = gt[z] = bt[z] = 0 132 | } 133 | 134 | for (row = -range; row <= range; row++) { 135 | rowIndex = y + row 136 | if (rowIndex < 0 || rowIndex >= height) continue 137 | offset = rowIndex * width 138 | for (col = -range; col <= range; col++) { 139 | colIndex = x + col 140 | if (colIndex < 0 || colIndex >= width) continue 141 | srcIndex = (offset + colIndex) << 2 142 | sr = oPixels[srcIndex] 143 | sg = oPixels[srcIndex + 1] 144 | sb = oPixels[srcIndex + 2] 145 | ri = (sr * levels) >> 8 146 | gi = (sg * levels) >> 8 147 | bi = (sb * levels) >> 8 148 | rt[ri] += sr 149 | gt[gi] += sg 150 | bt[bi] += sb 151 | rh[ri] += 1 152 | gh[gi] += 1 153 | bh[bi] += 1 154 | } 155 | } 156 | r = g = b = 0 157 | for (z = 1; z < levels; z++) { 158 | if (rh[z] > rh[r]) r = z 159 | if (gh[z] > gh[g]) g = z 160 | if (bh[z] > bh[b]) b = z 161 | } 162 | dstPixels[index] = rt[r] / rh[r] | 0 163 | dstPixels[index + 1] = gt[g] / gh[g] | 0 164 | dstPixels[index + 2] = bt[b] / bh[b] | 0 165 | dstPixels[index + 3] = oPixels[index + 3] 166 | index += 4 167 | } 168 | } 169 | ctx.putImageData(imageData, 0, 0) 170 | return [cvs, ctx] 171 | } -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { Image as NodeImage } from 'canvas' 2 | import { is } from './is' 3 | export { is, type } from './is' 4 | export { extend } from './extend' 5 | 6 | export function forin(obj, cbk) { 7 | for (let k in obj) { 8 | if (obj.hasOwnProperty(k)) { 9 | cbk(k, obj[k]) 10 | } 11 | } 12 | } 13 | 14 | export function belowIOS8() { 15 | if (ENV === 'node') return false 16 | const UA = window.navigator.userAgent.toLowerCase() 17 | const IOS = /(iPhone|iPad|iPod|iOS)/gi.test(UA) 18 | const IPAD = /(iPad)/gi.test(UA) 19 | 20 | if (IOS) { 21 | const version = IPAD ? UA.match(/cpu os (\d*)/) : UA.match(/iphone os (\d*)/) 22 | return !!(version && +version[1] < 9) 23 | }else { 24 | return false 25 | } 26 | } 27 | 28 | export function deepCopy(obj) { 29 | return JSON.parse(JSON.stringify(obj)) 30 | } 31 | 32 | function isImg(dom: any): dom is HTMLImageElement { 33 | return dom.tagName === 'IMG' 34 | } 35 | 36 | function isCanvas(dom: any): dom is HTMLCanvasElement { 37 | return dom.tagName === 'CANVAS' 38 | } 39 | 40 | 41 | export type TGetSizeImage = HTMLImageElement | HTMLCanvasElement | NodeImage 42 | export function getSize(img: TGetSizeImage): { 43 | iw: number, 44 | ih: number, 45 | } { 46 | if (isImg(img)) { 47 | return { 48 | iw: img.naturalWidth, 49 | ih: img.naturalHeight 50 | } 51 | } 52 | return { 53 | iw: img.width, 54 | ih: img.height 55 | } 56 | } 57 | 58 | export function include(tar, value) { 59 | return tar.indexOf && tar.indexOf(value) !== -1 60 | } 61 | 62 | // 参数加工函数; 63 | // 兼容 5 种 value 值: 64 | // x:250, x:'250px', x:'100%', x:'left:250',x:'center', 65 | // width:100,width:'100px',width:'100%' 66 | // par: 父级尺寸 67 | // child: 自身尺寸 68 | // value: 值 69 | // type: 不同处理方式 70 | export function transValue(par: number, child: number, value: number | string, type: 'pos' | 'crop') { 71 | let result = value 72 | if (is.str(value)) { 73 | if (include(value, ':') && type === 'pos') { 74 | const [ _attr, _value ] = value.split(':') 75 | switch (_attr) { 76 | case 'left': 77 | case 'top': 78 | result = +(_value.replace('px', '')) 79 | break 80 | case 'right': 81 | case 'bottom': 82 | result = par - (+(_value.replace('px', ''))) - child 83 | break 84 | } 85 | } else if (include(value, 'px')) { 86 | result = (+value.replace('px', '')) 87 | } else if (include(value, '%')) { 88 | result = (type === 'crop' ? child : par) * (+value.replace('%', '')) / 100 89 | } else if (include(['top', 'left'], value)) { 90 | result = 0 91 | } else if (include(['bottom', 'right'], value)) { 92 | result = par - child 93 | } else if (value === 'center') { 94 | result = (par - child) / 2 95 | } else if (value === 'origin') { 96 | result = child 97 | } else { 98 | result = +value 99 | } 100 | } 101 | 102 | return Math.round(result as number) 103 | } 104 | 105 | // 获取长度 106 | // imgW: 参照尺寸 107 | // value: 值 108 | export function getLength(ref: number, value: string | number) { 109 | let result = value 110 | if (is.str(value)) { 111 | if (include(value, 'px')) { 112 | result = (+value.replace('px', '')) 113 | } else if (include(value, '%')) { 114 | result = ref * (+value.replace('%', '')) / 100 115 | } else if (value === 'center') { 116 | result = ref / 2 117 | } else { 118 | result = +value 119 | } 120 | } 121 | return Math.round(result as number) 122 | } 123 | 124 | export function throwError(msg: string) { 125 | throw new Error(`[MCanvas ERROR]: ${msg}`) 126 | } 127 | 128 | export function throwWarn(msg: string) { 129 | throw new Error(`[MCanvas WARN]: ${msg}`) 130 | } 131 | 132 | function isLetter(temp: string) { 133 | const re = /^[A-Za-z]+$/g 134 | if (re.test(temp)) return true 135 | return false 136 | } 137 | 138 | function isSymbol(temp: string) { 139 | const re = /[\ |\s*(.*?)\s+$|\~|\`|\!|\@|\#|\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\||\\|\[|\]|\{|\}|\;|\:|\"|\'|\,|\,|\。|\“|\”|\‘|\’|\¥|\?|\(|\)|\;|\:|\、|\!|\<|\.|\>|\/|\?]/g 140 | if (re.test(temp)) return true 141 | return false 142 | } 143 | 144 | export function splitWords(context: string) { 145 | if (!is.str(context)) return [] 146 | const result: string[] = [] 147 | let i, l = context.length, word = '' 148 | for (i = 0; i < l; i++) { 149 | const single = context[i] 150 | if (isLetter(single)) { 151 | // 英文, 需要进行分词 152 | const next = context[i + 1] 153 | word += single 154 | if (isSymbol(next)) { 155 | result.push(word) 156 | word = '' 157 | } 158 | } else { 159 | // 非英文 160 | result.push(single) 161 | } 162 | } 163 | return result 164 | } 165 | -------------------------------------------------------------------------------- /src/utils/is.ts: -------------------------------------------------------------------------------- 1 | const typeString = 'Boolean Number String Function Array Date RegExp Object Error Symbol' 2 | const class2type = {} 3 | typeString.split(' ').forEach((type) => { 4 | class2type[`[object ${type}]`] = type.toLowerCase() 5 | }) 6 | 7 | export function type(object: any) { 8 | const type = class2type.toString.call(object) 9 | if (object === null) return object + '' 10 | if (Number.isNaN(object)) return 'NaN' 11 | 12 | const isObject = typeof object === 'object' 13 | const isFn = typeof object === 'function' 14 | return isObject || isFn ? class2type[type] || 'object' : typeof object 15 | } 16 | 17 | export const is = { 18 | primitive(tar: any): tar is (string | number) { 19 | return ['string', 'number'].includes(type(tar)) 20 | }, 21 | array(tar: any): tar is any[] { 22 | return type(tar) === 'array' 23 | }, 24 | str(tar: any): tar is string { 25 | return type(tar) === 'string' 26 | }, 27 | obj(tar: any): tar is object { 28 | return type(tar) === 'object' 29 | }, 30 | arr(tar: any): tar is any[] { 31 | return type(tar) === 'array' 32 | }, 33 | num(tar: any): tar is number { 34 | return type(tar) === 'number' 35 | }, 36 | fn(tar: any): tar is (...data: any) => any { 37 | return type(tar) === 'function' 38 | }, 39 | undef(tar: any): tar is undefined { 40 | return type(tar) === 'undefined' 41 | }, 42 | def(tar: any) { 43 | return !is.undef(tar) 44 | }, 45 | null(tar: any): tar is null { 46 | return type(tar) === 'null' 47 | }, 48 | empty(tar: any): boolean { 49 | const t = type(tar) 50 | switch (t) { 51 | case 'object': 52 | return Object.keys(tar).length === 0 53 | case 'array': 54 | return tar.length === 0 55 | case 'string': 56 | return !(tar.trim()) 57 | case 'undefined': 58 | case 'null': 59 | case 'NaN': 60 | case 'boolean': 61 | return true 62 | default: 63 | return false 64 | } 65 | }, 66 | Nil(tar: any): tar is (null | undefined) { 67 | return ['null', 'undefined'].includes(type(tar)) 68 | }, 69 | NaN(tar: any): boolean { 70 | return type(tar) === 'NaN' 71 | }, 72 | promise(tar: any): tar is Promise { 73 | return !!tar && (is.obj(tar) || is.fn(tar)) && is.fn(tar['then']) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/utils/queue.ts: -------------------------------------------------------------------------------- 1 | import { is } from './is' 2 | 3 | type Next = (data: any) => any 4 | interface IConfig { 5 | before?: (data: any) => any, 6 | after?: (data: any) => any, 7 | } 8 | export class Queue { 9 | // 绘制函数队列; 10 | private before: (data: any) => any 11 | private after: (data: any) => any 12 | private end: Next 13 | private isFirst: boolean = true 14 | private queue: ((next: Next, data: any) => void)[] = [] 15 | constructor(config?: IConfig) { 16 | const { before = data => data, after = data => data } = config || {} 17 | this.before = before 18 | this.after = after 19 | } 20 | public push(fn: (next: Next, data: any) => void) { 21 | if (is.fn(fn)) this.queue.push(fn) 22 | } 23 | public perform(end: Next, initData?: any) { 24 | this.end = end 25 | this._next(initData) 26 | } 27 | private _next = (data: any) => { 28 | if (!this.isFirst) data = this.after(data) 29 | if (this.queue.length > 0) { 30 | const fn = this.queue.shift() 31 | if (fn) { 32 | data = this.before(data) 33 | fn(this._next, data) 34 | this.isFirst = false 35 | } 36 | } else { 37 | this.end(data) 38 | this.queue = [] 39 | } 40 | } 41 | } 42 | 43 | export class MQueue { 44 | private _queue: Queue 45 | constructor() { 46 | this._queue = new Queue() 47 | } 48 | public line(fn: (data: any) => any) { 49 | this._queue.push((next, data) => { 50 | setTimeout(async () => { 51 | const nextData = fn(data) 52 | if (is.promise(nextData)) { 53 | nextData.then(next) 54 | } else { 55 | next(nextData) 56 | } 57 | }, 0) 58 | }) 59 | return this 60 | } 61 | public start(end: Next, initData?: any) { 62 | this._queue.perform(end, initData) 63 | } 64 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "outDir": "dist", 5 | "module": "esnext", 6 | "target": "es5", 7 | "sourceMap": true, 8 | "allowJs": true, 9 | "jsx": "react", 10 | "moduleResolution": "node", 11 | "forceConsistentCasingInFileNames": true, 12 | "noImplicitReturns": true, 13 | "noImplicitThis": true, 14 | "importHelpers": true, 15 | "strictNullChecks": true, 16 | "suppressImplicitAnyIndexErrors": true, 17 | "allowSyntheticDefaultImports": true, 18 | "resolveJsonModule": true, 19 | "esModuleInterop": true, 20 | "experimentalDecorators": true, 21 | "paths": { 22 | "@Src/*": ["./src/*"] 23 | } 24 | }, 25 | "include": [ 26 | "src/*", 27 | "example/*", 28 | "src/types/*", 29 | ], 30 | "exclude": [ 31 | "node_modules/*", 32 | "dist/*", 33 | ], 34 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "linterOptions": { 3 | "exclude": [ 4 | "node_modules/**/*.ts" 5 | ] 6 | }, 7 | "extends": [ 8 | "tslint:recommended" 9 | ], 10 | "jsRules": { 11 | "semicolon": [true, "never"], 12 | "no-console": false, 13 | "quotemark": false, 14 | "eofline": false 15 | }, 16 | "rules": { 17 | "semicolon": [ 18 | true, 19 | "never" 20 | ], 21 | "ordered-imports": false, 22 | "object-literal-sort-keys": false, 23 | "variable-name": false, 24 | "no-bitwise": false, 25 | "no-var-requires": false, 26 | "no-console": false, 27 | "jsx-no-lambda": false, 28 | "jsx-boolean-value":false, 29 | "no-shadowed-variable": false, 30 | "curly": [true, "ignore-same-line"], 31 | "interface-over-type-literal": false, 32 | "no-string-literal": false, 33 | "no-empty": false, 34 | "max-classes-per-file": false, 35 | "prefer-for-of": false, 36 | "no-unused-expression": false, 37 | "member-ordering":[false], 38 | "prefer-const": false, 39 | "max-line-length": false, 40 | "no-namespace": false, 41 | "interface-name": false, 42 | "quotemark": false, 43 | "radix": false, 44 | "no-trailing-whitespace": false, 45 | "one-variable-per-declaration": false, 46 | "whitespace": [true, 47 | "check-branch", 48 | "check-decl", 49 | "check-operator", 50 | "check-module", 51 | "check-separator", 52 | "check-rest-spread", 53 | "check-type", 54 | "check-typecast", 55 | "check-type-operator", 56 | "check-preblock" 57 | ] 58 | } 59 | } --------------------------------------------------------------------------------