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