当前位置:网站首页>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;
边栏推荐
- Number protection AXB function! (essence)
- I spring and autumn blasting-1
- Aike AI frontier promotion (7.5)
- Your childhood happiness was contracted by it
- Ten billion massage machine blue ocean, difficult to be a giant
- JS topic - console log()
- Coding devsecops helps financial enterprises run out of digital acceleration
- 华为哈勃化身硬科技IPO收割机
- 超越PaLM!北大碩士提出DiVeRSe,全面刷新NLP推理排行榜
- 亿咖通科技通过ISO27001与ISO21434安全管理体系认证
猜你喜欢
Bugku's Eval
Behind the ultra clear image quality of NBA Live Broadcast: an in-depth interpretation of Alibaba cloud video cloud "narrowband HD 2.0" technology
qt creater断点调试程序详解
Live broadcast preview | how to implement Devops with automatic tools (welfare at the end of the article)
华为哈勃化身硬科技IPO收割机
可视化任务编排&拖拉拽 | Scaleph 基于 Apache SeaTunnel的数据集成
1330:【例8.3】最少步数
Run faster with go: use golang to serve machine learning
Thymeleaf uses background custom tool classes to process text
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!
Optional parameters in the for loop
Bugku's Ah Da
What are the domestic formal futures company platforms in 2022? How about founder metaphase? Is it safe and reliable?
CPU design practice - Chapter 4 practical task 2 using blocking technology to solve conflicts caused by related problems
Common PHP interview questions (1) (written PHP interview questions)
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
Fr exercise topic - simple question
Interpretation of Apache linkage parameters in computing middleware
Brief introduction of machine learning framework
Using tensorboard to visualize the training process in pytoch
Photoshop plug-in - action related concepts - actions in non loaded execution action files - PS plug-in development
CODING DevSecOps 助力金融企业跑出数字加速度
Reconnaissance des caractères easycr
Ctfshow web entry explosion
Install and configure Jenkins
Visual task scheduling & drag and drop | scalph data integration based on Apache seatunnel
百亿按摩仪蓝海,难出巨头
P6183 [USACO10MAR] The Rock Game S
Common MySQL interview questions