1 |
- {"version":3,"sources":["index.js","src/index.js","src/util.js"],"names":[],"mappings":";;;;;;;AAAA;AACA;AACA;ACFA;AACA;AACA;AACA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"index.js","sourcesContent":["var __TEMP__ = require('./src/index');var Wxml2Canvas = __REQUIRE_DEFAULT__(__TEMP__);\n\nif (!exports.__esModule) Object.defineProperty(exports, \"__esModule\", { value: true });exports.default = Wxml2Canvas;","var __TEMP__ = require('./util');var Util = __REQUIRE_DEFAULT__(__TEMP__);\n\nconst imageMode = ['scaleToFill', 'aspectFit', 'aspectFill', 'widthFix', 'top', 'bottom', 'center', 'left', 'right', 'top left', 'top right', 'bottom left', 'bottom right']\n\nclass Wxml2Canvas {\n constructor (options = {}) {\n this.device = wx.getSystemInfoSync && wx.getSystemInfoSync() || {};\n \n if (!options.zoom) {\n this.zoom = this.device.windowWidth / 375;\n } else {\n this.zoom = options.zoom || 1;\n } \n \n this.element = options.element;\n this.object = options.obj;\n this.width = options.width * this.zoom || 0;\n this.height = options.height * this.zoom || 0;\n this.destZoom = options.destZoom || 3;\n this.destWidth = this.width * this.destZoom;\n this.destHeight = this.height * this.destZoom;\n this.translateX = options.translateX * this.zoom || 0;\n this.translateY = options.translateY * this.zoom || 0;\n this.gradientBackground = options.gradientBackground || null;\n this.background = options.background || '#ffffff';\n this.finishDraw = options.finish || function finish(params) {}\n this.errorHandler = options.error || function error(params) {}\n this.progress = options.progress || function progress(params) {}\n this.textAlign = options.textAlign || 'left';\n this.fullText = options.fullText || false;\n this.font = options.font || '14px PingFang SC';\n\n this._init();\n }\n\n draw (data = {}, that) {\n let self = this;\n this.data = data;\n this.fef = that;\n\n this.progress(10);\n this._preloadImage(data.list).then((result) => {\n this.progress(30);\n self._draw();\n }).catch((res) => {\n self.errorHandler(res);\n })\n }\n\n measureWidth (text, font) {\n if(font) {\n this.ctx.font = font;\n }\n let res = this.ctx.measureText(text) || {};\n return res.width || 0;\n }\n\n _init () {\n this.progressPercent = 0; // 绘制进度百分比\n this.data = null;\n this.ref = null;\n this.allPic = [];\n this.screenList = []; \n this.asyncList = [];\n this.imgUrl = '';\n this.progressPercent = 0;\n this.distance = 0;\n this.progress(0);\n\n this.ctx = wx.createCanvasContext(this.element, this.obj);\n this.ctx.font = this.font;\n this.ctx.setTextBaseline('top');\n this.ctx.setStrokeStyle('white');\n\n this.debug = this.device.platform === 'devtools' ? true : false;\n\n this._drawBakcground();\n }\n\n _drawBakcground () {\n if (this.gradientBackground) {\n let line = this.gradientBackground.line || [0, 0, 0, this.height];\n let color = this.gradientBackground.color || ['#fff', '#fff'];\n let style = { fill: { line, color } }\n this._drawRectToCanvas(0, 0, this.width, this.height, style);\n } else {\n let style = { fill: this.background }\n this._drawRectToCanvas(0, 0, this.width, this.height, style);\n }\n }\n\n _draw () {\n let self = this;\n let list = this.data.list || [];\n let index = 0;\n let all = [];\n let count = 0;\n\n list.forEach(item => {\n if(item.type === 'wxml') {\n count += 3;\n } else {\n count += 1;\n }\n })\n\n this.distance = 60 / (count || 1); // 进度条的间距\n this.progressPercent = 30;\n this.asyncList = list.filter( item => item.delay == true );\n list = list.filter( item => item.delay != true );\n drawList(list);\n\n Promise.all(all).then(results => {\n index = 0;\n drawList(self.asyncList, true);\n\n Promise.all(all).then(results => {\n self.progress(90);\n self._saveCanvasToImage();\n });\n }).catch (e => {\n console.log(e)\n self.errorHandler(e);\n });\n\n function drawList(list = [], noDelay) {\n list.forEach((item, i) => {\n all[index++] = new Promise((resolve, reject) => {\n let attr = item.style;\n item.progress = self.distance;\n if (noDelay) {\n item.delay = 0;\n }\n if (item.type === 'radius-image') {\n self._drawCircle(item, attr, resolve, reject, 'image');\n } else if (item.type === 'text') {\n self._drawText(item, attr, resolve, reject);\n } else if (item.type === 'line') {\n self._drawLine(item, attr, resolve, reject);\n } else if (item.type === 'circle') {\n self._drawCircle(item, attr, resolve, reject);\n } else if (item.type === 'rect') {\n self._drawRect(item, attr, resolve, reject);\n } else if (item.type === 'image') {\n self._drawRect(item, attr, resolve, reject, 'image');\n } else if (item.type === 'wxml') {\n self._drawWxml(item, attr, resolve, reject);\n }else {\n resolve();\n }\n });\n });\n }\n }\n\n _saveCanvasToImage () {\n let self = this; \n\n // 延时保存有两个原因,一个是等待绘制delay的元素,另一个是安卓上样式会错乱\n setTimeout(() => {\n self.progress(95);\n\n let obj = {\n x: 0,\n y: 0,\n width: self.width,\n height: self.height,\n canvasId: self.element,\n success: function (res) {\n\n self.progress(100);\n self.imgUrl = res.tempFilePath;\n self.finishDraw(self.imgUrl);\n },\n fail: function (res) {\n\n self.errorHandler({errcode: 1000, errmsg: 'save canvas error', e: res});\n }\n }\n\n if(self.destZoom !== 3) {\n obj.destWidth = self.destWidth;\n obj.destHeight = self.destHeight;\n }\n\n wx.canvasToTempFilePath(obj, self.object);\n }, self.device.system.indexOf('iOS') === -1 ? 300 : 100);\n }\n\n _preloadImage (list = []) {\n let self = this;\n let all = [];\n let count = 0;\n\n list.forEach((item, i) => {\n if (item.url && self._findPicIndex(item.url) === -1) {\n \n // 避免重复下载同一图片\n self.allPic.push({\n url: item.url,\n local: ''\n });\n all[count++] = new Promise((resolve, reject) => {\n // 非http(s)域名的就不下载了\n if (!/^http/.test(item.url) || /^http:\\/\\/(tmp)|(usr)\\//.test(item.url) || /^http:\\/\\/127\\.0\\.0\\.1/.test(item.url)) {\n if(item.isBase64) {\n let fileManager = wx.getFileSystemManager();\n\n fileManager.writeFile({\n filePath: item.url,\n data: item.isBase64.replace(/data:image\\/(.*);base64,/, ''),\n encoding: 'base64',\n success (res) {\n imageInfo(item.url);\n },\n fail (res) {\n reject(res);\n },\n })\n \n }else {\n imageInfo(item.url);\n }\n\n function imageInfo (url) {\n wx.getImageInfo({\n src: url,\n success (res) {\n let index = self._findPicIndex(url);\n if(index > -1) {\n self.allPic[index].local = url;\n self.allPic[index].width = res.width;\n self.allPic[index].height = res.height;\n }\n resolve({ tempFilePath: url });\n }, \n fail (res) {\n reject(res);\n }\n })\n }\n } else {\n wx.downloadFile({\n url: item.url.replace(/^https?/, 'https'),\n success: function (res) {\n wx.getImageInfo({\n src: res.tempFilePath,\n success (img) {\n let index = self._findPicIndex(item.url);\n if (index > -1) {\n self.allPic[index].local = res.tempFilePath;\n self.allPic[index].width = img.width;\n self.allPic[index].height = img.height;\n }\n resolve(res);\n },\n fail (res) {\n reject(res);\n }\n })\n },\n fail: (res) => {\n reject({errcode: 1001, errmsg: 'download pic error'});\n }\n })\n }\n }) \n }\n });\n\n return Promise.all(all).then(results => {\n return new Promise(resolve => { resolve() })\n }).catch((results) => {\n return new Promise((resolve, reject) => { reject(results) })\n })\n }\n\n _findPicIndex (url) {\n let index = this.allPic.findIndex(pic => pic.url === url);\n return index;\n }\n\n _drawRect (item, style, resolve, reject, isImage, isWxml) {\n let zoom = this.zoom;\n let leftOffset = 0;\n let topOffset = 0;\n let width = style.width;\n let height = style.height;\n let imgWidth = style.width;\n let imgHeight = style.height;\n let mode = null;\n\n try {\n item.x = this._resetPositionX(item, style);\n item.y = this._resetPositionY(item, style);\n \n let url;\n if(isImage) {\n let index = this._findPicIndex(item.url);\n if(index > -1) {\n url = this.allPic[index].local\n imgWidth = this.allPic[index].width\n imgHeight = this.allPic[index].height\n }else {\n url = item.url;\n }\n }\n\n style.padding = style.padding || [];\n if(isWxml === 'inline-wxml') {\n item.x = item.x + (style.padding[3] && style.padding[3] || 0)\n item.y = item.y + (style.padding[0] && style.padding[0] || 0)\n }\n\n leftOffset = item.x + style.width + (style.padding[1] && style.padding[1] || 0);\n\n if(!isWxml) {\n width = width * zoom;\n height = height * zoom;\n }\n\n if(style.dataset && style.dataset.mode && imageMode.indexOf(style.dataset.mode) > -1) {\n mode = {\n type: style.dataset.mode,\n width: imgWidth,\n height: imgHeight\n };\n }\n\n this._drawRectToCanvas(item.x, item.y, width, height, style, url, mode);\n this._updateProgress(item.progress);\n\n if(resolve) {\n resolve();\n }else {\n return {\n leftOffset,\n topOffset\n }\n }\n } catch (e) {\n reject && reject({ errcode: (isImage ? 1003 : 1002), errmsg: (isImage ? 'drawImage error' : 'drawRect error'), e });\n }\n }\n\n _drawRectToCanvas (x, y, width, height, style, url, mode) {\n let { fill, border, boxShadow } = style;\n this.ctx.save();\n this._drawBoxShadow(boxShadow, (res) => {\n // 真机上填充渐变色时,没有阴影,先画个相等大小的纯色矩形来实现阴影\n if(fill && typeof fill !== 'string' && !this.debug) {\n this.ctx.setFillStyle(res.color || '#ffffff');\n this.ctx.fillRect(x, y, width, height);\n }\n });\n \n if(url) {\n // 开发者工具有bug,先不裁剪\n if(mode) {\n this._resetImageByMode(url, x, y, width, height, mode);\n }else {\n this.ctx.drawImage(url, x, y, width, height)\n }\n }else {\n this._setFill(fill, () => {\n this.ctx.fillRect(x, y, width, height);\n });\n }\n\n this._drawBorder(border, style, (border) => {\n let fixBorder = border.width;\n this.ctx.strokeRect(x - fixBorder / 2, y - fixBorder / 2, width + fixBorder, height + fixBorder);\n });\n\n this.ctx.draw(true);\n this.ctx.restore();\n }\n\n _resetImageByMode (url, x, y, width, height, mode) {\n let self = this;\n let offsetX = 0;\n let offsetY = 0;\n let imgWidth = mode.width;\n let imgHeight = mode.height;\n\n switch (mode.type) {\n case 'scaleToFill': \n imgWidth = width;\n imgHeight = height;\n self.ctx.drawImage(url, x, y, width, height)\n break;\n case 'widthFix': \n height = width / ((imgWidth || 1) / (imgHeight || 1))\n self.ctx.drawImage(url, x, y, width, height)\n break; \n case 'aspectFit': \n if(imgWidth > imgHeight) {\n let realHeight = width / ((imgWidth || 1) / (imgHeight || 1))\n offsetY = -(height - realHeight) / 2\n imgWidth = width;\n imgHeight = realHeight;\n }else {\n let realWidth = height / ((imgHeight || 1) / (imgWidth || 1))\n offsetX = -(width - realWidth) / 2\n imgWidth = realWidth;\n imgHeight = height;\n }\n\n _clip();\n break;\n case 'aspectFill': \n if(imgWidth > imgHeight) {\n let realWidth = imgWidth / ((imgHeight || 1) / (height || 1))\n offsetX = (realWidth - width) / 2\n imgWidth = realWidth;\n imgHeight = height;\n }else {\n let realHeight = imgHeight / ((imgWidth || 1) / (width || 1))\n offsetY = (realHeight - height) / 2\n imgWidth = width;\n imgHeight = realHeight;\n }\n\n _clip();\n break;\n case 'top left': \n _clip();\n break;\n case 'top': \n offsetX = (mode.width - width) / 2;\n _clip();\n break;\n case 'top right': \n offsetX = (mode.width - width);\n _clip();\n break;\n case 'left': \n offsetY = (mode.height - height) / 2;\n _clip();\n break;\n case 'center': \n offsetX = (mode.width - width) / 2;\n offsetY = (mode.height - height) / 2;\n _clip();\n break;\n case 'right': \n offsetX = (mode.width - width);\n offsetY = (mode.height - height) / 2;\n _clip();\n break;\n case 'bottom left': \n offsetY = (mode.height - height)\n _clip();\n break;\n case 'bottom': \n offsetX = (mode.width - width) / 2;\n offsetY = (mode.height - height)\n _clip();\n break;\n case 'bottom right': \n offsetX = (mode.width - width);\n offsetY = (mode.height - height)\n _clip();\n break;\n default: \n imgWidth = width;\n imgHeight = height;\n break; \n }\n\n function _clip () {\n self.ctx.save();\n self.ctx.beginPath()\n self.ctx.rect(x, y, width, height)\n self.ctx.clip();\n self.ctx.drawImage(url, x - offsetX, y - offsetY, imgWidth, imgHeight)\n self.ctx.closePath();\n self.ctx.restore();\n }\n }\n\n _drawText (item, style, resolve, reject, type, isWxml) {\n let zoom = this.zoom;\n let leftOffset = 0;\n let topOffset = 0;\n\n try {\n style.fontSize = this._parseNumber(style.fontSize);\n let fontSize = Math.ceil((style.fontSize || 14) * zoom)\n this.ctx.setTextBaseline('top');\n this.ctx.font = (`${style.fontWeight ? (style.fontWeight) : 'normal'} ${ fontSize }px ${ style.fontFamily || 'PingFang SC' }`);\n this.ctx.setFillStyle(style.color || '#454545');\n\n let text = item.text || '';\n let textWidth = Math.floor(this.measureWidth(text, style.font || this.ctx.font));\n let lineHeight = this._getLineHeight(style);\n let textHeight = Math.ceil(textWidth / (style.width || textWidth)) * lineHeight;\n let width = Math.ceil((style.width || textWidth) * (!isWxml ? zoom : 1));\n let whiteSpace = style.whiteSpace || 'wrap';\n let x = 0;\n let y = 0; \n\n if(typeof style.padding === 'string') {\n style.padding = Util.transferPadding(style.padding);\n }\n item.x = this._resetPositionX(item, style);\n item.y = this._resetPositionY(item, style, textHeight);\n this._drawBoxShadow(style.boxShadow);\n\n if(style.background || style.border) {\n this._drawTextBackgroud(item, style, textWidth, textHeight, isWxml);\n }\n \n // 行内文本\n if(type === 'inline-text') {\n width = item.maxWidth;\n if(item.leftOffset + textWidth > width) {\n // 如果上一个行内元素换行了,这个元素要继续在后面补足一行\n let lineNum = Math.max(Math.floor(textWidth / width), 1);\n let length = text.length;\n let singleLength = Math.floor(length / lineNum);\n let widthOffset = item.leftOffset ? item.leftOffset - item.originX : 0;\n let { endIndex: currentIndex, single, singleWidth } = this._getTextSingleLine(text, width, singleLength, 0, widthOffset)\n x = this._resetTextPositionX(item, style, singleWidth);\n y = this._resetTextPositionY(item, style);\n this.ctx.fillText(single, x, y);\n leftOffset = x + singleWidth;\n topOffset = y;\n\n // 去除第一行补的内容,然后重置\n text = text.substring(currentIndex, text.length);\n currentIndex = 0;\n lineNum = Math.max(Math.floor(textWidth / width), 1);\n textWidth = Math.floor(this.measureWidth(text, style.font || this.ctx.font));\n item.x = item.originX; // 还原换行后的x\n for (let i = 0; i < lineNum; i++) {\n let { endIndex, single, singleWidth } = this._getTextSingleLine(text, width, singleLength, currentIndex);\n currentIndex = endIndex;\n if(single) {\n x = this._resetTextPositionX(item, style, singleWidth, width);\n y = this._resetTextPositionY(item, style, i + 1);\n this.ctx.fillText(single, x, y);\n if(i === lineNum - 1) {\n leftOffset = x + singleWidth;\n topOffset = lineHeight * lineNum;\n }\n }\n }\n\n let last = text.substring(currentIndex, length);\n let lastWidth = this.measureWidth(last);\n\n if(last) {\n x = this._resetTextPositionX(item, style, lastWidth, width);\n y = this._resetTextPositionY(item, style, lineNum + 1);\n this.ctx.fillText(last, x, y);\n leftOffset = x + lastWidth;\n topOffset = lineHeight * (lineNum + 1);\n }\n }else {\n x = this._resetTextPositionX(item, style, textWidth, width);\n y = this._resetTextPositionY(item, style);\n this.ctx.fillText(item.text, x, y);\n leftOffset = x + textWidth;\n topOffset = lineHeight;\n }\n }else {\n // block文本,如果文本长度超过宽度换行\n if (width && textWidth > width && whiteSpace !== 'nowrap') {\n let lineNum = Math.max(Math.floor(textWidth / width), 1);\n let length = text.length;\n let singleLength = Math.floor(length / lineNum);\n let currentIndex = 0;\n\n // lineClamp参数限制最多行数\n if (style.lineClamp && lineNum + 1 > style.lineClamp) {\n lineNum = style.lineClamp - 1;\n }\n\n for (let i = 0; i < lineNum; i++) {\n let { endIndex, single, singleWidth } = this._getTextSingleLine(text, width, singleLength, currentIndex);\n currentIndex = endIndex;\n x = this._resetTextPositionX(item, style, singleWidth, width);\n y = this._resetTextPositionY(item, style, i);\n this.ctx.fillText(single, x, y);\n\n }\n\n // 换行后剩余的文字,超过一行则截断增加省略号\n let last = text.substring(currentIndex, length);\n let lastWidth = this.measureWidth(last);\n if(lastWidth > width) {\n let { single, singleWidth } = this._getTextSingleLine(last, width, singleLength);\n lastWidth = singleWidth;\n last = single.substring(0, single.length - 1) + '...';\n }\n\n x = this._resetTextPositionX(item, style, lastWidth, width);\n y = this._resetTextPositionY(item, style, lineNum);\n this.ctx.fillText(last, x, y);\n\n }else {\n x = this._resetTextPositionX(item, style, textWidth, width);\n y = this._resetTextPositionY(item, style);\n this.ctx.fillText(item.text, x, y);\n }\n }\n \n this.ctx.draw(true);\n \n this._updateProgress(item.progress);\n\n if(resolve) {\n resolve();\n }else {\n return {\n leftOffset,\n topOffset\n }\n }\n } catch(e) {\n reject && reject({ errcode: 1004, errmsg: 'drawText error', e: e });\n }\n }\n\n _drawTextBackgroud (item, style, textWidth, textHeight, isWxml) {\n if(!style.width) return;\n let zoom = isWxml ? 1 : this.zoom;\n let width = style.width || textWidth;\n let height = style.height || textHeight;\n let rectStyle = {\n fill: style.background,\n border: style.border\n }\n style.padding = style.padding || [0, 0, 0, 0];\n width += (style.padding[1] || 0) + (style.padding[3] || 0);\n height += (style.padding[0] || 0) + (style.padding[2] || 0);\n width = width * zoom\n height = height * zoom\n this._drawRectToCanvas(item.x, item.y, width, height, rectStyle);\n }\n\n _drawCircle (item, style, resolve, reject, isImage, isWxml) {\n let zoom = this.zoom;\n let r = style.r;\n try {\n \n item.x = this._resetPositionX(item, style);\n item.y = this._resetPositionY(item, style);\n\n let url;\n if(isImage) {\n let index = this._findPicIndex(item.url);\n if (index > -1) {\n url = this.allPic[index].local;\n } else {\n url = item.url;\n }\n }\n\n if(!isWxml) {\n r = r * zoom;\n }\n\n this._drawCircleToCanvas(item.x, item.y, r, style, url);\n \n this._updateProgress(item.progress);\n resolve && resolve();\n } catch (e) {\n reject && reject({ errcode: (isImage ? 1006 : 1005), errmsg: (isImage ? 'drawCircleImage error' : 'drawCircle error'), e });\n }\n }\n\n _drawCircleToCanvas (x, y, r, style, url) {\n let { fill, border, boxShadow } = style;\n\n this.ctx.save();\n\n this._drawBoxShadow(boxShadow, (res) => {\n // 真机上填充渐变色时,没有阴影,先画个相等大小的纯色矩形来实现阴影\n if((fill && typeof fill !== 'string') || (url && res.color)) {\n this.ctx.setFillStyle(res.color || '#ffffff');\n this.ctx.beginPath();\n this.ctx.arc(x + r, y + r, r, 0, 2 * Math.PI);\n this.ctx.closePath();\n this.ctx.fill();\n }\n });\n\n if(url) {\n this.ctx.save();\n this.ctx.beginPath();\n this.ctx.arc(x + r, y + r, r, 0, 2 * Math.PI);\n this.ctx.clip();\n this.ctx.drawImage(url, x, y, r * 2, r * 2);\n this.ctx.closePath();\n this.ctx.restore();\n }else {\n this._setFill(fill, () => {\n this.ctx.beginPath();\n this.ctx.arc(x + r, y + r, r, 0, 2 * Math.PI);\n this.ctx.closePath();\n this.ctx.fill();\n });\n }\n\n this._drawBorder(border, style, (border) => {\n this.ctx.beginPath()\n this.ctx.arc(x + r, y + r, r + border.width / 2, 0, 2 * Math.PI)\n this.ctx.stroke()\n this.ctx.closePath();\n });\n\n this.ctx.draw(true);\n this.ctx.restore();\n }\n\n _drawLine (item, style, resolve, reject, isWxml) {\n let zoom = this.zoom;\n try {\n let x1 = item.x * zoom + this.translateX;\n let y1 = item.y * zoom + this.translateY;\n let x2 = item.x2 * zoom + this.translateX;\n let y2 = item.y2 * zoom + this.translateY;\n this._drawLineToCanvas(x1, y1, x2, y2, style);\n\n this._updateProgress(item.progress);\n resolve && resolve();\n } catch (e) {\n reject && reject({ errcode: 1007, errmsg: 'drawLine error', e });\n }\n }\n\n _drawLineToCanvas (x1, y1, x2, y2, style) {\n let { stroke, dash, boxShadow } = style;\n\n this.ctx.save();\n if(stroke) {\n this._setStroke(stroke);\n }\n\n this._drawBoxShadow(boxShadow);\n\n if(dash) {\n let dash = [style.dash[0] || 5, style.dash[1] || 5];\n let offset = style.dash[2] || 0;\n this.ctx.setLineDash(dash, offset || 0);\n }\n\n this.ctx.moveTo(x1, y1);\n this.ctx.setLineWidth((style.width || 1) * this.zoom);\n this.ctx.lineTo(x2, y2);\n this.ctx.stroke();\n this.ctx.draw(true);\n this.ctx.restore();\n }\n\n // 废弃,合并到_drawRect\n _drawImage (item, style, resolve, reject, isWxml) {\n let zoom = this.zoom;\n try {\n \n item.x = this._resetPositionX(item, style);\n item.y = this._resetPositionY(item, style);\n item.x = item.x + (style.padding[3] || 0);\n item.y = item.y + (style.padding[0] || 0);\n\n let index = this._findPicIndex(item.url);\n let url = index > -1 ? this.allPic[index].local : item.url;\n this._drawImageToCanvas(url, item.x, item.y, style.width * zoom, style.height * zoom, style);\n\n this._updateProgress(item.progress);\n resolve && resolve();\n } catch (e) {\n reject && reject({ errcode: 1012, errmsg: 'drawRect error', e });\n }\n }\n\n // 废弃,合并到_drawRect\n _drawImageToCanvas (url, x, y, width, height, style) {\n let { fill, border, boxShadow } = style;\n this.ctx.save();\n\n this._drawBoxShadow(boxShadow);\n this.ctx.drawImage(url, x, y, width, height);\n\n this._drawBorder(border, style, (border) => {\n let fixBorder = border.width;\n this.ctx.strokeRect(x - fixBorder / 2, y - fixBorder / 2, width + fixBorder, height + fixBorder);\n });\n this.ctx.draw(true);\n this.ctx.restore();\n }\n\n _drawWxml (item, style, resolve, reject) {\n let self = this;\n let all = [];\n try {\n this._getWxml(item, style).then((results) => {\n \n // 上 -> 下\n let sorted = self._sortListByTop(results[0]);\n let count = 0;\n let progress = 0;\n Object.keys(sorted).forEach(item => {\n count += sorted[item].length;\n })\n progress = this.distance * 3 / (count || 1);\n\n all = this._drawWxmlBlock(item, sorted, all, progress, results[1]);\n all = this._drawWxmlInline(item, sorted, all, progress, results[1]);\n\n Promise.all(all).then(results => {\n resolve && resolve();\n }).catch (e => {\n reject && reject(e);\n });\n });\n } catch (e) {\n reject && reject({ errcode: 1008, errmsg: 'drawWxml error' });\n }\n }\n\n _drawWxmlBlock (item, sorted, all, progress, results) {\n let self = this;\n // 用来限定位置范围,取相对位置\n let limitLeft = (results ? results.left : 0);\n let limitTop = (results ? results.top : 0);\n Object.keys(sorted).forEach((top, topIndex) => {\n // 左 -> 右\n let list = sorted[top].sort((a, b) => {\n return (a.left - b.left);\n });\n\n list = list.filter(sub => sub.dataset.type && sub.dataset.type.indexOf('inline') === -1);\n\n list.forEach((sub, index) => {\n all[index] = new Promise((resolve2, reject2) => {\n sub = self._transferWxmlStyle(sub, item, limitLeft, limitTop);\n sub.progress = progress;\n let type = sub.dataset.type;\n if(sub.dataset.delay) {\n setTimeout(() => {\n drawWxmlItem();\n }, sub.dataset.delay)\n } else {\n drawWxmlItem();\n }\n function drawWxmlItem () {\n if (type === 'text') {\n self._drawWxmlText(sub, resolve2, reject2);\n } else if (type === 'image') {\n self._drawWxmlImage(sub, resolve2, reject2);\n } else if (type === 'radius-image') {\n self._drawWxmlCircleImage(sub, resolve2, reject2);\n } else if (type === 'background-image') {\n self._drawWxmlBackgroundImage(sub, resolve2, reject2);\n }\n }\n });\n });\n });\n\n return all;\n\n }\n\n _drawWxmlInline (item, sorted, all, progress, results) {\n let self = this;\n let topOffset = 0;\n let leftOffset = 0;\n let lastTop = 0;\n let limitLeft = (results ? results.left : 0);\n let limitTop = (results ? results.top : 0);\n let p = new Promise((resolve2, reject2) => {\n let maxWidth = 0;\n let minLeft = Infinity;\n let maxRight = 0;\n\n // 找出同一top下的最小left和最大right,得到最大的宽度,用于换行\n Object.keys(sorted).forEach(top => {\n let inlineList = sorted[top].filter(sub => sub.dataset.type && sub.dataset.type.indexOf('inline') > -1);\n inlineList.forEach(sub => {\n if(sub.left < minLeft) {\n minLeft = sub.left\n }\n if(sub.right > maxRight) {\n maxRight = sub.right;\n }\n })\n });\n maxWidth = Math.ceil((maxRight - minLeft) || self.width);\n\n Object.keys(sorted).forEach((top, topIndex) => {\n // 左 -> 右\n let list = sorted[top].sort((a, b) => {\n return (a.left - b.left);\n });\n\n // 换行的行内元素left放到后面,version2.0.6后无法获取高度,改用bottom值来判断是否换行了\n let position = -1;\n for(let i = 0, len = list.length; i < len; i++) {\n if(list[i] && list[i + 1]) {\n if(list[i].bottom > list[i + 1].bottom) {\n position = i;\n break;\n }\n }\n }\n\n if(position > -1) {\n list.push(list.splice(position, 1)[0]);\n }\n\n let inlineList = list.filter(sub => sub.dataset.type && sub.dataset.type.indexOf('inline') > -1);\n let originLeft = (inlineList[0] ? inlineList[0].left : 0);\n // 换行后和top不相等时,认为是换行了,要清除左边距;当左偏移量大于最大宽度时,也要清除左边距; 当左偏移小于左边距时,也要清除\n if (Math.abs(topOffset + lastTop - top) > 2 || leftOffset - originLeft - limitLeft >= maxWidth || leftOffset <= originLeft - limitLeft - 2) {\n leftOffset = 0;\n }\n\n lastTop = +top;\n topOffset = 0;\n \n inlineList.forEach((sub, index) => {\n sub = self._transferWxmlStyle(sub, item, limitLeft, limitTop);\n sub.progress = progress;\n let type = sub.dataset.type;\n if (type === 'inline-text') {\n let drawRes = self._drawWxmlInlineText(sub, leftOffset, maxWidth);\n leftOffset = drawRes.leftOffset;\n topOffset = drawRes.topOffset;\n } else if (type === 'inline-image') {\n let drawRes = self._drawWxmlImage(sub) || {};\n leftOffset = drawRes.leftOffset || 0;\n topOffset = drawRes.topOffset || 0;\n }\n });\n });\n resolve2();\n })\n\n all.push(p); \n return all;\n }\n\n _drawWxmlInlineText (sub, leftOffset = 0, maxWidth) {\n let text = sub.dataset.text || '';\n if(sub.dataset.maxlength && text.length > sub.dataset.maxlength) {\n text = text.substring(0, sub.dataset.maxlength) + '...';\n }\n \n let textData = {\n text,\n originX: sub.left,\n x: leftOffset ? leftOffset : sub.left,\n y: sub.top,\n progress: sub.progress,\n leftOffset: leftOffset,\n maxWidth: maxWidth // 行内元素的最大宽度,取决于limit的宽度\n }\n\n if (sub.backgroundColor !== 'rgba(0, 0, 0, 0)') {\n sub.background = sub.backgroundColor;\n }else {\n sub.background = 'rgba(0, 0, 0, 0)';\n }\n\n if(sub.dataset.background) {\n sub.background = sub.dataset.background;\n }\n\n let res = this._drawText(textData, sub, null, null, 'inline-text', 'wxml');\n\n return res\n }\n\n _drawWxmlText (sub, resolve, reject) {\n let text = sub.dataset.text || '';\n if(sub.dataset.maxlength && text.length > sub.dataset.maxlength) {\n text = text.substring(0, sub.dataset.maxlength) + '...';\n }\n \n let textData = {\n text,\n x: sub.left,\n y: sub.top,\n progress: sub.progress\n }\n if (sub.backgroundColor !== 'rgba(0, 0, 0, 0)') {\n sub.background = sub.backgroundColor;\n }else {\n sub.background = 'rgba(0, 0, 0, 0)';\n }\n\n if(sub.dataset.background) {\n sub.background = sub.dataset.background;\n }\n\n this._drawText(textData, sub, resolve, reject, 'text', 'wxml');\n }\n\n _drawWxmlImage (sub, resolve, reject) {\n let imageData = {\n url: sub.dataset.url,\n x: sub.left,\n y: sub.top,\n progress: sub.progress\n }\n\n let res = this._drawRect(imageData, sub, resolve, reject, 'image', 'inline-wxml');\n\n return res\n }\n\n _drawWxmlCircleImage (sub, resolve, reject) {\n let imageData = {\n url: sub.dataset.url,\n x: sub.left,\n y: sub.top,\n progress: sub.progress\n }\n sub.r = sub.width / 2;\n\n this._drawCircle(imageData, sub, resolve, reject, true, 'wxml');\n }\n\n _drawWxmlBackgroundImage (sub, resolve, reject) {\n let url = sub.dataset.url;\n let index = this._findPicIndex(url);\n url = index > -1 ? this.allPic[index].local : url;\n let size = sub.backgroundSize.replace(/px/g, '').split(' ');\n\n let imageData = {\n url: url,\n x: sub.left,\n y: sub.top,\n progress: sub.progress\n }\n\n this._drawRect(imageData, sub, resolve, reject, 'image', 'wxml');\n }\n\n _getWxml (item, style) {\n let self = this;\n let query;\n if(this.obj) {\n query = wx.createSelectorQuery().in(this.obj);\n }else {\n query = wx.createSelectorQuery();\n }\n\n let p1 = new Promise((resolve, reject) => {\n // 会触发两次,要限制\n let count = 0;\n query.selectAll(`${item.class}`).fields({\n dataset: true,\n size: true,\n rect: true,\n computedStyle: ['width', 'height', 'font', 'fontSize', 'fontFamily', 'fontWeight', 'fontStyle', 'textAlign', \n 'color', 'lineHeight', 'border', 'borderColor', 'borderStyle', 'borderWidth', 'verticalAlign', 'boxShadow',\n 'background', 'backgroundColor', 'backgroundImage', 'backgroundPosition', 'backgroundSize', 'paddingLeft', 'paddingTop',\n 'paddingRight', 'paddingBottom'\n ]\n }, (res) => {\n if(count++ === 0) {\n let formated = self._formatImage(res);\n let list = formated.list;\n res = formated.res;\n\n self._preloadImage(list).then(result => {\n resolve(res);\n }).catch((res) => {\n reject && reject({ errcode: 1009, errmsg: 'drawWxml preLoadImage error' });\n });\n }\n }).exec();\n });\n\n let p2 = new Promise((resolve, reject) => {\n if (!item.limit) {\n resolve({ top: 0, width: self.width / self.zoom });\n }\n\n query.select(`${item.limit}`).fields({\n dataset: true,\n size: true,\n rect: true,\n }, (res) => {\n resolve(res);\n }).exec();\n });\n\n return Promise.all([p1, p2]);\n }\n\n _getLineHeight (style) {\n let zoom = this.zoom;\n if(style.dataset && style.dataset.type) {\n zoom = 1;\n }\n let lineHeight;\n if(!isNaN(style.lineHeight) && style.lineHeight > style.fontSize) {\n lineHeight = style.lineHeight;\n }else {\n style.lineHeight = (style.lineHeight || '') + '';\n lineHeight = +style.lineHeight.replace('px', '');\n lineHeight = lineHeight ? lineHeight : (style.fontSize || 14) * 1.2;\n }\n return lineHeight * zoom;\n }\n\n _formatImage (res = []) {\n let list = [];\n res.forEach((item, index) => {\n let dataset = item.dataset;\n let uid = Util.getUid();\n let filename = `${wx.env.USER_DATA_PATH}/${uid}.png`;\n if ((dataset.type === \"image\" || dataset.type === \"radius-image\") && dataset.url) {\n let sub = {\n url: dataset.base64 ? filename : dataset.url,\n isBase64: dataset.base64 ? dataset.url : false\n }\n\n res[index].dataset = Object.assign(res[index].dataset, sub);\n list.push(sub)\n } else if (dataset.type === 'background-image' && item.backgroundImage.indexOf('url') > -1) {\n let url = item.backgroundImage.replace(/url\\((\\\"|\\')?/, '').replace(/(\\\"|\\')?\\)$/, '');\n let sub = {\n url: dataset.base64 ? filename : url,\n isBase64: dataset.base64 ? url : false\n }\n res[index].dataset = Object.assign(res[index].dataset, sub);\n list.push(sub)\n }\n });\n\n return { list, res };\n }\n\n _updateProgress (distance) {\n this.progressPercent += distance;\n this.progress(this.progressPercent);\n }\n\n _sortListByTop (list = []) {\n let sorted = {};\n\n // 粗略地认为2px相差的元素在同一行\n list.forEach((item, index) => {\n let top = item.top;\n if (!sorted[top]) {\n if (sorted[top - 2]) {\n top = top - 2;\n }else if (sorted[top - 1]) {\n top = top - 1;\n } else if (sorted[top + 1]) {\n top = top + 1;\n } else if (sorted[top + 2]) {\n top = top + 2;\n } else {\n sorted[top] = [];\n }\n }\n sorted[top].push(item);\n });\n\n return sorted;\n }\n\n _parseNumber (number) {\n return isNaN(number) ? +(number || '').replace('px', '') : number;\n }\n\n _transferWxmlStyle (sub, item, limitLeft, limitTop) {\n let leftFix = (+sub.dataset.left || 0);\n let topFix = (+sub.dataset.top || 0);\n\n sub.width = this._parseNumber(sub.width);\n sub.height = this._parseNumber(sub.height);\n sub.left = this._parseNumber(sub.left) - limitLeft + (leftFix + (item.x || 0)) * this.zoom;\n sub.top = this._parseNumber(sub.top) - limitTop + (topFix + (item.y || 0)) * this.zoom;\n\n let padding = sub.dataset.padding || '0 0 0 0';\n if (typeof padding === 'string') {\n padding = Util.transferPadding(padding);\n }\n let paddingTop = Number(sub.paddingTop.replace('px', '')) + Number(padding[0]);\n let paddingRight = Number(sub.paddingRight.replace('px', '')) + Number(padding[1]);\n let paddingBottom = Number(sub.paddingBottom.replace('px', '')) + Number(padding[2]);\n let paddingLeft = Number(sub.paddingLeft.replace('px', '')) + Number(padding[3]);\n sub.padding = [paddingTop, paddingRight, paddingBottom, paddingLeft];\n \n return sub;\n }\n\n /**\n * 支持负值绘制,从右边计算\n * @param {*} item \n * @param {*} style \n */\n _resetPositionX (item, style) {\n let zoom = this.zoom;\n let x = 0;\n\n if(style.dataset && style.dataset.type) {\n zoom = 1;\n }\n\n // 通过wxml获取的不需要重置坐标\n if (item.x < 0 && item.type) {\n x = this.width + item.x * zoom - style.width * zoom;\n } else {\n x = item.x * zoom;\n }\n\n if (parseInt(style.borderWidth)) {\n x += parseInt(style.borderWidth)\n }\n\n return x + this.translateX;\n }\n\n /**\n * 支持负值绘制,从底部计算\n * @param {*} item \n * @param {*} style \n */\n _resetPositionY (item, style, textHeight) {\n let zoom = this.zoom;\n let y = 0;\n\n if(style.dataset && style.dataset.type) {\n zoom = 1;\n }\n\n if (item.y < 0) {\n y = this.height + item.y * zoom - (textHeight ? textHeight : style.height * zoom)\n } else {\n y = item.y * zoom;\n }\n\n if (parseInt(style.borderWidth)) {\n y += parseInt(style.borderWidth)\n }\n \n return y + this.translateY;\n }\n\n /**\n * 文字的padding、text-align\n * @param {*} item \n * @param {*} style \n * @param {*} textWidth\n */\n _resetTextPositionX (item, style, textWidth, width) {\n let textAlign = style.textAlign || 'left';\n let x = item.x;\n if (textAlign === 'center') {\n x = (width - textWidth) / 2 + item.x;\n } else if (textAlign === 'right') {\n x = width - textWidth + item.x;\n }\n\n let left = style.padding ? (style.padding[3] || 0) : 0;\n\n return x + left + this.translateX;\n }\n\n /**\n * 文字的padding、text-align\n * @param {*} item \n * @param {*} style \n * @param {*} textWidth\n */\n _resetTextPositionY (item, style, lineNum = 0) {\n let zoom = this.zoom;\n if(style.dataset && style.dataset.type) {\n zoom = 1;\n }\n\n let lineHeight = this._getLineHeight(style);\n let fontSize = Math.ceil((style.fontSize || 14) * zoom)\n\n let blockLineHeightFix = (style.dataset && style.dataset.type || '').indexOf('inline') > -1 ? 0 : (lineHeight - fontSize) / 2\n\n let top = style.padding ? (style.padding[0] || 0) : 0;\n\n // y + lineheight偏移 + 行数 + paddingTop + 整体画布位移\n return item.y + blockLineHeightFix + lineNum * lineHeight + top + this.translateY;\n }\n\n /**\n * 当文本超过宽度时,计算每一行应该绘制的文本\n * @param {*} text \n * @param {*} width \n * @param {*} singleLength \n * @param {*} currentIndex \n * @param {*} widthOffset\n */\n _getTextSingleLine(text, width, singleLength, currentIndex = 0, widthOffset = 0) {\n let offset = 0;\n let endIndex = currentIndex + singleLength + offset;\n let single = text.substring(currentIndex, endIndex);\n let singleWidth = this.measureWidth(single);\n\n while (Math.round(widthOffset + singleWidth) > width) {\n offset--;\n endIndex = currentIndex + singleLength + offset;\n single = text.substring(currentIndex, endIndex);\n singleWidth = this.measureWidth(single);\n }\n\n return {\n endIndex, \n single, \n singleWidth\n }\n }\n\n _drawBorder (border, style, callback) {\n let zoom = this.zoom;\n if(style.dataset && style.dataset.type) {\n zoom = 1;\n }\n border = Util.transferBorder(border);\n\n if (border && border.width) {\n // 空白阴影,清空掉边框的阴影\n this._drawBoxShadow();\n if (border) {\n \n this.ctx.setLineWidth(border.width * zoom);\n\n if (border.style === 'dashed') {\n let dash = style.dash || [5, 5, 0];\n let offset = dash[2] || 0;\n let array = [dash[0] || 5, dash[1] || 5];\n this.ctx.setLineDash(array, offset);\n }\n this.ctx.setStrokeStyle(border.color);\n }\n callback && callback(border);\n }\n } \n\n _drawBoxShadow (boxShadow, callback) {\n boxShadow = Util.transferBoxShadow(boxShadow);\n if (boxShadow) {\n this.ctx.setShadow(boxShadow.offsetX, boxShadow.offsetY, boxShadow.blur, boxShadow.color);\n }else {\n this.ctx.setShadow(0, 0, 0, '#ffffff');\n }\n\n callback && callback(boxShadow || {});\n }\n\n _setFill (fill, callback) {\n if(fill) {\n if (typeof fill === 'string') {\n this.ctx.setFillStyle(fill);\n } else {\n let line = fill.line;\n let color = fill.color;\n let grd = this.ctx.createLinearGradient(line[0], line[1], line[2], line[3]);\n grd.addColorStop(0, color[0]);\n grd.addColorStop(1, color[1]);\n this.ctx.setFillStyle(grd);\n }\n callback && callback();\n }\n }\n\n _setStroke (stroke, callback) {\n if(stroke) {\n if (typeof stroke === 'string') {\n this.ctx.setStrokeStyle(stroke);\n } else {\n let line = stroke.line;\n let color = stroke.color;\n let grd = this.ctx.createLinearGradient(line[0], line[1], line[2], line[3]);\n grd.addColorStop(0, color[0]);\n grd.addColorStop(1, color[1]);\n this.ctx.setStrokeStyle(grd);\n }\n\n callback && callback();\n }\n }\n}\n\nif (!exports.__esModule) Object.defineProperty(exports, \"__esModule\", { value: true });exports.default = Wxml2Canvas;","/**\n * 获取字符的长度,full为true时,一个汉字算两个长度\n * @param {String} str \n * @param {Boolean} full \n */\n\nfunction getTextLength (str, full) {\n let len = 0;\n for (let i = 0; i < str.length; i++) {\n let c = str.charCodeAt(i);\n //单字节加1 \n if ((c >= 0x0001 && c <= 0x007e) || (0xff60 <= c && c <= 0xff9f)) {\n len++;\n }\n else {\n len += (full ? 2 : 1);\n }\n }\n return len;\n}\n\n/**\n * rgba(255, 255, 255, 1) => #ffffff\n * @param {String} color \n */\nfunction transferColor (color = '') {\n let res = '#';\n color = color.replace(/^rgba?\\(/, '').replace(/\\)$/, '');\n color = color.split(', ');\n \n color.length > 3 ? color.length = 3 : '';\n for(let item of color) {\n item = parseInt(item || 0);\n if(item < 10) {\n res += ('0' + item)\n }else {\n res += (item.toString(16))\n }\n }\n\n return res;\n}\n\nfunction transferBorder (border = '') {\n let res = border.match(/(\\w+)px\\s(\\w+)\\s(.*)/);\n let obj = {};\n\n if(res) {\n obj = {\n width: +res[1],\n style: res[2],\n color: res[3]\n }\n }\n \n return res ? obj : null;\n}\n\n\n/**\n * 内边距,依次为上右下左\n * @param {*} padding \n */\nfunction transferPadding (padding = '0 0 0 0') {\n padding = padding.split(' ');\n for(let i = 0, len = padding.length; i < len; i++) {\n padding[i] = +padding[i].replace('px', '');\n }\n\n return padding;\n}\n/**\n * type1: 0, 25, 17, rgba(0, 0, 0, 0.3)\n * type2: rgba(0, 0, 0, 0.3) 0px 25px 17px 0px => (0, 25, 17, rgba(0, 0, 0, 0.3))\n * @param {*} shadow \n */\nfunction transferBoxShadow(shadow = '', type) {\n if(!shadow || shadow === 'none') return;\n let color;\n let split;\n\n split = shadow.match(/(\\w+)\\s(\\w+)\\s(\\w+)\\s(rgb.*)/);\n\n if (split) {\n split.shift();\n shadow = split;\n color = split[3] || '#ffffff';\n } else {\n split = shadow.split(') ');\n color = split[0] + ')'\n shadow = split[1].split('px ');\n }\n\n return {\n offsetX: +shadow[0] || 0,\n offsetY: +shadow[1] || 0,\n blur: +shadow[2] || 0,\n color\n }\n}\n\nfunction getUid(prefix) {\n prefix = prefix || '';\n\n return (\n prefix +\n 'xxyxxyxx'.replace(/[xy]/g, c => {\n let r = (Math.random() * 16) | 0;\n let v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n })\n );\n}\n\nif (!exports.__esModule) Object.defineProperty(exports, \"__esModule\", { value: true });exports.default = {\n getTextLength,\n transferBorder,\n transferColor,\n transferPadding,\n transferBoxShadow,\n getUid\n};"]}
|