crazy-poster-uni
Version:
uniapp html2canvas.
1 lines • 11 kB
JavaScript
const deepClone=obj=>{if(obj===null||typeof obj!=="object"){return obj}let clone=Array.isArray(obj)?[]:{};for(let key in obj){if(obj.hasOwnProperty(key)){clone[key]=deepClone(obj[key])}}return clone};export default class JzWxml2Canvas{constructor(options={}){this.drawClass=options.draw;this.limit=options.limit;this.canvas=options.canvas;this.finish=options.finish;this.fail=options.fail;this.scope=options.scope;this.oaid=options.oaid;try{this._init()}catch(error){const errorObj={msg:"初始化失败",error:error};this.fail(errorObj);throw new Error("初始化失败")}}draw(){this._draw()}_init(){this.ctx=uni.createCanvasContext(this.canvas);this._wxmlInitial()}_wxmlInitial(){try{this._getWxml().then(element=>{const fatherOrigin={x:element[1].left,y:element[1].top};const regex=/url\("([^"]+)"\)/;const match=element[1].backgroundImage.match(regex);const arr=element[0].map((item,index,array)=>{item.left=item.left-fatherOrigin.x;item.top=item.top-fatherOrigin.y;item._width=parseInt(item.width);item._height=parseInt(item.height);if(parseInt(item.borderWidth)){item.left=item.left+parseInt(item.borderWidth);item.top=item.top+parseInt(item.borderWidth);item._height=item._height+parseInt(item.borderWidth);item._width=item._width+parseInt(item.borderWidth)}if(item.dataset&&item.dataset.type==="text-struct"&&array[index+1].dataset.text){array[index+1].maxWidth=item._width}return item});element[1]["_width"]=parseInt(element[1].width);element[1]["_height"]=parseInt(element[1].height);const cloneElement1={...deepClone(element[1]),dataset:{url:match&&match[1]},full:true};arr.unshift(cloneElement1);this.allDraw=arr;this.father=element[1]}).catch(err=>{console.log("获取wxml元素失败err ------\x3e",err);const errorObj={msg:"获取wxml元素失败",error:err};this.fail(errorObj);throw new Error("获取wxml元素失败")})}catch(error){const errorObj={msg:"wxml元素初始化失败",error:error};this.fail(errorObj);throw new Error("wxml元素初始化失败")}}_draw(){console.log("this.allDraw",this.allDraw);console.log("绘制元素长度",this.allDraw.length);try{this.allDraw.map(element=>{if(element.dataset&&element.dataset.url){this._drawImage(element,element.full)}else if(element.dataset&&element.dataset.type==="line"){this._drawBorder(element)}else if(element.dataset&&element.dataset.type==="rect"){this._drawRectangle(element)}else if(element.dataset&&element.dataset.text){this._drawText(element)}})}catch(error){console.log("绘制过程失败error ------\x3e",error);const errorObj={msg:"绘制过程失败",error:error};this.fail(errorObj);throw new Error("绘制过程失败")}try{this.ctx.draw(true,()=>{setTimeout(()=>{uni.canvasToTempFilePath({x:0,y:0,canvasId:this.canvas,success:async res=>{const{tempFilePath:tempFilePath}=res;this.finish(tempFilePath)},fail:err=>{const errorObj={msg:"canvas转图片失败",error:err};this.fail(errorObj);throw new Error("canvas转图片失败")}})},500)})}catch(error){const errorObj={msg:"绘制失败",error:error};this.fail(errorObj);throw new Error("绘制失败")}}_getWxml(){const promise_all=new Promise((resolve,reject)=>{uni.createSelectorQuery().in(this.scope).selectAll(this.drawClass).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","borderRadius","borderBottom"]},res=>{resolve(res)}).exec()});const promise_limit=new Promise((resolve,reject)=>{uni.createSelectorQuery().in(this.scope).select(this.limit).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=>{resolve(res)}).exec()});return Promise.all([promise_all,promise_limit])}_preloadImage(arr){const self=this;const promiseArr=[];this.allDraw=arr.map(item=>{if(item.dataset&&item.dataset.url&&!item.dataset.url.includes("skip")){const{url:url}=item.dataset;promiseArr.push(this.downloadImage_tool(url,url=>{item.url=url}))}return item});return Promise.all(promiseArr)}_drawBoldText(text,x,y){const left=x,top=y;const distance=.01;this.ctx.fillText(text,left,top)}_drawText(element){const{dataset:dataset,fontWeight:fontWeight,fontFamily:fontFamily,fontSize:fontSize,color:color,left:left,top:top,maxWidth:maxWidth=0,lineHeight:lineHeight}=element;const _fontWeight=parseInt(fontWeight);const _fontSize=parseInt(fontSize);const text=dataset.text;let y=top+_fontSize;const whichDrawText=(text="",x=0,y=0)=>{if(_fontWeight>=500){this._drawBoldText(text,x,y)}else{this.ctx.fillText(text,x,y)}};this.ctx.save();this.ctx.font=`${_fontWeight} ${_fontSize}px ${fontFamily}`;this.ctx.setFillStyle(color);if(maxWidth){let line="";const _lineHeight=parseInt(lineHeight);const result=[];for(let i=0;i<text.length;i++){const testLine=line+text[i];const metrics=this.ctx.measureText(testLine);const testWidth=metrics.width;if(testWidth>maxWidth&&line.length>0){result.push(line);line=text[i]}else{line=testLine}}result.push(line);result.map(line=>{whichDrawText(line,left,y);y+=_fontSize+_lineHeight/2})}else{whichDrawText(text,left,y)}this.ctx.restore()}_drawImage(element,full=false){const{url:url,left:left,top:top,_width:_width,_height:_height,borderRadius:borderRadius=0}=element;const radius=parseInt(borderRadius);const x=full?0:left;const y=full?0:top;this.ctx.save();if(radius){this.ctx.beginPath();this.ctx.moveTo(left+radius,top);this.ctx.lineTo(left+_width-radius,top);this.ctx.quadraticCurveTo(left+_width,top,left+_width,top+radius);this.ctx.lineTo(left+_width,top+_height-radius);this.ctx.quadraticCurveTo(left+_width,top+_height,left+_width-radius,top+_height);this.ctx.lineTo(left+radius,top+_height);this.ctx.quadraticCurveTo(left,top+_height,left,top+_height-radius);this.ctx.lineTo(left,top+radius);this.ctx.quadraticCurveTo(left,top,left+radius,top);this.ctx.closePath();this.ctx.clip()}this.ctx.drawImage(element.dataset.url,x,y,_width,_height);this.ctx.restore()}_drawRectangle(element){const{backgroundImage:backgroundImage,borderWidth:borderWidth}=element;if(borderWidth.split(" ").length>1||borderWidth.split(" ").length===1&&parseInt(borderWidth)){this._drawBorder(element)}if(backgroundImage.includes("linear-gradient")){this._drawLinearGradient(element)}else{this._drawBackground(element)}}_drawBorder(element){const{borderColor:borderColor,borderWidth:borderWidth,left:left,top:top,_width:_width,_height:_height}=element;const borderWidths=borderWidth.split(" ").map(value=>Math.round(parseFloat(value)||0));const borderColors=borderColor.split(") ").map((i,index,arr)=>index+1!==arr.length?i+")":i);this.ctx.save();if(borderWidths.length===1){this.ctx.setStrokeStyle(borderColor);this.ctx.setLineWidth(parseInt(borderWidth,10))}else{const topBorder=borderWidths[0];const leftRightBorder=borderWidths[1];const bottomBorder=borderWidths[2];const topColor=borderColors[0];const leftRightColor=borderColors[1];const bottomColor=borderColors[2];if(topBorder){this.ctx.beginPath();this.ctx.setLineWidth(topBorder);this.ctx.setStrokeStyle(topColor);this.ctx.moveTo(left,top-.5);this.ctx.lineTo(left+_width,top-.5);this.ctx.stroke()}if(leftRightBorder){this.ctx.beginPath();this.ctx.setLineWidth(leftRightBorder);this.ctx.setStrokeStyle(leftRightColor);this.ctx.moveTo(left,top-.5);this.ctx.lineTo(left,top+_height-.5);this.ctx.stroke()}if(leftRightBorder){this.ctx.beginPath();this.ctx.setLineWidth(leftRightBorder);this.ctx.setStrokeStyle(leftRightColor);this.ctx.moveTo(left+_width,top-.5);this.ctx.lineTo(left+_width,top+_height-.5);this.ctx.stroke()}if(bottomBorder){this.ctx.beginPath();this.ctx.setLineWidth(bottomBorder);this.ctx.setStrokeStyle(bottomColor);this.ctx.moveTo(left+_width,top+_height-.5);this.ctx.lineTo(left,top+_height-.5);this.ctx.stroke()}}this.ctx.restore()}_drawLinearGradient(element,isFull=false){const{backgroundImage:backgroundImage,left:left,top:top,_width:_width,_height:_height,borderRadius:borderRadius}=element;const matchArr=this.matchAllRgb(backgroundImage);const isToBottom=backgroundImage.includes("540deg");const _left=isFull?0:left;const _top=isFull?0:top;const orientation=()=>{if(isToBottom)return[left,top,left,top+_height];return[left,top,left+_width,top]};const grd=this.ctx.createLinearGradient(...orientation());const radius=borderRadius?parseInt(borderRadius):0;this.ctx.save();grd.addColorStop(0,matchArr[0]);grd.addColorStop(1,matchArr[1]);this.ctx.setFillStyle(grd);if(radius){this._drawRadius(element)}else{this.ctx.fillRect(_left,_top,_width,_height)}this.ctx.restore()}_drawBackground(element,isFull=false){const{left:left,top:top,_width:_width,_height:_height,backgroundColor:backgroundColor,borderRadius:borderRadius}=element;const _left=isFull?0:left;const _top=isFull?0:top;const radius=borderRadius?parseInt(borderRadius):0;this.ctx.save();this.ctx.setFillStyle(backgroundColor);if(radius){this._drawRadius(element)}else{this.ctx.fillRect(_left,_top,_width,_height)}this.ctx.restore()}_drawRadius(element){const{left:left,top:top,_width:_width,_height:_height,borderRadius:borderRadius}=element;const differentCornerComputed=()=>{const radius=borderRadius.split(" ").map(value=>parseInt(value)||0);switch(radius.length){case 1:return[radius[0],radius[0],radius[0],radius[0]];case 2:return[radius[0],radius[1],radius[0],radius[1]];case 3:return[radius[0],radius[1],radius[2],radius[1]];case 4:return radius;default:return[0,0,0,0]}};const radius=differentCornerComputed();this.ctx.save();this.ctx.beginPath();this.ctx.moveTo(left+radius[0],top);this.ctx.lineTo(left+_width-radius[1],top);this.ctx.quadraticCurveTo(left+_width,top,left+_width,top+radius[1]);this.ctx.lineTo(left+_width,top+_height-radius[2]);this.ctx.quadraticCurveTo(left+_width,top+_height,left+_width-radius[2],top+_height);this.ctx.lineTo(left+radius[3],top+_height);this.ctx.quadraticCurveTo(left,top+_height,left,top+_height-radius[3]);this.ctx.lineTo(left,top+radius[0]);this.ctx.quadraticCurveTo(left,top,left+radius[0],top);this.ctx.fill();this.ctx.closePath();this.ctx.restore()}downloadImage_tool(url,callback){return new Promise((resolve,reject)=>{uni.downloadFile({url:url.replace("http://","https://"),success:res=>{const{statusCode:statusCode,tempFilePath:tempFilePath}=res;const isFilePathOK=!tempFilePath.includes(".json");if(statusCode===200&&isFilePathOK){callback(tempFilePath);resolve(tempFilePath)}else{reject(errMsg)}},fail:()=>{reject(errMsg)}})})}matchAllRgb(str,regexStr="rgb"){const regex=new RegExp(`${regexStr}a?\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*(?:,\\s*\\d*\\.?\\d+\\s*)?\\)`,"g");const matches=str.match(regex);return matches||[]}}