wxml

<view class="shareBox" style="backgound:{{isShow ? '#000' : '#fff'}}" wx:if="{{isShow && canvasList}}">
<canvas canvas-id="firstCanvas" class="canvas myCanvas" style="width:{{canvasWidth}}px;height:{{canvasHeight}}px;margin-top:calc((100vh - 54px - {{canvasHeight}}px) / 2)"></canvas>
<button class="saveImg {{iphoneX ? 'iphonex' : ''}}" bindtap="saveImage" disabled="{{btnShow}}">保存图片</button>
</view>
<view wx:else class="refresh">
<button size="default" type="primary" bindtap="readyCanvas">重新加载</button>
</view>

  

js

import regeneratorRuntime from "../../../lib/regenerator-runtime/runtime";
let ctx = false, crown = 0, widFit = 0 // ctx canvas对象, crown生成图的宽高比, widFit当然布局下与需生成图寛比, heiFit高度比
Component({
externalClasses:['myCanvas'],
properties: {
canvasList: {
type: Array,
value: []
},
getShareWidth: { //想得到的分享图宽度
type: Number,
value: 1080
}, getShareHeight: { //想得到的分享图高度
type: Number,
value: 1900
} }, /**
* 组件的初始数据
*/
data: {
canvasWidth: 375, //屏幕宽度
canvasHeight: 375, //屏幕高度
isShow: true, //canvas组件默认显示
iphoneX: false, //适配机型
btnShow: false, //阻止事件多次触发
}, /**
* 组件的方法列表
*/
methods: {
async canvasStart() {
this.drawbackColor()
const canvasList = this.data.canvasList
await canvasList.map( (v,k)=>{
if(v.type === 'backImage'){
this.drawBackImg(v.url)
}else if(v.type === 'image'){
this.drawContentImg(v)
}else if(v.type === 'line'){
this.drawLine(v.drawLine)
}else{
this.drawText(v.text,v.drawText)
}
}) ctx.draw()
wx.hideLoading()
// this.saveImage()
}, //画默认白底色
drawbackColor(){
const { canvasWidth, canvasHeight } = this.data
ctx.save()
ctx.rect(0, 0, canvasWidth, canvasHeight)
ctx.setFillStyle('white')
ctx.fill()
}, // 画背景图
drawBackImg(url) {
// console.log('drawBackImg',url)
const {
canvasWidth,
canvasHeight
} = this.data
ctx.save()
ctx.drawImage(url, 0, 0, canvasWidth, canvasHeight)
}, // 画图不用裁剪(查看小程序canvas api 文档 https://developers.weixin.qq.com/miniprogram/dev/api/CanvasContext.drawImage.html)
drawImg(url, drawArguments) {
const arg = Object.keys(drawArguments)
if (arg.length == 8) { const { sx, sy, sWidth, sHeight, dwX, dwY, dWidth, dHeight } = drawArguments
ctx.drawImage(url, sx * widFit, sy * widFit, sWidth * widFit, sHeight * widFit, dwX * widFit, dwY * widFit, dWidth * widFit, dHeight * widFit) } else if (arg.length == 4) { const { dwX, dwY, dWidth, dHeight } = drawArguments
ctx.drawImage(url, dwX * widFit, dwY * widFit, dWidth * widFit, dHeight * widFit) } else if (arg.length == 2) { const { dwX, dwY } = drawArguments
ctx.drawImage(url, dwX * widFit, dwY * widFit) } else { wx.showToast({
title: '背景图传入参数有误,请确认无误后再进行操作',
icon: 'none'
}) } }, // 画内容图 clip裁剪
drawContentImg(val){
const { url, clip, drawArguments, drawArc} = val if(clip){ //裁剪流图片
if(!url || !drawArguments || !drawArc){
wx.showToast({
title: '请确认drawContentImg参数无误',
icon: 'none'
})
return;
} const {
x,
y,
radius
} = drawArc
if ((x || x === 0) && (y || y === 0) && (radius || radius === 0)) {
const {
x,
y,
radius,
startRadian = 0,
endRadian = 2 * Math.PI
} = drawArc
ctx.save()
ctx.beginPath();
ctx.arc(x * widFit, y * widFit, radius * widFit, startRadian, endRadian) // arc(x坐标,y坐标,radius半径,startRadian起始弧度/单位弧度(默认在3点钟方向),endRadian终止弧度)
ctx.clip()
this.drawImg(url, drawArguments)
ctx.restore()
} else {
wx.showToast({
title: '画圆参数有误',
icon: 'none'
})
}
} else {
ctx.save()
this.drawImg(url, drawArguments)
ctx.restore()
} }, // 画文字
drawText(text,drawText){
const canvasWidth = this.data.canvasWidth
const exg = /[a-zA-Z0-9]/g
const { x, y, bold, maxWidth = canvasWidth, fontSize = 14, lineHeight = 0, color = 'white', textAlign = 'left' } = drawText // text文字内容, x画布X坐标, y画布y坐标, maxWidth最大宽度, fontSize字体大小, color文字颜色 const patchTextHeight = fontSize ? fontSize : lineHeight
if(!text || (!x && x !== 0) || (!y && y !== 0)){
wx.showToast({
title: '文字传入参数有误',
icon: 'none'
})
return
}
ctx.save()
ctx.setFillStyle(color) //设置文字颜色
ctx.setTextAlign(textAlign)
if(bold){
ctx.font = `${fontSize}px bold PingFangSC-Medium` //设置文字样式
}else{
ctx.setFontSize(fontSize) //设置文字字体
} const measure = ctx.measureText(text).width //测量文本宽度
const scale = Math.ceil(measure / (maxWidth * widFit)) //scale<1则 maxWidth>measure,1 <= scale < 2 则 maxWidth >= measure/2,scale >= 2 则 maxWidth <= measure / 2
let arr = [], count = 0 if(scale >= 2){
let fontNum = Math.floor((maxWidth / fontSize) * widFit) //每行最多字体个数
let patchVal = 0, patchY = y + patchTextHeight
for(var i = 0; i < scale; i++){
arr[i] = text.substr(patchVal,fontNum)
count = arr[i].match(exg) ? arr[i].match(exg).length / 2 - 1 : 0
// console.log('正则匹配:',ctx.measureText('和').width,ctx.measureText('B').width,ctx.measureText('a').width,ctx.measureText(1).width)
fontNum += count
if(i < scale - 1){
ctx.fillText(text.substr(patchVal,fontNum), x * widFit, patchY * widFit)
patchVal += fontNum
patchY += patchTextHeight
}else{
arr[i] = text.substr(patchVal)
ctx.fillText(text.substr(patchVal), x * widFit, patchY * widFit) //画最后剩下的内容
console.log('arr:',arr)
}
}
}else{
ctx.fillText(text, x * widFit, (y + patchTextHeight)* widFit);
console.log('felltext:',text,x*widFit,y*widFit)
} ctx.restore()
}, // 画线
drawLine(drwaLine){
const { sx, sy, ex, ey, lineWidth = 1, color = '#EBEBEB' } = drwaLine
if(!sx || !sy || !ex || !ey){
wx.showToast({
title: '画线参数有误',
icon: 'none'
})
return
}else{
ctx.save()
ctx.moveTo(sx, sy) //线起点
ctx.lineTo(ex, ey) //线终点
ctx.setLineWidth(lineWidth) //线宽度
ctx.setStrokeStyle(color) //线颜色
ctx.stroke()
ctx.restore()
}
}, // 保存图片
saveImage(){
wx.showLoading({
title: '生成图片中...',
mask: true
})
this.setData({
btnShow: true
})
const { getShareWidth, getShareHeight } = this.data
wx.canvasToTempFilePath({
destWidth: getShareWidth * 5,
destHeight: getShareHeight * 5,
canvasId: 'firstCanvas',
quality: 1,
complete: fin=>{
// console.log('finish',fin)
if(fin.tempFilePath){
wx.saveImageToPhotosAlbum({
filePath: fin.tempFilePath,
success: (res)=>{
wx.showToast({
title: '保存图片成功',
icon: 'none',
duration: 2000
})
this.setData({
btnShow: false
})
}
})
}else{
wx.showToast({
title: '生成图片失败',
icon: 'none'
})
this.setData({
btnShow: false,
})
} }
},this)
}, //转成本地图片
getImages(url){
return new Promise( (sovle,reject)=>{
wx.getImageInfo({
src: url,
success: (res)=>{
sovle(res.path)
},
fail: (err)=>{
wx.showToast({
title: '网络不好,请稍后再试',
icon: 'none'
})
this.setData({
isShow: false
})
}
});
}) }, // 画图前准备工作
async readyCanvas(){
const { windowWidth, model } = wx.getSystemInfoSync(); // 获取屏幕宽高 let { getShareWidth, getShareHeight, canvasList} = this.data
crown = getShareWidth / getShareHeight //分享图的宽高比
const canvasHeight = windowWidth / crown if(model === 'iPhone X'){
this.setData({
canvasWidth: windowWidth,
canvasHeight,
iphoneX: true
})
}else{
this.setData({
canvasWidth: windowWidth,
canvasHeight,
iphoneX: false
})
} ctx = wx.createCanvasContext('firstCanvas',this) //把ctx赋值给全局变量
widFit = windowWidth / getShareWidth //宽比 (以px为单位) for(let i = 0; i < canvasList.length; i++){
if(canvasList[i].url){
canvasList[i].url = await this.getImages(canvasList[i].url) // console.log(i,canvasList[i].url)
}
} this.setData({
canvasList,
isShow: true
},()=>{
console.log(getShareWidth,getShareHeight,canvasList)
this.canvasStart()
})
} }, lifetimes: { }, pageLifetimes: {
show(){
wx.showLoading({
title: '加载中...',
mask: true
})
this.readyCanvas()
}
}
})
// let shareList = { //传参例子
// canvasList: [
// {
// type: 'image',
// clip: false,
// url: courseInfo.bgImage, //顶部背景
// drawArguments:{
// dwX: 0,
// dwY: 0,
// dWidth: 375,
// dHeight: 209
// },
// },
// {
// type: 'text',
// text: courseInfo.courseTitle,
// drawText:{
// x: 20,
// y: 227,
// fontSize: 18,
// color: '#303030',
// bold: true
// }
// },
// {
// type: 'image',
// clip: true,
// url: courseInfo.coachesInfo[0].avatar, //教练头像
// drawArguments:{
// dwX: 20,
// dwY: 304,
// dWidth: 32,
// dHeight: 32
// },
// drawArc:{
// x: 36,
// y: 320,
// radius: 16,
// }
// },
// {
// type:'line',
// drawLine:{
// sx: 20, //开始x坐标
// sy: 348, //开始y坐标
// ex: 355, //结束x坐标
// ey: 348, //结束y坐标
// lineWidth: 0.5, //线宽度
// color: '#ebebeb' //线颜色
// }
// },
// ],
// getShareWidth: 375,
// getShareHeight: 463,
// pageTitle: courseInfo.courseTitle
// }

 wxss

.shareBox{
width: 100vw;
height: 100vh;
background: #000;
position: fixed;
left: 0;
top: 0;
}
.canvas{
background: #fff;
}
.saveImg{
width: 100%;
height: 108rpx;
line-height: 108rpx;
background: #00C3AA;
text-align: center;
font-size: 30rpx;
color: #FFFFFF;
border-radius: 0;
border: 0;
position: absolute;
left: 0;
bottom: 0;
}
.refresh{
width: 350rpx;
height: 92rpx;
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
margin: auto;
}
.iphonex{
height: 172rpx;
}

  

优化版小程序canvas,增加失败逻辑,及完善文字的更多相关文章

  1. 小程序Canvas性能优化实战

    以下内容转载自totoro的文章<小程序Canvas性能优化实战!> 作者:totoro 链接:https://blog.totoroxiao.com/canvas-perf-mini/ ...

  2. 技术博客--微信小程序canvas实现图片编辑

    技术博客--微信小程序canvas实现图片编辑 我们的这个小程序不仅仅是想给用户提供一个保存和查找的平台,还希望能给用户一个展示自己创意的舞台,因此我们实现了图片的编辑部分.我们对对图片的编辑集成了很 ...

  3. 微信小程序canvas生成并保存图片

    ---恢复内容开始--- 微信小程序canvas生成并保存图片,具体实现效果如下图     实现效果需要做以下几步工作 一.先获取用户屏幕大小,然后才能根据屏幕大小来定义canvas的大小 二.获取图 ...

  4. 微信小程序--canvas画布实现图片的编辑

    技术:微信小程序   概述 上传图片,编辑图片大小,添加文字,改变文字颜色等 详细 代码下载:http://www.demodashi.com/demo/14789.html 概述 微信小程序--ca ...

  5. [技术博客]海报图片生成——小程序canvas画布

    目录 背景介绍 canvas简介 代码实现 难点讲解 圆角矩形裁剪失败之PS的妙用 编码不要过硬 对过长的文字进行截取 真机首次生成时字体不对 drawImage只能使用本地图片 背景介绍 目标:利用 ...

  6. 原创:WeZRender:微信小程序Canvas增强组件

    WeZRender是一个微信小程序Canvas增强组件,基于HTML5 Canvas类库ZRender. 使用 WXML: <canvas style="width: 375px; h ...

  7. 记录一下小程序canvas

    小程序canvas学习 效果图: .wxml <canvas style="width: 100vw; height: 100vh;" canvas-id="fir ...

  8. 微信小程序-canvas绘制文字实现自动换行

    在使用微信小程序canvas绘制文字时,时常会遇到这样的问题:因为canvasContext.fillText参数为 我们只能设置文本的最大宽度,这就产生一定的了问题.如果我们绘制的文本长度不确定或者 ...

  9. 微信小程序 canvas 字体自动换行(支持换行符)

    微信小程序 canvas 自动适配 自动换行,保存图片分享到朋友圈  https://github.com/richard1015/News 微信IDE演示代码https://developers.w ...

随机推荐

  1. Rabbitmq 与springboot 结合

    <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring- ...

  2. openwrt从18.0.1降级回到17.0.6遇到的问题

    因为觉得openwrt的18的配置检查功能很费时,特别是遇到ar93xx慢的真可以,所以决定从18.0.1降回到17.0.6上 先把18.0.1的配置backup出来,然后刷17.0.6,再把back ...

  3. 记一次vcsa6修复过程

    一. 某天发现一台vmware  vCenter Server Appliance services 6偶尔能登陆了,但极不稳定,连shell都偶尔能进...... 然后利用各种手段想方设法进到she ...

  4. user story

    What is a user story? A user story is a short description of something that your customer will do wh ...

  5. NodeJs命令

    cd命令,就是change directory的缩写,表示更改当前目录 cls命令,清屏.清屏幕命令(CLS,CLear Screen) tab键,自动补全. 上键,提示最近的命令   在cmd窗口 ...

  6. CF 317 A. Lengthening Sticks(容斥+组合数学)

    传送门:点我 A. Lengthening Sticks  time limit per test        1 second You are given three sticks with po ...

  7. 【c++】内存检查工具Valgrind介绍,安装及使用以及内存泄漏的常见原因

    转自:https://www.cnblogs.com/LyndonYoung/articles/5320277.html Valgrind是运行在Linux上一套基于仿真技术的程序调试和分析工具,它包 ...

  8. 54. Spiral Matrix以螺旋顺序输出数组

    [抄题]: Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spi ...

  9. yum -y install php-mysql 版本冲突

    yum -y install  php-mysql 版本冲突 2018年09月02日 19:16:59 乐于技术分享 阅读数:640   [root@itop yum.repos.d]# yum -y ...

  10. 设计模式学习心得<抽象工厂模式 Abstract Factory>

    抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂.该超级工厂又称为其他工厂的工厂.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 在抽 ...