当前位置:网站首页>wxml2canvas
wxml2canvas
2022-07-05 15:21:00 【Shepherd Wolf】
Catalog
introduce wxml2canvas library , Directory as follows :

Document address
https://github.com/liudongyun1215/wxml2canvas
https://github.com/liudongyun1215/wxml2canvas
application
wxml
<dialog show="{
{isShowCirclePicDia}}" style="position: relative;">
<canvas canvas-id="myCanvas" class="canvas" style="width: {
{canvasWidth}}px; height: {
{canvasHeight}}px;position: fixed; top: -199999rpx;"></canvas>
<view class="friendcircle-dialog-wrap">
<image class="close-dia" src="/images/[email protected]" bindtap="closeCirclePicDialog"></image>
<image class="fcdia-pic draw_canvas" data-type="image" data-url="/images/fc-dia-pic.png" src="/images/fc-dia-pic.png"></image>
<image class="goods-img draw_canvas" data-type="image" data-url="{
{item.item_img}}" src="{
{item.item_img}}"></image>
<view class="fc-info draw_canvas">
<view class="fc-left draw_canvas">
<view wx:if="{
{item.shop_icon}}" class="title-box draw_canvas" data-type="text" data-text=" 【{
{item.brand_name}}】{
{item.item_title}}" data-lineClamp="{
{2}}">
<view class="draw_canvas shop-icon-wrap">
<!-- <image class="shop-icon draw_canvas" data-type="image" data-url="/images/test-douyin.png" data-mode="aspectFit" src="/images/test-douyin.png" mode="aspectFit"></image> -->
<image class="shop-icon draw_canvas" data-type="image" data-url="{
{item.shop_icon}}" data-mode="aspectFit" src="{
{item.shop_icon}}" mode="aspectFit"></image>
</view>
<text><text wx:if="{
{item.brand_name}}">【{
{item.brand_name}}】</text>{
{item.item_title}}</text>
</view>
<view wx:else class="title-box draw_canvas" data-type="text" data-text="【{
{item.brand_name}}】{
{item.item_title}}" data-lineClamp="{
{2}}">
<text><text wx:if="{
{item.brand_name}}">【{
{item.brand_name}}】</text>{
{item.item_title}}</text>
</view>
<view class="price-now-box draw_canvas">
<view class="price-txt draw_canvas" data-type="text" data-text=" To get "> To get </view><view class="yuan draw_canvas" data-type="text" data-text="¥">¥</view>
<view class="money draw_canvas" data-type="text" data-text="{
{item.price_one}}.">{
{item.price_one}}.</view>
<view class="money-float draw_canvas" data-type="text" data-text="{
{item.price_two}}">{
{item.price_two}}</view>
</view>
<view class="price-extra-box draw_canvas">
<view class="txt draw_canvas" data-type="text" data-text="{
{item.shop_name}} price ">{
{item.shop_name}} price </view><view class="yuan draw_canvas" data-type="text" data-text="¥">¥</view><view class="money draw_canvas" data-type="text" data-text="{
{item.item_price}}">{
{item.item_price}}</view>
<view class="fanli-tag draw_canvas" data-type="background-image" data-base64="1" style="background-image:url('data:image/png;base64,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');"><text class="draw_canvas" data-type="text" data-text=" return {
{item.share_comm_rate}}"> return {
{item.share_comm_rate}}</text><text class="draw_canvas" data-type="text" data-text="%">%</text></view>
</view>
</view>
<view class="fc-right draw_canvas">
<!-- <image class="qr-code draw_canvas" data-type="image" data-url="/images/test-qr.png" src="/images/test-qr.png"/> -->
<image class="qr-code draw_canvas" data-type="image" data-url="{
{item.qrLink}}" src="{
{item.qrLink}}"/>
<view class="qr-txt draw_canvas" data-type="text" data-text=" Long press the identification to receive the discount "> Long press the identification to receive the discount </view>
</view>
</view>
</view>
<view class="save-btn" bindtap="saveFriendCircleImg"> Save the picture </view>
</dialog>js
drawImage() {
const that = this;
var wxcanvas = new Wxml2Canvas({
width: 300,
height: 456,
element: 'myCanvas',
background: '#fff',
progress(percent) {},
finish(url) {
wx.hideLoading();
wx.saveImageToPhotosAlbum({
filePath: url,
// Authorized success , Save the picture
success: function () {
that.setData({
isShowCirclePicDia: false
})
wx.showToast({
title: ' Saved successfully ',
icon: 'success',
duration: 2000
})
}
})
},
error(res) {}
});
let data = {
list: [
{
type: 'wxml',
class: '.friendcircle-dialog-wrap .draw_canvas',
limit: '.friendcircle-dialog-wrap',
x: 10,
y: 10
}
]
}
wxcanvas.draw(data);
},util.js
/**
* Get the length of the character ,full by true when , One Chinese character counts as two lengths
* @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);
// Single byte plus 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;
}
/**
* padding , Top right, bottom left
* @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);
})
);
}
export default {
getTextLength,
transferBorder,
transferColor,
transferPadding,
transferBoxShadow,
getUid
}index.js
import Util from './util';
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.obj = 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; // Draw progress percentage
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.obj);
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); // Spacing of progress bar
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;
// There are two reasons for delaying saving , One is waiting to draw delay The elements of , The other is that the style on Android will be disordered
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.obj);
}, 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) {
// Avoid downloading the same image repeatedly
self.allPic.push({
url: item.url,
local: ''
});
all[count++] = new Promise((resolve, reject) => {
// Not http(s) The domain name will not be downloaded
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) => {
// When filling gradient color on real machine , There is no shadow , First draw a solid color rectangle of equal size to realize the shadow
if(fill && typeof fill !== 'string' && !this.debug) {
this.ctx.setFillStyle(res.color || '#ffffff');
this.ctx.fillRect(x, y, width, height);
}
});
if(url) {
// The developer tools are bug, Don't cut it first
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();
}
_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);
}
// Inline text
if(type === 'inline-text') {
width = item.maxWidth;
if(item.leftOffset + textWidth > width) {
// If the previous inline element breaks , This element should continue to be supplemented by a line
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;
// Remove the contents of the first line , Then reset
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; // Restore the newline 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 Text , If the text length exceeds the width, wrap
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 The parameter limits the maximum number of rows
if (style.dataset.lineclamp && lineNum + 1 > style.dataset.lineclamp) {
lineNum = style.dataset.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);
}
// The remaining text after line breaking , If more than one line, truncate and add ellipsis
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) => {
// When filling gradient color on real machine , There is no shadow , First draw a solid color rectangle of equal size to realize the shadow
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();
}
// abandoned , Merge into _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 });
}
}
// abandoned , Merge into _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) => {
// On -> Next
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;
// Used to limit the range of positions , Take the relative position
let limitLeft = (results ? results.left : 0);
let limitTop = (results ? results.top : 0);
Object.keys(sorted).forEach((top, topIndex) => {
// Left -> Right
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;
// Find the same top Minimum under left And the biggest right, Get the maximum width , For line breaks
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) => {
// Left -> Right
let list = sorted[top].sort((a, b) => {
return (a.left - b.left);
});
// Inline element of newline left Put it in the back ,version2.0.6 Cannot get height after , change to the use of sth. bottom Value to determine whether the line breaks
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);
// After line feed and top When they are not equal , Think it's a new line , To clear the left margin ; When the left offset is greater than the maximum width , Also clear the left margin ; When the left offset is less than the left margin , Also remove
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 // The maximum width of an element in a row , Depending on limit Width
}
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();
}
let p1 = new Promise((resolve, reject) => {
// Will trigger twice , To limit
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 = {};
// Roughly speaking 2px The different elements are on the same line
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;
}
/**
* Support negative value drawing , Calculate from the right
* @param {*} item
* @param {*} style
*/
_resetPositionX (item, style) {
let zoom = this.zoom;
let x = 0;
if(style.dataset && style.dataset.type) {
zoom = 1;
}
// adopt wxml The obtained coordinates do not need to be reset
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;
}
/**
* Support negative value drawing , Calculate from the bottom
* @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;
}
/**
* In words 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;
}
/**
* In words 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 The offset + Row number + paddingTop + Overall canvas displacement
return item.y + blockLineHeightFix + lineNum * lineHeight + top + this.translateY;
}
/**
* When the text exceeds the width , Calculate the text that should be drawn on each line
* @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) {
// Blank shadow , Clear the shadow of the border
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();
}
}
}
export default Wxml2Canvas;边栏推荐
- Photoshop plug-in action related concepts actionlist actiondescriptor actionlist action execution load call delete PS plug-in development
- Reconnaissance des caractères easycr
- queryRunner. Query method
- 超越PaLM!北大碩士提出DiVeRSe,全面刷新NLP推理排行榜
- CODING DevSecOps 助力金融企业跑出数字加速度
- Go learning ----- relevant knowledge of JWT
- JS topic - console log()
- DVWA range clearance tutorial
- I spring and autumn blasting-1
- CPU design related notes
猜你喜欢

Mysql---- function

Database learning - Database Security

Interview shock 62: what are the precautions for group by?

可视化任务编排&拖拉拽 | Scaleph 基于 Apache SeaTunnel的数据集成

OSI 七层模型

Misc Basic test method and knowledge points of CTF

Ten billion massage machine blue ocean, difficult to be a giant

DVWA range clearance tutorial

Fr exercise topic - simple question
![[12 classic written questions of array and advanced pointer] these questions meet all your illusions about array and pointer, come on!](/img/d2/c0a19c85b2011ecd07c9944d996c4d.png)
[12 classic written questions of array and advanced pointer] these questions meet all your illusions about array and pointer, come on!
随机推荐
easyOCR 字符识别
Bugku telnet
Long list optimized virtual scrolling
NBA赛事直播超清画质背后:阿里云视频云「窄带高清2.0」技术深度解读
想问下大家伙,有无是从腾讯云MYSQL同步到其他地方的呀?腾讯云MySQL存到COS上的binlog
如何将 DevSecOps 引入企业?
Leetcode: Shortest Word Distance II
MySQL之CRUD
Bugku alert
CODING DevSecOps 助力金融企业跑出数字加速度
超越PaLM!北大硕士提出DiVeRSe,全面刷新NLP推理排行榜
How can the boss choose programmers to help me with development?
CPU design practice - Chapter 4 practice task 3 use pre delivery technology to solve conflicts caused by related issues
qt creater断点调试程序详解
Talking about how dataset and dataloader call when loading data__ getitem__ () function
超越PaLM!北大碩士提出DiVeRSe,全面刷新NLP推理排行榜
Reconnaissance des caractères easycr
百亿按摩仪蓝海,难出巨头
What are CSRF, XSS, SQL injection, DDoS attack and timing attack respectively and how to prevent them (PHP interview theory question)
The elimination strategy of redis