├── .babelrc ├── .eslintrc.json ├── .gitignore ├── LICENSE ├── README.md ├── benchmark.md ├── build ├── README.template.md ├── es5.config.js └── es6.config.js ├── dist ├── weRender.es6.js ├── weRender.js └── weRender.min.js ├── examples ├── circle-actions.html ├── circle-clone.html ├── circle-composite.html ├── circle-zoom.html ├── index.html ├── lib │ ├── weRender.min.js │ └── weScroll.min.js └── path │ ├── index.html │ ├── money.png │ ├── seat.draw.js │ ├── seat.svg │ ├── seatCanvas.js │ └── smile.png ├── package.json └── src ├── index.js ├── weCanvas.js └── weStage.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["latest", { 4 | "es2015": { 5 | "modules": false 6 | } 7 | }] 8 | ], 9 | "plugins": ["external-helpers", "transform-class-properties"] 10 | } -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "react-app" 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/* 2 | .DS_Store 3 | .idea 4 | yarn.lock -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 henryluki 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # weRender 2 | Simple, light-weight, Canvas library for 2D rendering 3 | 4 | 5 | # Features 6 | 7 | - `Chain` API 8 | 9 | - Drawing `actions` reusable 10 | 11 | - Same methods as CanvasRenderingContext2D 12 | 13 | # Install 14 | 15 | ```shell 16 | npm install we-render 17 | ``` 18 | 19 | # Usage 20 | 21 | - **ES6 module** 22 | 23 | ```javascript 24 | 25 | import { WeCanvas, WeStage } from 'we-render' 26 | 27 | const child = new WeCanvas() 28 | .setSize(100, 100) 29 | .scale(2, 2) 30 | .fillStyle("red") // different here 31 | .fillRect(0, 0, 10, 10) 32 | 33 | const stage = new WeStage() 34 | 35 | stage.addChid(child) 36 | stage.update() 37 | 38 | ``` 39 | 40 | - **In browser** 41 | 42 | ```html 43 | 44 | 45 | 65 | 66 | ``` 67 | 68 | # Examples 69 | 70 | See examples: 71 | `npm run static` 72 | 73 | * simple 74 | 75 | * clone 76 | 77 | * reuse actions 78 | 79 | * composite 80 | 81 | * zoom 82 | 83 | # Scripts 84 | 85 | Usefull Scripts for improving efficiency. 86 | 87 | **`SVG` to `Canvas`** 88 | 89 | See: https://github.com/weiying-shenzhen/svgToCanvas 90 | 91 | Examples: 92 | 93 | See examples under directory`examples/path` 94 | 95 | # API reference 96 | 97 | ## Modules 98 | 99 |
100 |
weRender
101 |

weRender: Simple Canvas library for easy usage

102 |
103 |
104 | 105 | ## Classes 106 | 107 |
108 |
WeCanvas
109 |

WeCanvas: Easy canvas api for using, support useing chain

110 | 115 |
116 |
WeStage
117 |

WeStage: Canvas manager for WeCanvas

118 |
119 |
120 | 121 | 122 | 123 | ## weRender 124 | weRender: Simple Canvas library for easy usage 125 | 126 | 127 | 128 | ## WeCanvas 129 | WeCanvas: Easy canvas api for using, support useing chain 130 | 131 | - Directly use CanvasRenderingContext2D` methods` 132 | - `Property` of `CanvasRenderingContext2D` here is `method` 133 | - Won't really drawing Canvas until run `draw()` 134 | 135 | **Kind**: global class 136 | 137 | * [WeCanvas](#WeCanvas) 138 | * [new WeCanvas(options)](#new_WeCanvas_new) 139 | * [.setSize(width, height)](#WeCanvas+setSize) 140 | * [.setStyle(width, height)](#WeCanvas+setStyle) 141 | * [.setCoordinate(x, y)](#WeCanvas+setCoordinate) 142 | * [.setScale(scale)](#WeCanvas+setScale) 143 | * [.clear()](#WeCanvas+clear) 144 | * [.getActions()](#WeCanvas+getActions) 145 | * [.setActions(actions)](#WeCanvas+setActions) 146 | * [.draw()](#WeCanvas+draw) 147 | * [.cache(ifCache)](#WeCanvas+cache) 148 | 149 | 150 | 151 | ### new WeCanvas(options) 152 | create a WeCanvas instance 153 | 154 | 155 | | Param | Type | Description | 156 | | --- | --- | --- | 157 | | options | Object | option settions for instance | 158 | 159 | 160 | 161 | ### weCanvas.setSize(width, height) 162 | set canvas size 163 | 164 | **Kind**: instance method of [WeCanvas](#WeCanvas) 165 | 166 | | Param | Type | Description | 167 | | --- | --- | --- | 168 | | width | Number | canvas width | 169 | | height | Number | canvas height | 170 | 171 | 172 | 173 | ### weCanvas.setStyle(width, height) 174 | set canvas style, only width and height 175 | 176 | **Kind**: instance method of [WeCanvas](#WeCanvas) 177 | 178 | | Param | Type | Description | 179 | | --- | --- | --- | 180 | | width | String | canvas style width | 181 | | height | String | canvas style height | 182 | 183 | 184 | 185 | ### weCanvas.setCoordinate(x, y) 186 | set coordinate of stage 187 | 188 | **Kind**: instance method of [WeCanvas](#WeCanvas) 189 | 190 | | Param | Type | Default | Description | 191 | | --- | --- | --- | --- | 192 | | x | Number | 0 | horizontal axis | 193 | | y | Number | 0 | vertical axis | 194 | 195 | 196 | 197 | ### weCanvas.setScale(scale) 198 | set init scale 199 | 200 | **Kind**: instance method of [WeCanvas](#WeCanvas) 201 | 202 | | Param | Type | Description | 203 | | --- | --- | --- | 204 | | scale | Number | scale ratio | 205 | 206 | 207 | 208 | ### weCanvas.clear() 209 | clear canvas 210 | 211 | **Kind**: instance method of [WeCanvas](#WeCanvas) 212 | 213 | 214 | ### weCanvas.getActions() 215 | get actions for context drawing 216 | 217 | **Kind**: instance method of [WeCanvas](#WeCanvas) 218 | 219 | 220 | ### weCanvas.setActions(actions) 221 | set actions 222 | 223 | **Kind**: instance method of [WeCanvas](#WeCanvas) 224 | 225 | | Param | Type | Description | 226 | | --- | --- | --- | 227 | | actions | Array | actions for context drawing | 228 | 229 | 230 | 231 | ### weCanvas.draw() 232 | run actions, draw canvas 233 | 234 | **Kind**: instance method of [WeCanvas](#WeCanvas) 235 | 236 | 237 | ### weCanvas.cache(ifCache) 238 | set cache, default: true 239 | 240 | **Kind**: instance method of [WeCanvas](#WeCanvas) 241 | 242 | | Param | Type | Default | Description | 243 | | --- | --- | --- | --- | 244 | | ifCache | Boolean | true | if set cache | 245 | 246 | 247 | 248 | ## WeStage 249 | WeStage: Canvas manager for WeCanvas 250 | 251 | **Kind**: global class 252 | 253 | * [WeStage](#WeStage) 254 | * [new WeStage(canvas, options)](#new_WeStage_new) 255 | * [.setSize(width, height)](#WeStage+setSize) 256 | * [.setStyle(width, height)](#WeStage+setStyle) 257 | * [.destory()](#WeStage+destory) 258 | * [.clear()](#WeStage+clear) 259 | * [.addChild(child)](#WeStage+addChild) 260 | * [.translate(x, y, reset)](#WeStage+translate) 261 | * [.update()](#WeStage+update) 262 | 263 | 264 | 265 | ### new WeStage(canvas, options) 266 | create a WeStage instance 267 | 268 | 269 | | Param | Type | Description | 270 | | --- | --- | --- | 271 | | canvas | Canvas | a Canvas element related to the dom | 272 | | options | Object | stage settings | 273 | 274 | 275 | 276 | ### weStage.setSize(width, height) 277 | set stage size 278 | 279 | **Kind**: instance method of [WeStage](#WeStage) 280 | 281 | | Param | Type | Description | 282 | | --- | --- | --- | 283 | | width | Number | stage width | 284 | | height | Number | stage height | 285 | 286 | 287 | 288 | ### weStage.setStyle(width, height) 289 | set stage style 290 | 291 | **Kind**: instance method of [WeStage](#WeStage) 292 | 293 | | Param | Type | Description | 294 | | --- | --- | --- | 295 | | width | String | stage style width | 296 | | height | String | stage style height | 297 | 298 | 299 | 300 | ### weStage.destory() 301 | destrory stage 302 | 303 | **Kind**: instance method of [WeStage](#WeStage) 304 | 305 | 306 | ### weStage.clear() 307 | clear canvas 308 | 309 | **Kind**: instance method of [WeStage](#WeStage) 310 | 311 | 312 | ### weStage.addChild(child) 313 | add child to the stage 314 | 315 | **Kind**: instance method of [WeStage](#WeStage) 316 | 317 | | Param | Type | Description | 318 | | --- | --- | --- | 319 | | child | [WeCanvas](#WeCanvas) \| Object | WeCanvas instance or Object cotains Canvas | 320 | 321 | 322 | 323 | ### weStage.translate(x, y, reset) 324 | translate stage, move coordinate 325 | 326 | **Kind**: instance method of [WeStage](#WeStage) 327 | 328 | | Param | Type | Default | Description | 329 | | --- | --- | --- | --- | 330 | | x | Number | 0 | offset x | 331 | | y | Number | 0 | offset y | 332 | | reset | Boolean | false | should reset after update | 333 | 334 | 335 | 336 | ### weStage.update() 337 | update stage, draw child on canvas 338 | **run this method, all child canvas will draw** 339 | 340 | **Kind**: instance method of [WeStage](#WeStage) 341 | 342 | *docs autogenerated via [jsdoc2md](https://github.com/jsdoc2md/jsdoc-to-markdown)* 343 | -------------------------------------------------------------------------------- /benchmark.md: -------------------------------------------------------------------------------- 1 | 有无同等屏幕大小的离屏 canvas 性能测试 2 | ---------- 3 | 4 | 对例子 (`circle-zoom`) 使用 `timeit` 打点记时工具,记录了 `WeStage` 有无 `_offscreenCanvas`(离屏缓冲)情况的重绘时间 5 | 6 | ### 数据 7 | 8 | - **`Android`** 9 | 10 | - 无: `key:withoutOffscreen:times.length:1630:average:4.108588957055215` 11 | - 有: `key:offscreen:times.length:1227:average:12.859005704971475` 12 | 13 | - **`IOS`** 14 | 15 | - 无: `key:withoutOffscreen:times.length:1215:average:5.57283950617284` 16 | - 有: `key:offscreen:times.length:1385:average:5.624548736462094` 17 | 18 | - **`Chrome`** 19 | 20 | - 无: `key:withoutOffscreen:times.length:1024:average:0.8701171875` 21 | - 有: `key:offscreen:times.length:1020:average:1.607843137254902` 22 | 23 | ### 统计 24 | | 类别 |有 | 无 | 25 | | --- | --- | --- | 26 | | `Android` | 平均:`12.86ms`| 平均:`4.1ms` | 27 | | `IOS` | 平均:`5.62ms` | 平均:`5.57ms` | 28 | | `Chrome` | 平均:`1.61ms` | 平均:`0.87ms`| 29 | 30 | ### 结论 31 | 32 | 对于 `Android`而言,没有离屏 Canvas 性能提升明显;对`IOS`而言,两者基本没有差别;对`Chrome`而言,差距不大。总体而言,同等大小的离屏 Canvas 缓冲没必要存在。 -------------------------------------------------------------------------------- /build/README.template.md: -------------------------------------------------------------------------------- 1 | # weRender 2 | Simple, light-weight, Canvas library for easy usage 3 | 4 | 5 | # Features 6 | 7 | - `Chain` API 8 | 9 | - Drawing `actions` reusable 10 | 11 | - Same methods as CanvasRenderingContext2D 12 | 13 | # Usage 14 | 15 | - **ES6 module** 16 | 17 | ```javascript 18 | 19 | import { WeCanvas, WeRender } from 'weRender' 20 | 21 | const child = new WeCanvas() 22 | .setSize(100, 100) 23 | .scale(2, 2) 24 | .fillStyle("red") // different here 25 | .fillRect(0, 0, 10, 10) 26 | 27 | const stage = new WeStage() 28 | 29 | stage.addChid(child) 30 | stage.update() 31 | 32 | ``` 33 | 34 | - **In browser** 35 | 36 | ```html 37 | 38 | 39 | 59 | 60 | ``` 61 | 62 | # Examples 63 | 64 | See examples: 65 | `npm run static` 66 | 67 | * simple 68 | 69 | * clone 70 | 71 | * reuse actions 72 | 73 | * composite 74 | 75 | * zoom 76 | 77 | # Scripts 78 | 79 | Usefull Scripts, see: 80 | 81 | * scripts 82 | 83 | # API reference 84 | 85 | {{>main}} 86 | 87 | *docs autogenerated via [jsdoc2md](https://github.com/jsdoc2md/jsdoc-to-markdown)* 88 | -------------------------------------------------------------------------------- /build/es5.config.js: -------------------------------------------------------------------------------- 1 | import resolve from 'rollup-plugin-node-resolve'; 2 | import babel from 'rollup-plugin-babel'; 3 | import eslint from 'rollup-plugin-eslint'; 4 | import uglify from 'rollup-plugin-uglify'; 5 | 6 | const isProduction = process.env.NODE_ENV === 'production' 7 | 8 | export default { 9 | entry: 'src/index.js', 10 | moduleName: "weRender", 11 | format: 'umd', 12 | plugins: [ 13 | resolve(), 14 | babel({ 15 | exclude: 'node_modules/**' 16 | }), 17 | eslint({ 18 | exclude: [ 19 | 'src/styles/**' 20 | ] 21 | }), 22 | ( isProduction && uglify()) 23 | ], 24 | dest: isProduction ? 'dist/weRender.min.js' : 'dist/weRender.js' 25 | }; -------------------------------------------------------------------------------- /build/es6.config.js: -------------------------------------------------------------------------------- 1 | import eslint from 'rollup-plugin-eslint'; 2 | import babel from 'rollup-plugin-babel'; 3 | 4 | export default { 5 | entry: 'src/index.js', 6 | format: 'cjs', 7 | plugins: [ 8 | eslint({ 9 | exclude: [ 10 | 'src/styles/**' 11 | ] 12 | }), 13 | babel({ 14 | babelrc: false, 15 | plugins: ["external-helpers", "transform-class-properties"], 16 | }) 17 | ], 18 | dest: 'dist/weRender.es6.js' 19 | }; 20 | -------------------------------------------------------------------------------- /dist/weRender.es6.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { value: true }); 4 | 5 | /** 6 | * WeCanvas: Easy canvas api for using, support useing chain 7 | * 8 | * - Directly use CanvasRenderingContext2D` methods` 9 | * - `Property` of `CanvasRenderingContext2D` here is `method` 10 | * - Won't really drawing Canvas until run `draw()` 11 | * 12 | */ 13 | class WeCanvas { 14 | static generatorMethod() { 15 | WeCanvas.hasGeneratored = true; 16 | 17 | const retangles = ["clearRect", "fillRect", "strokeRect"]; 18 | const text = ["fillText", "strokeText", "measureText"]; 19 | const lineStyles = ["lineWidth", "lineCap", "lineJoin"]; 20 | const textStyles = ["font", "textAlgin", "textBaseline", "direction"]; 21 | const fillStrokeStyles = ["fillStyle", "strokeStyle"]; 22 | const paths = ["beginPath", "closePath", "moveTo", "lineTo", "bezierCurveTo", "quadraticCurveTo", "arc", "arcTo", "rect"]; 23 | const pathsDrawing = ["fill", "stroke"]; 24 | const transformations = ["rotate", "scale", "translate", "transform", "resetTransform"]; 25 | const images = ["drawImage"]; 26 | const pixel = ["createImageData", "getImageData", "putImageData"]; 27 | const state = ["save", "restore"]; 28 | const shadow = ['shadowColor', 'shadowBlur']; 29 | const _methods = [].concat(retangles, text, lineStyles, textStyles, fillStrokeStyles, paths, pathsDrawing, transformations, images, pixel, state, shadow); 30 | return _methods; 31 | } 32 | /** 33 | * create a WeCanvas instance 34 | * 35 | * @param {Object} options - option settions for instance 36 | */ 37 | constructor(options) { 38 | this._rendered = false; 39 | this._init(options); 40 | this.width = this.canvas.width; 41 | this.height = this.canvas.height; 42 | } 43 | /** 44 | * init 45 | * 46 | * @private 47 | * @param {Canvas} options.canvas - canvas element 48 | * @param {Array} options.actions - context drawing actions 49 | * @param {Number} options.width - canvas witdh 50 | * @param {Number} options.height - canvas height 51 | * @param {Number} options.x - horizontal axis 52 | * @param {Number} options.y - vertical axis 53 | * @param {Array} options.methods - methods for context 54 | */ 55 | _init({ 56 | canvas, 57 | actions, 58 | width, 59 | height, 60 | x, 61 | y, 62 | methods = [] 63 | } = {}) { 64 | this.canvas = canvas || document.createElement('canvas'); 65 | this._ctx = this.canvas.getContext('2d'); 66 | this._initMethods(WeCanvas.hasGeneratored ? methods : WeCanvas.generatorMethod().concat(methods)); 67 | this.setSize(width, height); 68 | this.setCoordinate(x, y); 69 | this.setActions(actions); 70 | } 71 | /** 72 | * reduce context methods that need to be bind 73 | * 74 | * @private 75 | * @param {Array} methods - context methods 76 | */ 77 | _initMethods(methods = []) { 78 | methods.reduce((hash, method) => { 79 | if (!this[method]) { 80 | this._proxy(method); 81 | hash[method] = true; 82 | } 83 | return hash; 84 | }, {}); 85 | } 86 | /** 87 | * bind context method or property to this instance 88 | * 89 | * @private 90 | * @param {String} method - context method or property 91 | */ 92 | _proxy(method) { 93 | const prop = this._ctx[method]; 94 | if (prop === undefined) return; 95 | WeCanvas.prototype[method] = function (...args) { 96 | this._actions.push([Object.prototype.toString.call(prop) === "[object Function]" ? "method" : "property", method, args]); 97 | return this; 98 | }; 99 | } 100 | /** 101 | * set canvas size 102 | * 103 | * @param {Number} width - canvas width 104 | * @param {Number} height - canvas height 105 | */ 106 | setSize(width, height) { 107 | if (width && width !== this.width) { 108 | this.canvas.width = width; 109 | this.width = width; 110 | } 111 | if (height && height !== this.height) { 112 | this.canvas.height = height; 113 | this.height = height; 114 | } 115 | return this; 116 | } 117 | /** 118 | * set canvas style, only width and height 119 | * 120 | * @param {String} width - canvas style width 121 | * @param {String} height - canvas style height 122 | */ 123 | setStyle(width, height) { 124 | if (width) { 125 | this.canvas.style.width = width; 126 | } 127 | if (height) { 128 | this.canvas.style.height = height; 129 | } 130 | return this; 131 | } 132 | /** 133 | * set coordinate of stage 134 | * 135 | * @param {Number} x - horizontal axis 136 | * @param {Number} y - vertical axis 137 | */ 138 | setCoordinate(x = 0, y = 0) { 139 | this.x = x; 140 | this.y = y; 141 | return this; 142 | } 143 | /** 144 | * set init scale 145 | * 146 | * @param {Number} scale - scale ratio 147 | */ 148 | setScale(scale) { 149 | this._ctx.scale(scale, scale); 150 | } 151 | /** 152 | * clear canvas 153 | */ 154 | clear() { 155 | this._ctx.clearRect(0, 0, this.width, this.height); 156 | this.setActions([]); 157 | } 158 | /** 159 | * get actions for context drawing 160 | */ 161 | getActions() { 162 | return this._actions; 163 | } 164 | /** 165 | * set actions 166 | * 167 | * @param {Array} actions - actions for context drawing 168 | */ 169 | setActions(actions = []) { 170 | if (Array.isArray(actions)) { 171 | this._actions = actions; 172 | } 173 | } 174 | /** 175 | * run actions, draw canvas 176 | */ 177 | draw() { 178 | const shouldRender = (!this._rendered ? true : !this._cache) && !!this._actions.length; 179 | if (!shouldRender) return; 180 | 181 | this._actions.forEach(([type, method, args]) => { 182 | const params = Array.prototype.slice.call(args); 183 | 184 | if (type === "method") { 185 | this._ctx[method].apply(this._ctx, params); 186 | } else { 187 | this._ctx[method] = params[0]; 188 | } 189 | }); 190 | if (!this._rendered) { 191 | this._rendered = true; 192 | } 193 | return this; 194 | } 195 | /** 196 | * set cache, default: true 197 | * 198 | * @param {Boolean} ifCache - if set cache 199 | */ 200 | cache(ifCache = true) { 201 | if (typeof ifCache === "boolean") { 202 | this._cache = ifCache; 203 | } 204 | return this; 205 | } 206 | } 207 | 208 | WeCanvas.hasGeneratored = false; 209 | 210 | /** 211 | * WeStage: Canvas manager for WeCanvas 212 | * 213 | */ 214 | class WeStage { 215 | /** 216 | * create a WeStage instance 217 | * 218 | * @param {Canvas} canvas - a Canvas element related to the dom 219 | * @param {Object} options - stage settings 220 | */ 221 | constructor(canvas, options = {}) { 222 | this._canvas = new WeCanvas({ 223 | canvas 224 | }).cache(false); 225 | this._children = []; 226 | this._updating = false; 227 | this._initOptions(options); 228 | } 229 | /** 230 | * init options for settings 231 | * 232 | * @private 233 | * @param {Boolean} options.clear - auto clear stage 234 | * @param {Number} options.ratio - zoom down level when draw child Canvas 235 | */ 236 | _initOptions({ 237 | clear = true, 238 | ratio = 1 239 | }) { 240 | if (typeof clear === "boolean") { 241 | this._clear = clear; 242 | } 243 | if (typeof ratio === "number") { 244 | this._ratio = ratio; 245 | } 246 | } 247 | /** 248 | * set stage size 249 | * 250 | * @param {Number} width - stage width 251 | * @param {Number} height - stage height 252 | */ 253 | setSize(width, height) { 254 | this._canvas.setSize(width, height); 255 | this._canvas.setScale(this._ratio); 256 | } 257 | /** 258 | * set stage style 259 | * 260 | * @param {String} width - stage style width 261 | * @param {String} height - stage style height 262 | */ 263 | setStyle(width, height) { 264 | this._canvas.setStyle(width, height); 265 | } 266 | /** 267 | * destrory stage 268 | */ 269 | destory() { 270 | this._canvas = null; 271 | this._children.length = 0; 272 | } 273 | /** 274 | * clear canvas 275 | */ 276 | clear() { 277 | this._canvas.clear(); 278 | } 279 | 280 | /** 281 | * add child to the stage 282 | * 283 | * @param {WeCanvas|Object} child - WeCanvas instance or Object cotains Canvas 284 | */ 285 | addChild(child) { 286 | if (child instanceof WeCanvas || child.canvas.getContext('2d')) { 287 | this._children.push(child); 288 | } 289 | } 290 | /** 291 | * translate stage, move coordinate 292 | * 293 | * @param {Number} x - offset x 294 | * @param {Number} y - offset y 295 | * @param {Boolean} reset - should reset after update 296 | */ 297 | translate(x = 0, y = 0, reset = false) { 298 | if (typeof x === "number" && typeof y === "number") { 299 | this._coordinate = { 300 | x, 301 | y 302 | }; 303 | } 304 | if (typeof reset === "boolean") { 305 | this._translateReset = reset; 306 | } 307 | } 308 | /** 309 | * update stage, draw child on canvas 310 | * **run this method, all child canvas will draw** 311 | */ 312 | update() { 313 | if (!this._children.length || this._updating) return; 314 | this._updating = true; 315 | if (this._clear) { 316 | this.clear(); 317 | } 318 | this._translateStage(); 319 | this._drawChildren(); 320 | this._resetCoordinate(); 321 | this._updateStage(); 322 | this._updating = false; 323 | this._children = []; 324 | } 325 | /** 326 | * translate stage 327 | * @private 328 | */ 329 | _translateStage() { 330 | if (this._coordinate) { 331 | const { 332 | x, 333 | y 334 | } = this._coordinate; 335 | this._canvas.translate(x, y); 336 | } 337 | } 338 | /** 339 | * reset coordinate 340 | * @private 341 | */ 342 | _resetCoordinate() { 343 | if (this._translateReset) { 344 | const { 345 | x, 346 | y 347 | } = this._coordinate; 348 | this._canvas.translate(-x, -y); 349 | this._coordinate = null; 350 | } 351 | } 352 | /** 353 | * draw children 354 | * @private 355 | */ 356 | _drawChildren() { 357 | this._children.forEach(child => { 358 | if (child instanceof WeCanvas) { 359 | this._drawChild(child); 360 | } else { 361 | this._drawChildCanvas(child); 362 | } 363 | }); 364 | } 365 | /** 366 | * draw child, which is a instance of WeCanvas 367 | * 368 | * @private 369 | * @param {WeCanvas} child - a WeCanvas instance 370 | */ 371 | _drawChild(child) { 372 | const { 373 | canvas, 374 | x, 375 | y, 376 | width, 377 | height 378 | } = child; 379 | child.draw(); 380 | this._canvas.drawImage(canvas, x, y, width / this._ratio, height / this._ratio); 381 | } 382 | /** 383 | * draw child, which is a raw Canvas element 384 | * 385 | * @private 386 | * @param {Number} options.x - horizontal axis 387 | * @param {Number} options.y - vertical axis 388 | * @param {Canvas} options.canvas - Canvas 389 | */ 390 | _drawChildCanvas({ 391 | x = 0, 392 | y = 0, 393 | canvas 394 | }) { 395 | this._canvas.drawImage(canvas, x, y, canvas.width / this._ratio, canvas.height / this._ratio); 396 | } 397 | /** 398 | * draw dom canvas 399 | * @private 400 | */ 401 | _updateStage() { 402 | this._canvas.draw(); 403 | } 404 | } 405 | 406 | exports.WeCanvas = WeCanvas; 407 | exports.WeStage = WeStage; 408 | -------------------------------------------------------------------------------- /dist/weRender.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : 3 | typeof define === 'function' && define.amd ? define(['exports'], factory) : 4 | (factory((global.weRender = global.weRender || {}))); 5 | }(this, (function (exports) { 'use strict'; 6 | 7 | var classCallCheck = function (instance, Constructor) { 8 | if (!(instance instanceof Constructor)) { 9 | throw new TypeError("Cannot call a class as a function"); 10 | } 11 | }; 12 | 13 | var createClass = function () { 14 | function defineProperties(target, props) { 15 | for (var i = 0; i < props.length; i++) { 16 | var descriptor = props[i]; 17 | descriptor.enumerable = descriptor.enumerable || false; 18 | descriptor.configurable = true; 19 | if ("value" in descriptor) descriptor.writable = true; 20 | Object.defineProperty(target, descriptor.key, descriptor); 21 | } 22 | } 23 | 24 | return function (Constructor, protoProps, staticProps) { 25 | if (protoProps) defineProperties(Constructor.prototype, protoProps); 26 | if (staticProps) defineProperties(Constructor, staticProps); 27 | return Constructor; 28 | }; 29 | }(); 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | var slicedToArray = function () { 58 | function sliceIterator(arr, i) { 59 | var _arr = []; 60 | var _n = true; 61 | var _d = false; 62 | var _e = undefined; 63 | 64 | try { 65 | for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { 66 | _arr.push(_s.value); 67 | 68 | if (i && _arr.length === i) break; 69 | } 70 | } catch (err) { 71 | _d = true; 72 | _e = err; 73 | } finally { 74 | try { 75 | if (!_n && _i["return"]) _i["return"](); 76 | } finally { 77 | if (_d) throw _e; 78 | } 79 | } 80 | 81 | return _arr; 82 | } 83 | 84 | return function (arr, i) { 85 | if (Array.isArray(arr)) { 86 | return arr; 87 | } else if (Symbol.iterator in Object(arr)) { 88 | return sliceIterator(arr, i); 89 | } else { 90 | throw new TypeError("Invalid attempt to destructure non-iterable instance"); 91 | } 92 | }; 93 | }(); 94 | 95 | /** 96 | * WeCanvas: Easy canvas api for using, support useing chain 97 | * 98 | * - Directly use CanvasRenderingContext2D` methods` 99 | * - `Property` of `CanvasRenderingContext2D` here is `method` 100 | * - Won't really drawing Canvas until run `draw()` 101 | * 102 | */ 103 | var WeCanvas = function () { 104 | createClass(WeCanvas, null, [{ 105 | key: "generatorMethod", 106 | value: function generatorMethod() { 107 | WeCanvas.hasGeneratored = true; 108 | 109 | var retangles = ["clearRect", "fillRect", "strokeRect"]; 110 | var text = ["fillText", "strokeText", "measureText"]; 111 | var lineStyles = ["lineWidth", "lineCap", "lineJoin"]; 112 | var textStyles = ["font", "textAlgin", "textBaseline", "direction"]; 113 | var fillStrokeStyles = ["fillStyle", "strokeStyle"]; 114 | var paths = ["beginPath", "closePath", "moveTo", "lineTo", "bezierCurveTo", "quadraticCurveTo", "arc", "arcTo", "rect"]; 115 | var pathsDrawing = ["fill", "stroke"]; 116 | var transformations = ["rotate", "scale", "translate", "transform", "resetTransform"]; 117 | var images = ["drawImage"]; 118 | var pixel = ["createImageData", "getImageData", "putImageData"]; 119 | var state = ["save", "restore"]; 120 | var shadow = ['shadowColor', 'shadowBlur']; 121 | var _methods = [].concat(retangles, text, lineStyles, textStyles, fillStrokeStyles, paths, pathsDrawing, transformations, images, pixel, state, shadow); 122 | return _methods; 123 | } 124 | /** 125 | * create a WeCanvas instance 126 | * 127 | * @param {Object} options - option settions for instance 128 | */ 129 | 130 | }]); 131 | 132 | function WeCanvas(options) { 133 | classCallCheck(this, WeCanvas); 134 | 135 | this._rendered = false; 136 | this._init(options); 137 | this.width = this.canvas.width; 138 | this.height = this.canvas.height; 139 | } 140 | /** 141 | * init 142 | * 143 | * @private 144 | * @param {Canvas} options.canvas - canvas element 145 | * @param {Array} options.actions - context drawing actions 146 | * @param {Number} options.width - canvas witdh 147 | * @param {Number} options.height - canvas height 148 | * @param {Number} options.x - horizontal axis 149 | * @param {Number} options.y - vertical axis 150 | * @param {Array} options.methods - methods for context 151 | */ 152 | 153 | 154 | createClass(WeCanvas, [{ 155 | key: "_init", 156 | value: function _init() { 157 | var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, 158 | canvas = _ref.canvas, 159 | actions = _ref.actions, 160 | width = _ref.width, 161 | height = _ref.height, 162 | x = _ref.x, 163 | y = _ref.y, 164 | _ref$methods = _ref.methods, 165 | methods = _ref$methods === undefined ? [] : _ref$methods; 166 | 167 | this.canvas = canvas || document.createElement('canvas'); 168 | this._ctx = this.canvas.getContext('2d'); 169 | this._initMethods(WeCanvas.hasGeneratored ? methods : WeCanvas.generatorMethod().concat(methods)); 170 | this.setSize(width, height); 171 | this.setCoordinate(x, y); 172 | this.setActions(actions); 173 | } 174 | /** 175 | * reduce context methods that need to be bind 176 | * 177 | * @private 178 | * @param {Array} methods - context methods 179 | */ 180 | 181 | }, { 182 | key: "_initMethods", 183 | value: function _initMethods() { 184 | var _this = this; 185 | 186 | var methods = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; 187 | 188 | methods.reduce(function (hash, method) { 189 | if (!_this[method]) { 190 | _this._proxy(method); 191 | hash[method] = true; 192 | } 193 | return hash; 194 | }, {}); 195 | } 196 | /** 197 | * bind context method or property to this instance 198 | * 199 | * @private 200 | * @param {String} method - context method or property 201 | */ 202 | 203 | }, { 204 | key: "_proxy", 205 | value: function _proxy(method) { 206 | var prop = this._ctx[method]; 207 | if (prop === undefined) return; 208 | WeCanvas.prototype[method] = function () { 209 | for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { 210 | args[_key] = arguments[_key]; 211 | } 212 | 213 | this._actions.push([Object.prototype.toString.call(prop) === "[object Function]" ? "method" : "property", method, args]); 214 | return this; 215 | }; 216 | } 217 | /** 218 | * set canvas size 219 | * 220 | * @param {Number} width - canvas width 221 | * @param {Number} height - canvas height 222 | */ 223 | 224 | }, { 225 | key: "setSize", 226 | value: function setSize(width, height) { 227 | if (width && width !== this.width) { 228 | this.canvas.width = width; 229 | this.width = width; 230 | } 231 | if (height && height !== this.height) { 232 | this.canvas.height = height; 233 | this.height = height; 234 | } 235 | return this; 236 | } 237 | /** 238 | * set canvas style, only width and height 239 | * 240 | * @param {String} width - canvas style width 241 | * @param {String} height - canvas style height 242 | */ 243 | 244 | }, { 245 | key: "setStyle", 246 | value: function setStyle(width, height) { 247 | if (width) { 248 | this.canvas.style.width = width; 249 | } 250 | if (height) { 251 | this.canvas.style.height = height; 252 | } 253 | return this; 254 | } 255 | /** 256 | * set coordinate of stage 257 | * 258 | * @param {Number} x - horizontal axis 259 | * @param {Number} y - vertical axis 260 | */ 261 | 262 | }, { 263 | key: "setCoordinate", 264 | value: function setCoordinate() { 265 | var x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; 266 | var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; 267 | 268 | this.x = x; 269 | this.y = y; 270 | return this; 271 | } 272 | /** 273 | * set init scale 274 | * 275 | * @param {Number} scale - scale ratio 276 | */ 277 | 278 | }, { 279 | key: "setScale", 280 | value: function setScale(scale) { 281 | this._ctx.scale(scale, scale); 282 | } 283 | /** 284 | * clear canvas 285 | */ 286 | 287 | }, { 288 | key: "clear", 289 | value: function clear() { 290 | this._ctx.clearRect(0, 0, this.width, this.height); 291 | this.setActions([]); 292 | } 293 | /** 294 | * get actions for context drawing 295 | */ 296 | 297 | }, { 298 | key: "getActions", 299 | value: function getActions() { 300 | return this._actions; 301 | } 302 | /** 303 | * set actions 304 | * 305 | * @param {Array} actions - actions for context drawing 306 | */ 307 | 308 | }, { 309 | key: "setActions", 310 | value: function setActions() { 311 | var actions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; 312 | 313 | if (Array.isArray(actions)) { 314 | this._actions = actions; 315 | } 316 | } 317 | /** 318 | * run actions, draw canvas 319 | */ 320 | 321 | }, { 322 | key: "draw", 323 | value: function draw() { 324 | var _this2 = this; 325 | 326 | var shouldRender = (!this._rendered ? true : !this._cache) && !!this._actions.length; 327 | if (!shouldRender) return; 328 | 329 | this._actions.forEach(function (_ref2) { 330 | var _ref3 = slicedToArray(_ref2, 3), 331 | type = _ref3[0], 332 | method = _ref3[1], 333 | args = _ref3[2]; 334 | 335 | var params = Array.prototype.slice.call(args); 336 | 337 | if (type === "method") { 338 | _this2._ctx[method].apply(_this2._ctx, params); 339 | } else { 340 | _this2._ctx[method] = params[0]; 341 | } 342 | }); 343 | if (!this._rendered) { 344 | this._rendered = true; 345 | } 346 | return this; 347 | } 348 | /** 349 | * set cache, default: true 350 | * 351 | * @param {Boolean} ifCache - if set cache 352 | */ 353 | 354 | }, { 355 | key: "cache", 356 | value: function cache() { 357 | var ifCache = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; 358 | 359 | if (typeof ifCache === "boolean") { 360 | this._cache = ifCache; 361 | } 362 | return this; 363 | } 364 | }]); 365 | return WeCanvas; 366 | }(); 367 | 368 | WeCanvas.hasGeneratored = false; 369 | 370 | /** 371 | * WeStage: Canvas manager for WeCanvas 372 | * 373 | */ 374 | 375 | var WeStage = function () { 376 | /** 377 | * create a WeStage instance 378 | * 379 | * @param {Canvas} canvas - a Canvas element related to the dom 380 | * @param {Object} options - stage settings 381 | */ 382 | function WeStage(canvas) { 383 | var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 384 | classCallCheck(this, WeStage); 385 | 386 | this._canvas = new WeCanvas({ 387 | canvas: canvas 388 | }).cache(false); 389 | this._children = []; 390 | this._updating = false; 391 | this._initOptions(options); 392 | } 393 | /** 394 | * init options for settings 395 | * 396 | * @private 397 | * @param {Boolean} options.clear - auto clear stage 398 | * @param {Number} options.ratio - zoom down level when draw child Canvas 399 | */ 400 | 401 | 402 | createClass(WeStage, [{ 403 | key: "_initOptions", 404 | value: function _initOptions(_ref) { 405 | var _ref$clear = _ref.clear, 406 | clear = _ref$clear === undefined ? true : _ref$clear, 407 | _ref$ratio = _ref.ratio, 408 | ratio = _ref$ratio === undefined ? 1 : _ref$ratio; 409 | 410 | if (typeof clear === "boolean") { 411 | this._clear = clear; 412 | } 413 | if (typeof ratio === "number") { 414 | this._ratio = ratio; 415 | } 416 | } 417 | /** 418 | * set stage size 419 | * 420 | * @param {Number} width - stage width 421 | * @param {Number} height - stage height 422 | */ 423 | 424 | }, { 425 | key: "setSize", 426 | value: function setSize(width, height) { 427 | this._canvas.setSize(width, height); 428 | this._canvas.setScale(this._ratio); 429 | } 430 | /** 431 | * set stage style 432 | * 433 | * @param {String} width - stage style width 434 | * @param {String} height - stage style height 435 | */ 436 | 437 | }, { 438 | key: "setStyle", 439 | value: function setStyle(width, height) { 440 | this._canvas.setStyle(width, height); 441 | } 442 | /** 443 | * destrory stage 444 | */ 445 | 446 | }, { 447 | key: "destory", 448 | value: function destory() { 449 | this._canvas = null; 450 | this._children.length = 0; 451 | } 452 | /** 453 | * clear canvas 454 | */ 455 | 456 | }, { 457 | key: "clear", 458 | value: function clear() { 459 | this._canvas.clear(); 460 | } 461 | 462 | /** 463 | * add child to the stage 464 | * 465 | * @param {WeCanvas|Object} child - WeCanvas instance or Object cotains Canvas 466 | */ 467 | 468 | }, { 469 | key: "addChild", 470 | value: function addChild(child) { 471 | if (child instanceof WeCanvas || child.canvas.getContext('2d')) { 472 | this._children.push(child); 473 | } 474 | } 475 | /** 476 | * translate stage, move coordinate 477 | * 478 | * @param {Number} x - offset x 479 | * @param {Number} y - offset y 480 | * @param {Boolean} reset - should reset after update 481 | */ 482 | 483 | }, { 484 | key: "translate", 485 | value: function translate() { 486 | var x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; 487 | var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; 488 | var reset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; 489 | 490 | if (typeof x === "number" && typeof y === "number") { 491 | this._coordinate = { 492 | x: x, 493 | y: y 494 | }; 495 | } 496 | if (typeof reset === "boolean") { 497 | this._translateReset = reset; 498 | } 499 | } 500 | /** 501 | * update stage, draw child on canvas 502 | * **run this method, all child canvas will draw** 503 | */ 504 | 505 | }, { 506 | key: "update", 507 | value: function update() { 508 | if (!this._children.length || this._updating) return; 509 | this._updating = true; 510 | if (this._clear) { 511 | this.clear(); 512 | } 513 | this._translateStage(); 514 | this._drawChildren(); 515 | this._resetCoordinate(); 516 | this._updateStage(); 517 | this._updating = false; 518 | this._children = []; 519 | } 520 | /** 521 | * translate stage 522 | * @private 523 | */ 524 | 525 | }, { 526 | key: "_translateStage", 527 | value: function _translateStage() { 528 | if (this._coordinate) { 529 | var _coordinate = this._coordinate, 530 | x = _coordinate.x, 531 | y = _coordinate.y; 532 | 533 | this._canvas.translate(x, y); 534 | } 535 | } 536 | /** 537 | * reset coordinate 538 | * @private 539 | */ 540 | 541 | }, { 542 | key: "_resetCoordinate", 543 | value: function _resetCoordinate() { 544 | if (this._translateReset) { 545 | var _coordinate2 = this._coordinate, 546 | x = _coordinate2.x, 547 | y = _coordinate2.y; 548 | 549 | this._canvas.translate(-x, -y); 550 | this._coordinate = null; 551 | } 552 | } 553 | /** 554 | * draw children 555 | * @private 556 | */ 557 | 558 | }, { 559 | key: "_drawChildren", 560 | value: function _drawChildren() { 561 | var _this = this; 562 | 563 | this._children.forEach(function (child) { 564 | if (child instanceof WeCanvas) { 565 | _this._drawChild(child); 566 | } else { 567 | _this._drawChildCanvas(child); 568 | } 569 | }); 570 | } 571 | /** 572 | * draw child, which is a instance of WeCanvas 573 | * 574 | * @private 575 | * @param {WeCanvas} child - a WeCanvas instance 576 | */ 577 | 578 | }, { 579 | key: "_drawChild", 580 | value: function _drawChild(child) { 581 | var canvas = child.canvas, 582 | x = child.x, 583 | y = child.y, 584 | width = child.width, 585 | height = child.height; 586 | 587 | child.draw(); 588 | this._canvas.drawImage(canvas, x, y, width / this._ratio, height / this._ratio); 589 | } 590 | /** 591 | * draw child, which is a raw Canvas element 592 | * 593 | * @private 594 | * @param {Number} options.x - horizontal axis 595 | * @param {Number} options.y - vertical axis 596 | * @param {Canvas} options.canvas - Canvas 597 | */ 598 | 599 | }, { 600 | key: "_drawChildCanvas", 601 | value: function _drawChildCanvas(_ref2) { 602 | var _ref2$x = _ref2.x, 603 | x = _ref2$x === undefined ? 0 : _ref2$x, 604 | _ref2$y = _ref2.y, 605 | y = _ref2$y === undefined ? 0 : _ref2$y, 606 | canvas = _ref2.canvas; 607 | 608 | this._canvas.drawImage(canvas, x, y, canvas.width / this._ratio, canvas.height / this._ratio); 609 | } 610 | /** 611 | * draw dom canvas 612 | * @private 613 | */ 614 | 615 | }, { 616 | key: "_updateStage", 617 | value: function _updateStage() { 618 | this._canvas.draw(); 619 | } 620 | }]); 621 | return WeStage; 622 | }(); 623 | 624 | exports.WeCanvas = WeCanvas; 625 | exports.WeStage = WeStage; 626 | 627 | Object.defineProperty(exports, '__esModule', { value: true }); 628 | 629 | }))); 630 | -------------------------------------------------------------------------------- /dist/weRender.min.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e(t.weRender=t.weRender||{})}(this,function(t){"use strict";var e=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},i=function(){function t(t,e){for(var i=0;i0&&void 0!==arguments[0]?arguments[0]:{},i=e.canvas,n=e.actions,a=e.width,r=e.height,s=e.x,o=e.y,h=e.methods,c=void 0===h?[]:h;this.canvas=i||document.createElement("canvas"),this._ctx=this.canvas.getContext("2d"),this._initMethods(t.hasGeneratored?c:t.generatorMethod().concat(c)),this.setSize(a,r),this.setCoordinate(s,o),this.setActions(n)}},{key:"_initMethods",value:function(){var t=this;(arguments.length>0&&void 0!==arguments[0]?arguments[0]:[]).reduce(function(e,i){return t[i]||(t._proxy(i),e[i]=!0),e},{})}},{key:"_proxy",value:function(e){var i=this._ctx[e];void 0!==i&&(t.prototype[e]=function(){for(var t=arguments.length,n=Array(t),a=0;a0&&void 0!==arguments[0]?arguments[0]:0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return this.x=t,this.y=e,this}},{key:"setScale",value:function(t){this._ctx.scale(t,t)}},{key:"clear",value:function(){this._ctx.clearRect(0,0,this.width,this.height),this.setActions([])}},{key:"getActions",value:function(){return this._actions}},{key:"setActions",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];Array.isArray(t)&&(this._actions=t)}},{key:"draw",value:function(){var t=this;if(!(this._rendered&&this._cache||!this._actions.length))return this._actions.forEach(function(e){var i=n(e,3),a=i[0],r=i[1],s=i[2],o=Array.prototype.slice.call(s);"method"===a?t._ctx[r].apply(t._ctx,o):t._ctx[r]=o[0]}),this._rendered||(this._rendered=!0),this}},{key:"cache",value:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];return"boolean"==typeof t&&(this._cache=t),this}}]),t}();a.hasGeneratored=!1;var r=function(){function t(i){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};e(this,t),this._canvas=new a({canvas:i}).cache(!1),this._children=[],this._updating=!1,this._initOptions(n)}return i(t,[{key:"_initOptions",value:function(t){var e=t.clear,i=void 0===e||e,n=t.ratio,a=void 0===n?1:n;"boolean"==typeof i&&(this._clear=i),"number"==typeof a&&(this._ratio=a)}},{key:"setSize",value:function(t,e){this._canvas.setSize(t,e),this._canvas.setScale(this._ratio)}},{key:"setStyle",value:function(t,e){this._canvas.setStyle(t,e)}},{key:"destory",value:function(){this._canvas=null,this._children.length=0}},{key:"clear",value:function(){this._canvas.clear()}},{key:"addChild",value:function(t){(t instanceof a||t.canvas.getContext("2d"))&&this._children.push(t)}},{key:"translate",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,i=arguments.length>2&&void 0!==arguments[2]&&arguments[2];"number"==typeof t&&"number"==typeof e&&(this._coordinate={x:t,y:e}),"boolean"==typeof i&&(this._translateReset=i)}},{key:"update",value:function(){this._children.length&&!this._updating&&(this._updating=!0,this._clear&&this.clear(),this._translateStage(),this._drawChildren(),this._resetCoordinate(),this._updateStage(),this._updating=!1,this._children=[])}},{key:"_translateStage",value:function(){if(this._coordinate){var t=this._coordinate,e=t.x,i=t.y;this._canvas.translate(e,i)}}},{key:"_resetCoordinate",value:function(){if(this._translateReset){var t=this._coordinate,e=t.x,i=t.y;this._canvas.translate(-e,-i),this._coordinate=null}}},{key:"_drawChildren",value:function(){var t=this;this._children.forEach(function(e){e instanceof a?t._drawChild(e):t._drawChildCanvas(e)})}},{key:"_drawChild",value:function(t){var e=t.canvas,i=t.x,n=t.y,a=t.width,r=t.height;t.draw(),this._canvas.drawImage(e,i,n,a/this._ratio,r/this._ratio)}},{key:"_drawChildCanvas",value:function(t){var e=t.x,i=void 0===e?0:e,n=t.y,a=void 0===n?0:n,r=t.canvas;this._canvas.drawImage(r,i,a,r.width/this._ratio,r.height/this._ratio)}},{key:"_updateStage",value:function(){this._canvas.draw()}}]),t}();t.WeCanvas=a,t.WeStage=r,Object.defineProperty(t,"__esModule",{value:!0})}); 2 | -------------------------------------------------------------------------------- /examples/circle-actions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | circle 5 | 6 | 18 | 19 | 20 |
21 | 22 |
23 | 24 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /examples/circle-clone.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | circle 5 | 6 | 18 | 19 | 20 |
21 | 22 |
23 | 24 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /examples/circle-composite.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | circle 5 | 6 | 18 | 19 | 20 |
21 | 22 |
23 | 24 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /examples/circle-zoom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | scroll 5 | 6 | 15 | 16 | 17 |
18 | 19 |
20 | 21 | 22 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | circle 5 | 6 | 18 | 19 | 20 |
21 | 22 |
23 |

other examples:

24 | 30 |
31 |
32 | 33 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /examples/lib/weRender.min.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e(t.weRender=t.weRender||{})}(this,function(t){"use strict";var e=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},i=function(){function t(t,e){for(var i=0;i0&&void 0!==arguments[0]?arguments[0]:{},i=e.canvas,n=e.actions,a=e.width,r=e.height,s=e.x,o=e.y,h=e.methods,c=void 0===h?[]:h;this.canvas=i||document.createElement("canvas"),this._ctx=this.canvas.getContext("2d"),this._initMethods(t.hasGeneratored?c:t.generatorMethod().concat(c)),this.setSize(a,r),this.setCoordinate(s,o),this.setActions(n)}},{key:"_initMethods",value:function(){var t=this;(arguments.length>0&&void 0!==arguments[0]?arguments[0]:[]).reduce(function(e,i){return t[i]||(t._proxy(i),e[i]=!0),e},{})}},{key:"_proxy",value:function(e){var i=this._ctx[e];void 0!==i&&(t.prototype[e]=function(){for(var t=arguments.length,n=Array(t),a=0;a0&&void 0!==arguments[0]?arguments[0]:0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return this.x=t,this.y=e,this}},{key:"setScale",value:function(t){this._ctx.scale(t,t)}},{key:"clear",value:function(){this._ctx.clearRect(0,0,this.width,this.height),this.setActions([])}},{key:"getActions",value:function(){return this._actions}},{key:"setActions",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];Array.isArray(t)&&(this._actions=t)}},{key:"draw",value:function(){var t=this;if(!(this._rendered&&this._cache||!this._actions.length))return this._actions.forEach(function(e){var i=n(e,3),a=i[0],r=i[1],s=i[2],o=Array.prototype.slice.call(s);"method"===a?t._ctx[r].apply(t._ctx,o):t._ctx[r]=o[0]}),this._rendered||(this._rendered=!0),this}},{key:"cache",value:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];return"boolean"==typeof t&&(this._cache=t),this}}]),t}();a.hasGeneratored=!1;var r=function(){function t(i){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};e(this,t),this._canvas=new a({canvas:i}).cache(!1),this._children=[],this._updating=!1,this._initOptions(n)}return i(t,[{key:"_initOptions",value:function(t){var e=t.clear,i=void 0===e||e,n=t.ratio,a=void 0===n?1:n;"boolean"==typeof i&&(this._clear=i),"number"==typeof a&&(this._ratio=a)}},{key:"setSize",value:function(t,e){this._canvas.setSize(t,e),this._canvas.setScale(this._ratio)}},{key:"setStyle",value:function(t,e){this._canvas.setStyle(t,e)}},{key:"destory",value:function(){this._canvas=null,this._children.length=0}},{key:"clear",value:function(){this._canvas.clear()}},{key:"addChild",value:function(t){(t instanceof a||t.canvas.getContext("2d"))&&this._children.push(t)}},{key:"translate",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,i=arguments.length>2&&void 0!==arguments[2]&&arguments[2];"number"==typeof t&&"number"==typeof e&&(this._coordinate={x:t,y:e}),"boolean"==typeof i&&(this._translateReset=i)}},{key:"update",value:function(){this._children.length&&!this._updating&&(this._updating=!0,this._clear&&this.clear(),this._translateStage(),this._drawChildren(),this._resetCoordinate(),this._updateStage(),this._updating=!1,this._children=[])}},{key:"_translateStage",value:function(){if(this._coordinate){var t=this._coordinate,e=t.x,i=t.y;this._canvas.translate(e,i)}}},{key:"_resetCoordinate",value:function(){if(this._translateReset){var t=this._coordinate,e=t.x,i=t.y;this._canvas.translate(-e,-i),this._coordinate=null}}},{key:"_drawChildren",value:function(){var t=this;this._children.forEach(function(e){e instanceof a?t._drawChild(e):t._drawChildCanvas(e)})}},{key:"_drawChild",value:function(t){var e=t.canvas,i=t.x,n=t.y,a=t.width,r=t.height;t.draw(),this._canvas.drawImage(e,i,n,a/this._ratio,r/this._ratio)}},{key:"_drawChildCanvas",value:function(t){var e=t.x,i=void 0===e?0:e,n=t.y,a=void 0===n?0:n,r=t.canvas;this._canvas.drawImage(r,i,a,r.width/this._ratio,r.height/this._ratio)}},{key:"_updateStage",value:function(){this._canvas.draw()}}]),t}();t.WeCanvas=a,t.WeStage=r,Object.defineProperty(t,"__esModule",{value:!0})}); 2 | -------------------------------------------------------------------------------- /examples/lib/weScroll.min.js: -------------------------------------------------------------------------------- 1 | !function(t,i){"object"==typeof exports&&"undefined"!=typeof module?module.exports=i():"function"==typeof define&&define.amd?define(i):t.WeScroll=i()}(this,function(){"use strict";function t(t,i,e,s){t.addEventListener(i,e,!!s)}function i(t,i,e,s){t.removeEventListener(i,e,!!s)}function e(t,i){var e=document.createEvent("Event");e.initEvent(i,!0,!0),t=t.changedTouches[0]||t,e.pageX=t.pageX,e.pageY=t.pageY,t.target.dispatchEvent(e)}var s=void 0;s="function"!=typeof Object.assign?function(t){if(void 0===t||null===t)throw new TypeError("Cannot convert undefined or null to object");for(var i=Object(t),e=1;e-1&&this._events[t].splice(e,1)}}},{key:"trigger",value:function(t){if(this._events[t]){var i=0,e=this._events[t].length;if(e)for(;i300&&h<10&&a<10||(this.directionLocked||(h>a+this.options.directionLockThreshold?this.directionLocked="h":a>=h+this.options.directionLockThreshold?this.directionLocked="v":this.directionLocked="n"),"h"===this.directionLocked?s=0:"v"===this.directionLocked&&(e=0),n=this.x+e,r=this.y+s,(n>0||n0?0:this.maxScrollX),(r>0||r0?0:this.maxScrollY),this.directionX=e>0?-1:e<0?1:0,this.directionY=s>0?-1:s<0?1:0,this.moved||this.observer.trigger("scrollStart"),o-this.startTime>300&&(this.startTime=o,this.startX=this.x,this.startY=this.y),this._render(n,r),this.moved=!0)}}},{key:"_end",value:function(t){if(!this._ticking&&this.enabled){this.options.preventDefault&&t.preventDefault();var i=Math.round(this.x),s=Math.round(this.y),o="";if(this.endTime=Date.now(),!this.resetPosition(this.options.bounceTime))return this._scrollTo(i,s),this.moved?i!==this.x||s!==this.y?((i>this.options.marginLeft||ithis.options.marginTop||sthis.options.marginLeft?e=this.options.marginLeft:ethis.options.marginTop?s=this.options.marginTop:s0&&void 0!==arguments[0]?arguments[0]:0,i=this._adjustPosition(this.x,this.y),e=u(i,2),s=e[0],o=e[1];return(s!==this.x||o!==this.y)&&(this._scrollTo(s,o,t,h.circular),!0)}},{key:"disable",value:function(){this.enabled=!1}},{key:"enable",value:function(){this.enabled=!0}},{key:"refresh",value:function(){this.wrapperWidth=this.wrapper.clientWidth,this.wrapperHeight=this.wrapper.clientHeight,this.scrollerWidth=Math.round(this.options.contentWidth*this.scale),this.scrollerHeight=Math.round(this.options.contentHeight*this.scale),this.maxScrollX=this.wrapperWidth-this.scrollerWidth-this.options.marginRight,this.maxScrollY=Math.min(this.options.marginTop,this.wrapperHeight-this.scrollerHeight-this.options.marginBottom),this.endTime=0,this.directionX=0,this.directionY=0,this.wrapperOffset=p(this.wrapper),this.observer.trigger("refresh")}},{key:"_render",value:function(t,i){if(!this._ticking){var e=function(){this.options.render(t,i,this.scale),this.x=t,this.y=i,this._ticking=!1};if(!this._ticking){var s=e.bind(this);d(s),this._ticking=!0}}}},{key:"_zoomStart",value:function(t){var i=Math.abs(t.touches[0].pageX-t.touches[1].pageX),e=Math.abs(t.touches[0].pageY-t.touches[1].pageY);this.touchesDistanceStart=Math.sqrt(i*i+e*e),this.startScale=this.scale,this.originX=Math.abs(t.touches[0].pageX+t.touches[1].pageX)/2+this.wrapperOffset.left-this.x,this.originY=Math.abs(t.touches[0].pageY+t.touches[1].pageY)/2+this.wrapperOffset.top-this.y,this.observer.trigger("zoomStart")}},{key:"_zoom",value:function(t){if(this.enabled){this.options.preventDefault&&t.preventDefault();var i=Math.abs(t.touches[0].pageX-t.touches[1].pageX),e=Math.abs(t.touches[0].pageY-t.touches[1].pageY),s=Math.sqrt(i*i+e*e),o=1/this.touchesDistanceStart*s*this.startScale,n=void 0,r=void 0,h=void 0;this.scaled=!0,othis.options.zoomMax&&(o=2*this.options.zoomMax*Math.pow(.5,this.options.zoomMax/o)),n=o/this.startScale,r=this.originX-this.originX*n+this.startX,h=this.originY-this.originY*n+this.startY,this.scale=o,this._scrollTo(r,h,0)}}},{key:"_zoomEnd",value:function(t){if(this.enabled){this.options.preventDefault&&t.preventDefault(),this.initiated=0,this.scale>this.options.zoomMax?this.scale=this.options.zoomMax:this.scale=c)return n.isAnimating=!1,void n._render(t,i);u=(u-a)/e,f=s(u),l=(t-r)*f+r,p=(i-h)*f+h,n._render(l,p),n.isAnimating&&d(o)}var n=this,r=this.x,h=this.y,a=Date.now(),c=a+e;this.isAnimating=!0,o()}},{key:"_scrollTo",value:function(t,i,e,s){s=s||h.circular,e?this._animate(t,i,e,s):this._render(t,i)}},{key:"scrollTo",value:function(t,i,e,s){e=void 0===e?this.options.duration:e,s=s||h.circular,t=-t*this.scale+this.wrapperWidth/2,i=-i*this.scale+this.wrapperHeight/2;var o=this._adjustPosition(t,i),n=u(o,2),r=n[0],a=n[1];this._scrollTo(r,a,e,s)}},{key:"zoom",value:function(t,i,e,s){function o(){var h=Date.now(),l=void 0,p=void 0,f=void 0,v=void 0;if(h>=c)return n.isAnimating=!1,n.scale=t,n.refresh(),f=n._getDestinationPosition(i,e),void n._render(f.x,f.y);h=(h-a)/s,v=u(h),p=(t-r)*v+r,n.scale=p,n.refresh(),l=n._getDestinationPosition(i,e),n._render(l.x,l.y),n.isAnimating&&d(o)}if(tthis.options.zoomMax&&(t=this.options.zoomMax),t!==this.scale){s=void 0===s?300:s;var n=this,r=n.scale,a=Date.now(),c=a+s,u=h.circular;this.isAnimating=!0,o()}}},{key:"_initEvents",value:function(e){var s=e?i:t,o=this.options.bindToWrapper?this.wrapper:window,r=this._handleEvent.bind(this);if(s(window,"resize",r),this.options.click&&s(this.wrapper,"click",r,!0),!this.options.disableMouse){s(this.wrapper,"mousedown",r);["mousemove","mousecancel","mouseup"].forEach(function(t){s(o,t,r)})}if(n&&!this.options.disableTouch){s(this.wrapper,"touchstart",r);["touchmove","touchcancel","touchend"].forEach(function(t){s(o,t,r)})}}},{key:"_handleEvent",value:function(t){switch(t.type){case"touchstart":case"pointerdown":case"MSPointerDown":case"mousedown":this._start(t),this.options.zoom&&t.touches&&t.touches.length>1&&this._zoomStart(t);break;case"touchmove":case"pointermove":case"MSPointerMove":case"mousemove":if(this.options.zoom&&t.touches&&t.touches[1])return void this._zoom(t);this._move(t);break;case"touchend":case"pointerup":case"MSPointerUp":case"mouseup":case"touchcancel":case"pointercancel":case"MSPointerCancel":case"mousecancel":if(this.scaled)return void this._zoomEnd(t);this._end(t);break;case"orientationchange":case"resize":this._resize()}}}]),s}()}); 2 | -------------------------------------------------------------------------------- /examples/path/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | circle 5 | 6 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
33 | 34 | 35 | 36 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /examples/path/money.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weiying-shenzhen/weRender/d4302193dbd0fd016dbbb81b90cfe5b2ecb49e62/examples/path/money.png -------------------------------------------------------------------------------- /examples/path/seat.draw.js: -------------------------------------------------------------------------------- 1 | new WeCanvas({ width: 56, height: 56 }) 2 | .translate(10.000000, 10.000000) 3 | .beginPath() 4 | .fillStyle('#D8D8D8') 5 | .moveTo(33,34) 6 | .lineTo(33,34) 7 | .bezierCurveTo(35,34,36,33,36,31) 8 | .lineTo(36,18) 9 | .bezierCurveTo(36,16,35,15,33,15) 10 | .lineTo(33,15) 11 | .bezierCurveTo(33,7,26,0,18,0) 12 | .bezierCurveTo(10,0,3,7,3,15) 13 | .lineTo(3,15) 14 | .bezierCurveTo(1,15,0,16,0,18) 15 | .lineTo(0,31) 16 | .bezierCurveTo(0,33,1,34,3,34) 17 | .lineTo(3,34) 18 | .bezierCurveTo(3,35,4,36,5,36) 19 | .lineTo(31,36) 20 | .bezierCurveTo(32,36,33,35,33,34) 21 | .closePath() 22 | .fill() -------------------------------------------------------------------------------- /examples/path/seat.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /examples/path/seatCanvas.js: -------------------------------------------------------------------------------- 1 | var cache = {} 2 | 3 | function getSeatCanvas(scale, fillStyle, type, image) { 4 | var type = type || "normal" 5 | var key = type + scale + ":" + fillStyle 6 | var canvas = cache[key] 7 | 8 | if (!canvas) { 9 | canvas = drawSeat(scale, fillStyle, type, image) 10 | cache[key] = canvas 11 | } 12 | 13 | return canvas 14 | } 15 | 16 | function drawSeat(scale, fillStyle, type, image) { 17 | var seat = new WeCanvas({ 18 | width: 56 * scale, 19 | height: 56 * scale 20 | }) 21 | .scale(scale, scale) 22 | .translate(10, 10) 23 | .beginPath() 24 | .fillStyle(fillStyle) 25 | .moveTo(33,34) 26 | .lineTo(33,34) 27 | .bezierCurveTo(35,34,36,33,36,31) 28 | .lineTo(36,18) 29 | .bezierCurveTo(36,16,35,15,33,15) 30 | .lineTo(33,15) 31 | .bezierCurveTo(33,7,26,0,18,0) 32 | .bezierCurveTo(10,0,3,7,3,15) 33 | .lineTo(3,15) 34 | .bezierCurveTo(1,15,0,16,0,18) 35 | .lineTo(0,31) 36 | .bezierCurveTo(0,33,1,34,3,34) 37 | .lineTo(3,34) 38 | .bezierCurveTo(3,35,4,36,5,36) 39 | .lineTo(31,36) 40 | .bezierCurveTo(32,36,33,35,33,34) 41 | .lineTo(33,34) 42 | .closePath() 43 | .fill() 44 | 45 | if (image) { 46 | if (type == "money") { 47 | seat.drawImage(image, 9, 10, 18, 18) 48 | } else { 49 | seat.drawImage(image, 11, 11, 14, 16) 50 | } 51 | } 52 | 53 | return seat.draw() 54 | } -------------------------------------------------------------------------------- /examples/path/smile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weiying-shenzhen/weRender/d4302193dbd0fd016dbbb81b90cfe5b2ecb49e62/examples/path/smile.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "we-render", 3 | "version": "1.0.2", 4 | "description": "canvas render engine", 5 | "main": "dist/weRender.js", 6 | "scripts": { 7 | "build": "rollup -c build/es5.config.js", 8 | "build:es6": "rollup -c build/es6.config.js", 9 | "pro": "NODE_ENV=production rollup -c build/es5.config.js", 10 | "watch": "rollup -c build/es5.config.js -w", 11 | "update": "npm run build:es6 && npm run build && npm run pro && cp dist/weRender.min.js examples/lib", 12 | "static": "cd examples && python -m SimpleHTTPServer 8000", 13 | "doc": "./node_modules/.bin/jsdoc2md -t build/README.template.md -f src/*.js > README.md" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/weiying-shenzhen/weRender.git" 18 | }, 19 | "keywords": [ 20 | "canvas", 21 | "2d", 22 | "render", 23 | "easy", 24 | "light-weight", 25 | "engine" 26 | ], 27 | "author": "henryluki", 28 | "license": "MIT", 29 | "bugs": { 30 | "url": "https://github.com/weiying-shenzhen/weRender/issues" 31 | }, 32 | "homepage": "https://github.com/weiying-shenzhen/weRender#readme", 33 | "devDependencies": { 34 | "babel-eslint": "^7.2.2", 35 | "babel-plugin-external-helpers": "^6.22.0", 36 | "babel-plugin-transform-class-properties": "^6.24.1", 37 | "babel-preset-latest": "^6.24.1", 38 | "eslint": "^3.19.0", 39 | "eslint-config-airbnb-base": "^11.1.3", 40 | "eslint-config-react-app": "^0.6.2", 41 | "eslint-plugin-flowtype": "^2.30.4", 42 | "eslint-plugin-import": "^2.2.0", 43 | "eslint-plugin-jsx-a11y": "^4.0.0", 44 | "eslint-plugin-react": "^6.10.3", 45 | "jsdoc-to-markdown": "^3.0.0", 46 | "rollup-plugin-babel": "^2.7.1", 47 | "rollup-plugin-eslint": "^3.0.0", 48 | "rollup-plugin-node-resolve": "^3.0.0", 49 | "rollup-plugin-uglify": "^1.0.1", 50 | "rollup-watch": "^3.2.2" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import WeCanvas from './weCanvas' 2 | import WeStage from './weStage' 3 | 4 | /** 5 | * weRender: Simple Canvas library for easy usage 6 | * 7 | * @module weRender 8 | */ 9 | export { 10 | WeCanvas, 11 | WeStage 12 | } 13 | -------------------------------------------------------------------------------- /src/weCanvas.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WeCanvas: Easy canvas api for using, support useing chain 3 | * 4 | * - Directly use CanvasRenderingContext2D` methods` 5 | * - `Property` of `CanvasRenderingContext2D` here is `method` 6 | * - Won't really drawing Canvas until run `draw()` 7 | * 8 | */ 9 | class WeCanvas { 10 | static hasGeneratored = false 11 | static generatorMethod() { 12 | WeCanvas.hasGeneratored = true 13 | 14 | const retangles = ["clearRect", "fillRect", "strokeRect"] 15 | const text = ["fillText", "strokeText", "measureText"] 16 | const lineStyles = ["lineWidth", "lineCap", "lineJoin"] 17 | const textStyles = ["font", "textAlgin", "textBaseline", "direction"] 18 | const fillStrokeStyles = ["fillStyle", "strokeStyle"] 19 | const paths = ["beginPath", "closePath", "moveTo", "lineTo", "bezierCurveTo", "quadraticCurveTo", "arc", "arcTo", "rect"] 20 | const pathsDrawing = ["fill", "stroke"] 21 | const transformations = ["rotate", "scale", "translate", "transform", "resetTransform"] 22 | const images = ["drawImage"] 23 | const pixel = ["createImageData", "getImageData", "putImageData"] 24 | const state = ["save", "restore"] 25 | const shadow = ['shadowColor', 'shadowBlur'] 26 | const _methods = [].concat( 27 | retangles, 28 | text, 29 | lineStyles, 30 | textStyles, 31 | fillStrokeStyles, 32 | paths, 33 | pathsDrawing, 34 | transformations, 35 | images, 36 | pixel, 37 | state, 38 | shadow, 39 | ) 40 | return _methods 41 | } 42 | /** 43 | * create a WeCanvas instance 44 | * 45 | * @param {Object} options - option settions for instance 46 | */ 47 | constructor(options) { 48 | this._rendered = false 49 | this._init(options) 50 | this.width = this.canvas.width 51 | this.height = this.canvas.height 52 | } 53 | /** 54 | * init 55 | * 56 | * @private 57 | * @param {Canvas} options.canvas - canvas element 58 | * @param {Array} options.actions - context drawing actions 59 | * @param {Number} options.width - canvas witdh 60 | * @param {Number} options.height - canvas height 61 | * @param {Number} options.x - horizontal axis 62 | * @param {Number} options.y - vertical axis 63 | * @param {Array} options.methods - methods for context 64 | */ 65 | _init({ 66 | canvas, 67 | actions, 68 | width, 69 | height, 70 | x, 71 | y, 72 | methods = [], 73 | } = {}) { 74 | this.canvas = canvas || document.createElement('canvas') 75 | this._ctx = this.canvas.getContext('2d') 76 | this._initMethods(WeCanvas.hasGeneratored ? methods : WeCanvas.generatorMethod().concat(methods)) 77 | this.setSize(width, height) 78 | this.setCoordinate(x, y) 79 | this.setActions(actions) 80 | } 81 | /** 82 | * reduce context methods that need to be bind 83 | * 84 | * @private 85 | * @param {Array} methods - context methods 86 | */ 87 | _initMethods(methods = []) { 88 | methods.reduce((hash, method) => { 89 | if (!this[method]) { 90 | this._proxy(method) 91 | hash[method] = true 92 | } 93 | return hash 94 | }, {}) 95 | } 96 | /** 97 | * bind context method or property to this instance 98 | * 99 | * @private 100 | * @param {String} method - context method or property 101 | */ 102 | _proxy(method) { 103 | const prop = this._ctx[method] 104 | if (prop === undefined) return 105 | WeCanvas.prototype[method] = function(...args) { 106 | this._actions.push([ 107 | Object.prototype.toString.call(prop) === "[object Function]" ? "method" : "property", 108 | method, 109 | args 110 | ]) 111 | return this 112 | } 113 | } 114 | /** 115 | * set canvas size 116 | * 117 | * @param {Number} width - canvas width 118 | * @param {Number} height - canvas height 119 | */ 120 | setSize(width, height) { 121 | if (width && width !== this.width) { 122 | this.canvas.width = width 123 | this.width = width 124 | } 125 | if (height && height !== this.height) { 126 | this.canvas.height = height 127 | this.height = height 128 | } 129 | return this 130 | } 131 | /** 132 | * set canvas style, only width and height 133 | * 134 | * @param {String} width - canvas style width 135 | * @param {String} height - canvas style height 136 | */ 137 | setStyle(width, height) { 138 | if (width) { 139 | this.canvas.style.width = width 140 | } 141 | if (height) { 142 | this.canvas.style.height = height 143 | } 144 | return this 145 | } 146 | /** 147 | * set coordinate of stage 148 | * 149 | * @param {Number} x - horizontal axis 150 | * @param {Number} y - vertical axis 151 | */ 152 | setCoordinate(x = 0, y = 0) { 153 | this.x = x 154 | this.y = y 155 | return this 156 | } 157 | /** 158 | * set init scale 159 | * 160 | * @param {Number} scale - scale ratio 161 | */ 162 | setScale(scale) { 163 | this._ctx.scale(scale, scale) 164 | } 165 | /** 166 | * clear canvas 167 | */ 168 | clear() { 169 | this._ctx.clearRect(0, 0, this.width, this.height) 170 | this.setActions([]) 171 | } 172 | /** 173 | * get actions for context drawing 174 | */ 175 | getActions() { 176 | return this._actions 177 | } 178 | /** 179 | * set actions 180 | * 181 | * @param {Array} actions - actions for context drawing 182 | */ 183 | setActions(actions = []) { 184 | if (Array.isArray(actions)) { 185 | this._actions = actions 186 | } 187 | } 188 | /** 189 | * run actions, draw canvas 190 | */ 191 | draw() { 192 | const shouldRender = ((!this._rendered ? true : !this._cache) && !!this._actions.length) 193 | if (!shouldRender) return 194 | 195 | this._actions.forEach(([ 196 | type, 197 | method, 198 | args 199 | ]) => { 200 | const params = Array.prototype.slice.call(args) 201 | 202 | if (type === "method") { 203 | this._ctx[method].apply(this._ctx, params) 204 | } else { 205 | this._ctx[method] = params[0] 206 | } 207 | }) 208 | if (!this._rendered) { 209 | this._rendered = true 210 | } 211 | return this 212 | } 213 | /** 214 | * set cache, default: true 215 | * 216 | * @param {Boolean} ifCache - if set cache 217 | */ 218 | cache(ifCache = true) { 219 | if (typeof ifCache === "boolean") { 220 | this._cache = ifCache 221 | } 222 | return this 223 | } 224 | } 225 | 226 | export default WeCanvas 227 | -------------------------------------------------------------------------------- /src/weStage.js: -------------------------------------------------------------------------------- 1 | import WeCanvas from './weCanvas' 2 | /** 3 | * WeStage: Canvas manager for WeCanvas 4 | * 5 | */ 6 | class WeStage { 7 | /** 8 | * create a WeStage instance 9 | * 10 | * @param {Canvas} canvas - a Canvas element related to the dom 11 | * @param {Object} options - stage settings 12 | */ 13 | constructor(canvas, options = {}) { 14 | this._canvas = new WeCanvas({ 15 | canvas 16 | }).cache(false) 17 | this._children = [] 18 | this._updating = false 19 | this._initOptions(options) 20 | } 21 | /** 22 | * init options for settings 23 | * 24 | * @private 25 | * @param {Boolean} options.clear - auto clear stage 26 | * @param {Number} options.ratio - zoom down level when draw child Canvas 27 | */ 28 | _initOptions({ 29 | clear = true, 30 | ratio = 1 31 | }) { 32 | if (typeof clear === "boolean") { 33 | this._clear = clear 34 | } 35 | if (typeof ratio === "number") { 36 | this._ratio = ratio 37 | } 38 | } 39 | /** 40 | * set stage size 41 | * 42 | * @param {Number} width - stage width 43 | * @param {Number} height - stage height 44 | */ 45 | setSize(width, height) { 46 | this._canvas.setSize(width, height) 47 | this._canvas.setScale(this._ratio) 48 | } 49 | /** 50 | * set stage style 51 | * 52 | * @param {String} width - stage style width 53 | * @param {String} height - stage style height 54 | */ 55 | setStyle(width, height) { 56 | this._canvas.setStyle(width, height) 57 | } 58 | /** 59 | * destrory stage 60 | */ 61 | destory() { 62 | this._canvas = null 63 | this._children.length = 0 64 | } 65 | /** 66 | * clear canvas 67 | */ 68 | clear() { 69 | this._canvas.clear() 70 | } 71 | 72 | /** 73 | * add child to the stage 74 | * 75 | * @param {WeCanvas|Object} child - WeCanvas instance or Object cotains Canvas 76 | */ 77 | addChild(child) { 78 | if (child instanceof WeCanvas || child.canvas.getContext('2d')) { 79 | this._children.push(child) 80 | } 81 | } 82 | /** 83 | * translate stage, move coordinate 84 | * 85 | * @param {Number} x - offset x 86 | * @param {Number} y - offset y 87 | * @param {Boolean} reset - should reset after update 88 | */ 89 | translate(x = 0, y = 0, reset = false) { 90 | if (typeof x === "number" && typeof y === "number") { 91 | this._coordinate = { 92 | x, 93 | y 94 | } 95 | } 96 | if (typeof reset === "boolean") { 97 | this._translateReset = reset 98 | } 99 | } 100 | /** 101 | * update stage, draw child on canvas 102 | * **run this method, all child canvas will draw** 103 | */ 104 | update() { 105 | if (!this._children.length || this._updating) return 106 | this._updating = true 107 | if (this._clear) { 108 | this.clear() 109 | } 110 | this._translateStage() 111 | this._drawChildren() 112 | this._resetCoordinate() 113 | this._updateStage() 114 | this._updating = false 115 | this._children = [] 116 | } 117 | /** 118 | * translate stage 119 | * @private 120 | */ 121 | _translateStage() { 122 | if (this._coordinate) { 123 | const { 124 | x, 125 | y 126 | } = this._coordinate 127 | this._canvas.translate(x, y) 128 | } 129 | } 130 | /** 131 | * reset coordinate 132 | * @private 133 | */ 134 | _resetCoordinate() { 135 | if (this._translateReset) { 136 | const { 137 | x, 138 | y 139 | } = this._coordinate 140 | this._canvas.translate(-x, -y) 141 | this._coordinate = null 142 | } 143 | } 144 | /** 145 | * draw children 146 | * @private 147 | */ 148 | _drawChildren() { 149 | this._children.forEach((child) => { 150 | if (child instanceof WeCanvas) { 151 | this._drawChild(child) 152 | } else { 153 | this._drawChildCanvas(child) 154 | } 155 | }) 156 | } 157 | /** 158 | * draw child, which is a instance of WeCanvas 159 | * 160 | * @private 161 | * @param {WeCanvas} child - a WeCanvas instance 162 | */ 163 | _drawChild(child) { 164 | const { 165 | canvas, 166 | x, 167 | y, 168 | width, 169 | height 170 | } = child 171 | child.draw() 172 | this._canvas.drawImage(canvas, x, y, width / this._ratio, height / this._ratio) 173 | } 174 | /** 175 | * draw child, which is a raw Canvas element 176 | * 177 | * @private 178 | * @param {Number} options.x - horizontal axis 179 | * @param {Number} options.y - vertical axis 180 | * @param {Canvas} options.canvas - Canvas 181 | */ 182 | _drawChildCanvas({ 183 | x = 0, 184 | y = 0, 185 | canvas 186 | }) { 187 | this._canvas.drawImage(canvas, x, y, canvas.width / this._ratio, canvas.height / this._ratio) 188 | } 189 | /** 190 | * draw dom canvas 191 | * @private 192 | */ 193 | _updateStage() { 194 | this._canvas.draw() 195 | } 196 | } 197 | 198 | export default WeStage --------------------------------------------------------------------------------