当前位置:网站首页>wxml2canvas
wxml2canvas
2022-07-05 15:21:00 【Shepherd Wolf】
Catalog
introduce wxml2canvas library , Directory as follows :
Document address
https://github.com/liudongyun1215/wxml2canvashttps://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;
边栏推荐
- No one consults when doing research and does not communicate with students. UNC assistant professor has a two-year history of teaching struggle
- Ctfshow web entry explosion
- 百亿按摩仪蓝海,难出巨头
- Live broadcast preview | how to implement Devops with automatic tools (welfare at the end of the article)
- Ten billion massage machine blue ocean, difficult to be a giant
- The difference between abstract classes and interfaces in PHP (PHP interview theory question)
- Behind the ultra clear image quality of NBA Live Broadcast: an in-depth interpretation of Alibaba cloud video cloud "narrowband HD 2.0" technology
- 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
- Super wow fast row, you are worth learning!
- Change multiple file names with one click
猜你喜欢
Optional parameters in the for loop
Bubble sort, insert sort
Super wow fast row, you are worth learning!
Redis' transaction mechanism
P1451 求细胞数量/1329:【例8.2】细胞
Visual task scheduling & drag and drop | scalph data integration based on Apache seatunnel
Install and configure Jenkins
百亿按摩仪蓝海,难出巨头
MySQL之CRUD
Bugku easy_ nbt
随机推荐
Number protection AXB function! (essence)
Ecotone technology has passed ISO27001 and iso21434 safety management system certification
Array sorting num ranking merge in ascending order
Interview shock 62: what are the precautions for group by?
1330: [example 8.3] minimum steps
Creation and optimization of MySQL index
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
P1451 求细胞数量/1329:【例8.2】细胞
Coding devsecops helps financial enterprises run out of digital acceleration
B站做短视频,学抖音死,学YouTube生?
Thymeleaf uses background custom tool classes to process text
[recruitment position] Software Engineer (full stack) - public safety direction
Stm32+bh1750 photosensitive sensor obtains light intensity
Common PHP interview questions (1) (written PHP interview questions)
Does maxcompute have SQL that can query the current storage capacity (KB) of the table?
Ctfshow web entry explosion
超越PaLM!北大硕士提出DiVeRSe,全面刷新NLP推理排行榜
CODING DevSecOps 助力金融企业跑出数字加速度
easyOCR 字符識別
把 ”中台“ 的思想迁移到代码中去