当前位置:网站首页>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;
边栏推荐
- SQL Server learning notes
- Using tensorboard to visualize the training process in pytoch
- I include of spring and Autumn
- GPS原始坐标转百度地图坐标(纯C代码)
- Bugku alert
- Bugku's Ping
- Your childhood happiness was contracted by it
- [12 classic written questions of array and advanced pointer] these questions meet all your illusions about array and pointer, come on!
- Ctfshow web entry command execution
- 机器学习框架简述
猜你喜欢
B站做短视频,学抖音死,学YouTube生?
OSI 七层模型
I spring web upload
Bugku alert
Common PHP interview questions (1) (written PHP interview questions)
Run faster with go: use golang to serve machine learning
Good article inventory
swiper. JS to achieve barrage effect
DVWA range clearance tutorial
How can I quickly check whether there is an error after FreeSurfer runs Recon all—— Core command tail redirection
随机推荐
What are CSRF, XSS, SQL injection, DDoS attack and timing attack respectively and how to prevent them (PHP interview theory question)
Mongdb learning notes
Coding devsecops helps financial enterprises run out of digital acceleration
lv_font_conv离线转换
Talk about your understanding of microservices (PHP interview theory question)
Redis distributed lock principle and its implementation with PHP (2)
Ionic Cordova project modification plug-in
Garbage collection mechanism of PHP (theoretical questions of PHP interview)
How can the boss choose programmers to help me with development?
Type declaration of all DOM elements in TS
JMeter performance test: serveragent resource monitoring
I want to inquire about how to ensure data consistency when a MySQL transaction updates multiple tables?
我想咨询一下,mysql一个事务对于多张表的更新,怎么保证数据一致性的?
Redis distributed lock principle and its implementation with PHP (1)
Aike AI frontier promotion (7.5)
【jvm】运算指令
Long list optimized virtual scrolling
[recruitment position] infrastructure software developer
Install PHP extension spoole
[12 classic written questions of array and advanced pointer] these questions meet all your illusions about array and pointer, come on!