当前位置:网站首页>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;边栏推荐
- Type declaration of all DOM elements in TS
- R 熵权法计算权重及综合得分
- P1451 calculate the number of cells / 1329: [example 8.2] cells
- Stm32+bh1750 photosensitive sensor obtains light intensity
- 如何将 DevSecOps 引入企业?
- ICML 2022 | 探索语言模型的最佳架构和训练方法
- 超越PaLM!北大碩士提出DiVeRSe,全面刷新NLP推理排行榜
- Au - delà du PARM! La maîtrise de l'Université de Pékin propose diverse pour actualiser complètement le classement du raisonnement du NLP
- PHP high concurrency and large traffic solution (PHP interview theory question)
- SQL Server learning notes
猜你喜欢

Bugku's eyes are not real

Fr exercise topic --- comprehensive question
![P1451 calculate the number of cells / 1329: [example 8.2] cells](/img/c4/c62f3464608dbd6cf776c2cd7f07f3.png)
P1451 calculate the number of cells / 1329: [example 8.2] cells

Bugku cyberpunk

机器学习笔记 - 灰狼优化

Creation and use of thymeleaf template

Huawei Hubble incarnation hard technology IPO harvester

No one consults when doing research and does not communicate with students. UNC assistant professor has a two-year history of teaching struggle

华为哈勃化身硬科技IPO收割机

Change multiple file names with one click
随机推荐
STM32+BH1750光敏传感器获取光照强度
Misc Basic test method and knowledge points of CTF
Photoshop plug-in - action related concepts - actions in non loaded execution action files - PS plug-in development
12 MySQL interview questions that you must chew through to enter Alibaba
No one consults when doing research and does not communicate with students. UNC assistant professor has a two-year history of teaching struggle
I want to inquire about how to ensure data consistency when a MySQL transaction updates multiple tables?
如何将 DevSecOps 引入企业?
sql server学习笔记
CODING DevSecOps 助力金融企业跑出数字加速度
Database learning - Database Security
Usage and usage instructions of JDBC connection pool
Fr exercise topic - simple question
Selection and use of bceloss, crossentropyloss, sigmoid, etc. in pytorch classification
Surpass palm! Peking University Master proposed diverse to comprehensively refresh the NLP reasoning ranking
Install and configure Jenkins
做研究无人咨询、与学生不交心,UNC助理教授两年教职挣扎史
Want to ask the big guy, is there any synchronization from Tencent cloud Mysql to other places? Binlog saved by Tencent cloud MySQL on cos
Long list optimized virtual scrolling
市值蒸发超百亿美元,“全球IoT云平台第一股”赴港求生
我这边同时采集多个oracle表,采集一会以后,会报oracle的oga内存超出,大家有没有遇到的?