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. tensorflow实战讨论

    欢迎关注微信公众号:樱园的玻尔兹曼机

  2. CentOS下安装Docker-CE

    1.安装最新版本的话可以使用阿里云的自动安装脚本: curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun 2.安装指定的 ...

  3. [INet] WebSocket 数据收发的详细过程

    WebSocket 和 HTTP 相似,只是一个应用层协议,对下层透明,所以不涉及 TCP/IP. 由于浏览器支持了 WebSocket,所以在用 JS 写客户端的时候,是无需考虑数据的编码解码的. ...

  4. python re模块与正则表达式

    首先要先继承re模块: import re re.findall() 方法 # 返回值为列表 \w 表示一个字符,为数字,字母,下滑线之一, \W匹配任意非数字,字母,下划线 print(re.fin ...

  5. 微信小程序--数据存储

    对本地缓存数据操作分为同步和异步两种.同步方法有成功回调函数,表示数 据处理成功后的操作.下面是小程序提供本地缓存操作接口: 以Sync结尾都是同步方法.同步方法和异步方法的区别是: 同步方法会堵塞当 ...

  6. Ubuntu安装软件提示boot空间不足

    用sudo apt-get install gitlab-ci-multi-runner安装应用都会出现“gzip: stdout: No space left on device”的问题. boot ...

  7. python argparse(参数解析)模块学习(一)

    class ArgumentParser(_AttributeHolder, _ActionsContainer): """Object for parsing comm ...

  8. Apache ab 压力并发测试工具

    当你使用PHP(或其他编程语言)完成一个web程序的开发,并且web程序在Apache服务器上正常运行的时候,你有没有考虑过对你的Apache服务器及部署在其上的web程序进行一些压力测试呢?毕竟,真 ...

  9. 顶级项目孵化的故事系列——Kylin的心路历程【转】

    现在已经名满天下的 Apache Kylin,是 Hadoop 大数据生态系统不可或缺的一部分,要知道在 Kylin 项目早期,可是以华人为主的开源团队,一路披荆斩棘经过几年的奋斗,才在 Apache ...

  10. 网站开发,推荐使用SuperSlide 插件-Tab标签切换,图片滚动,无缝滚动,焦点图

    SuperSlide 致力于解决网站大部分特效展示问题,使网站代码规范整洁,方便维护更新.网站上常用的“焦点图/幻灯片”“Tab标签切换”“图片滚动”“无缝滚动”等只需要一个SuperSlide即可解 ...