画布生成课程表
简述:先说一下,实现目标。
so,上图
因为,项目需求,需要把动态的课程表,实现点击保存按钮,实现将如上图一样的课程表保存到本地。
那么,实现的思路就是:
画布画出来,然后保存到本地
开发框架:uni,
开发工具:Hbulid-X,
既然有了思路,那么就好办了吧,我把课表分成了四个部分去画
1.左边的时间
2.顶部的场馆信息+底部的照片
3.底部的日期显示
4.课程表内容显示
把一张大图拆分四个部分画就很简单了。
但是,说一下,uni,编译成小程序的坑,首先,uni不支持es10的写法,亲测有效。
例如 :项目中用到了 es10的String.trimStart() //截取字符串前面的空格 ,然后换成了String.trim()使用
此处已班生成课程表照片,封装成组件。话不多说,上菜
index.vue
<template>
<view>
<canvasCourse :courseList='courseList' :variableMonth='variableMonth' :venueInfo='venueInfo' ></canvasCourse>
</view>
</template>
<script>
import canvasCourse from '@/components/canvasCourse.vue';
export default {
components: {
canvasCourse,
},
data() {
return {
weeks: ['一', '二', '三', '四', '五', '六', '日'],
variableMonth: ["7-10", "10-10", "7-10", "10-10", "7-10", "10-10", "9-30", "10-10"],
venueInfo: {
title: '王府健身',
codeImg: '/static/table.png',
logoImg: '/static/login.png'
},
courseList: [{
startTime: '13.00', //用来显示纵表头课程的开始时间
list: [{
timeSlot: "8:00-12:00", //课程时间段
courseName: "塑形", //课程名称
coash: "rechel", //教练
courseStar: 5, //课程难度
courseColor: 'background: red;'
},
{
timeSlot: "8:00-12:00", //课程时间段
courseName: "减肥", //课程名称
coash: "rechel", //教练
courseStar: 5, //课程难度
courseColor: 'background: yellow;'
},
{
timeSlot: "8:00-12:00", //课程时间段
courseName: "减肥", //课程名称
coash: "rechel", //教练
courseStar: 5, //课程难度
courseColor: 'background: green;'
},
{
timeSlot: "8:00-12:00", //课程时间段
courseName: "减肥", //课程名称
coash: "rechel", //教练
courseStar: 5, //课程难度
courseColor: 'background: green;'
},
{
timeSlot: "8:00-12:00", //课程时间段
courseName: "减肥", //课程名称
coash: "rechel", //教练
courseStar: 5, //课程难度
courseColor: 'background: green;'
},
{
timeSlot: "8:00-12:00", //课程时间段
courseName: "减肥", //课程名称
coash: "rechel", //教练
courseStar: 5, //课程难度
courseColor: 'background: green;'
},
{
timeSlot: "8:00-12:00", //课程时间段
courseName: "减肥", //课程名称
coash: "rechel", //教练
courseStar: 5, //课程难度
courseColor: 'background: green;'
}
],
},
{
startTime: '20.00', //用来显示纵表头课程的开始时间
list: [{
timeSlot: "8:00-12:00", //课程时间段
courseName: "塑形", //课程名称
coash: "rechel", //教练
courseStar: 5, //课程难度
courseColor: 'background: red;'
},
{
timeSlot: "8:00-12:00", //课程时间段
courseName: "减肥", //课程名称
coash: "rechel", //教练
courseStar: 5, //课程难度
courseColor: 'background: yellow;'
},
{
timeSlot: "8:00-12:00", //课程时间段
courseName: "减肥", //课程名称
coash: "rechel", //教练
courseStar: 5, //课程难度
courseColor: 'background: green;'
},
{
timeSlot: "8:00-12:00", //课程时间段
courseName: "减肥", //课程名称
coash: "rechel", //教练
courseStar: 5, //课程难度
courseColor: 'background: green;'
},
{
timeSlot: "8:00-12:00", //课程时间段
courseName: "减肥", //课程名称
coash: "rechel", //教练
courseStar: 5, //课程难度
courseColor: 'background: green;'
},
{
timeSlot: "8:00-12:00", //课程时间段
courseName: "减肥", //课程名称
coash: "rechel", //教练
courseStar: 5, //课程难度
courseColor: 'background: green;'
},
{
timeSlot: "8:00-12:00", //课程时间段
courseName: "减肥", //课程名称
coash: "rechel", //教练
courseStar: 5, //课程难度
courseColor: 'background: green;'
}
],
},
],
// ctxTotalH: null,
}
},
onReady() {
// this.draw()
},
onLoad() {
},
methods: {
}
}
</script>
<style scoped=''>
</style>
课程表组件
course.vue
<template>
<view>
<canvas canvas-id="myCanvas" :style="{height:canvasH}" />
<!-- <canvas canvas-id="myCanvas" :style="{width:canvasW,height:canvasH,border: '1px solid black',background:'#FFFFFF',}" /> -->
<button @tap="save"> 保存 </button>
</view>
</template>
<script>
export default {
props: {
courseList: {
type: Array,
// default: [{
// startTime: '8.00', //课程开始时间
// list: [{
// timeSlot: "8:00-12:00", //课程时间段
// courseName: "塑形", //课程名称
// coash: "rechel", //教练
// courseStar: 5, //课程难度
// courseColor: 'background: red;'
// }]
// }]
},
variableMonth: {
type: Array,
},
venueInfo: {
type: Object,
},
},
data() {
return {
canvasW: 1800 + 'upx',
canvasH: '0vh', //100 + 'vh',
ctxTotalH: 860,
weeks: ['一', '二', '三', '四', '五', '六', '日'],
}
},
mounted() {
// console.log(canvasH,canvasW,'canvasH');
// this.draw()//绘制课表
},
created() {
},
methods: {
drawVenueInfo(ctx, centerX, centerY) {
let height = (parseInt(this.canvasH)) * (this.courseList.length + 4) //初始化高度*行数
this.ctxTotalH = height
console.log(this.ctxTotalH, height, this.canvasH, "height")
let logoHeight = 200 + 100 * this.courseList.length //画布总高度
//顶部瑜伽场馆信息
let img = "/static/time.png"
ctx.drawImage(img, 30, 20, 50, 50) //x y W H
ctx.drawImage(this.venueInfo.logoImg, centerX, 10, 80, 80)
ctx.setFontSize(20)
let logoX = centerX + 120
ctx.fillText(this.venueInfo.title, logoX, centerY - 5);
ctx.fillText(this.variableMonth[0], logoX + 150, centerY - 5); //所在周周一
ctx.fillText('——', logoX + 150 + 60, centerY - 5); //所在周周一
ctx.fillText(this.variableMonth[6] + '课程表', logoX + 150 + 60 + 70, centerY - 5); //所在周周日
//左下角固定照片
ctx.drawImage('/static/leftLogo.png', 30, logoHeight, 150, 150)
//右下角瑜伽场馆二维码信息
ctx.drawImage(this.venueInfo.codeImg, 580, logoHeight - 10, 130, 130)
ctx.setFontSize(12)
ctx.fillText('长按识别二维码立即约课', 600 + 130, logoHeight + 50); //所在周周日
ctx.fillText('翼速网络有限公司技术支持', 600 + 130, logoHeight + 50 + 20); //所在周周日
},
drawTime(ctx, row, centerY) {
let oneWordX = 30 //距离左边30
let oneWordY = oneWordY = row * 100 + 90 + centerY
ctx.setFillStyle('white') //矩形背景色
ctx.fillRect(oneWordX, oneWordY, 70, 100) //画矩形
ctx.stroke(); //对当前路径进行描边
ctx.setFillStyle('black')
ctx.fillText(this.courseList[row].startTime, oneWordX + 15, oneWordY + 50);
},
drawDateTitle(ctx, centerY) {
let allRight = 100
for (let i = 0; i < 7; i++) {
let oneX = i * 110
let oneWordX = i * 110 + 30 + allRight
if (oneWordX == 0) {
oneWordX = 10
}
ctx.setFontSize(14)
ctx.setFillStyle('green')
ctx.fillText(this.variableMonth[i], oneWordX, 50 + centerY);
ctx.fillText("周" + this.weeks[i], oneWordX, 50 + 20 + centerY);
}
},
drawCourse(ctx, row, centerY) {
//画日期
// let 整体右移
let allRight = 100
let startHeight = row * 100 + 90 + centerY
//画一行课程表
for (let i = 0; i < 7; i++) {
let oneX = i * 110
let oneWordX = i * 110 + 10 + allRight
if (oneWordX == 0) {
oneWordX = 10
}
//文字要居中,等于矩形的一半高度
if (this.courseList[row] != null) {
let one = this.courseList[row].list[i]
let abc = this.courseList[row].list[i].courseColor
let b = abc.substring(abc.indexOf(':') + 1, abc.length);
//.replace(/^\s+|\s+$/g,"") 清楚两端空格
let color = b.substring(1, b.length - 1).replace(/^\s+|\s+$/g, "")
// let color = b.split(';')[0].trimStart() //trimStart()去掉空格 转化课程背景色
ctx.setStrokeStyle('#f2f2f2')
ctx.strokeRect(oneX + allRight, startHeight, 100, 100) //绘制无填充矩形边框
ctx.setFillStyle(color)
ctx.fillRect(oneX + allRight, startHeight, 99, 99) //绘制填充矩形 给矩形背景色 内容宽高小于有边框矩形宽高
ctx.setFontSize(14)
ctx.setFillStyle('white') //字体颜色
ctx.fillText(one.courseName, oneWordX, startHeight + 30); //渲染字体
ctx.fillText(one.coash, oneWordX, startHeight + 30 + 20);
ctx.fillText(one.timeSlot, oneWordX, startHeight + 30 + 40);
//画难度系数星星
let img;
if (!one.courseStar) {
one.courseStar = 0
} else {
for (let i = 0; i < 5; i++) {
if (i < parseInt(one.courseStar)) {
img = "/static/xing.png"
ctx.drawImage(img, oneWordX + 10 * i + 2, startHeight + 50 + 30, 10, 10)
} else {
img = "/static/xing1.png"
ctx.drawImage(img, oneWordX + 10 * i + 2, startHeight + 50 + 30, 10, 10)
}
}
}
}
}
},
draw() {
//画布顶部标题中心点
let centerX = 250 //300
let centerY = 50 //
const ctx = uni.createCanvasContext('myCanvas', this) //编译成小程序需要给canvas上下文绑定this指向
// const ctx = uni.createCanvasContext('myCanvas').bind(this)
ctx.fillStyle = "#FFFFFF"; //画布默认颜色色
let courseListCount = this.courseList.length
this.drawDateTitle(ctx, centerY) //画日期
this.drawVenueInfo(ctx, centerX, centerY) //画获取场馆信息
for (let i = 0; i < courseListCount; i++) {
//画一行 this.courseList[i]
this.drawTime(ctx, i, centerY)
this.drawCourse(ctx, i, centerY)
if (i > 0) {
this.canvasH = (i * centerY + 100) + 'vh' //动态获取画布高度 总高度是每一行之和 多余的100是展示logo和二维码
console.log(this.canvasH, '9999999999999');
}
}
ctx.draw()
},
save() {
console.log('保存');
this.draw()
console.log('画完了');
this.packSave()
console.log('保存到手机相册');
},
packSave() {
wx.showToast({
title: '分享图片生成中...',
icon: 'loading',
duration: 1000
});
let ctxTotalH = this.ctxTotalH
console.log(ctxTotalH, 'save 画布高度');
uni.showModal({
title: '是否保存为照片到本地',
success: (res) => {
console.log("eeeeeeeeee")
if (res.confirm) {
console.log("eeeeeeeeee2")
uni.canvasToTempFilePath({
x: 0,
y: 0,
width: 1800,
height: 1800,
destWidth: 900,
destHeight: 900,
canvasId: 'myCanvas',
fileType: 'jpg',
success: (res) => {
this.canvasImg = res.tempFilePath
console.log(this.canvasImg, '生成的照片')
this.saveImg()
}
}, this)
}
}
})
},
saveImg() {
uni.saveImageToPhotosAlbum({
filePath: this.canvasImg,
success: () => {
console.log('save success');
uni.showToast({
title: '保存成功',
success: () => {
setTimeout(() => {
uni.navigateBack({
delta: 2
})
}, 200)
}
})
}
})
},
}
}
</script>
<style scoped=''>
canvas {
background: white;
width: 2200upx;
border: 1px solid black;
background: #fff;
}
button {
font-size: 30upx;
height: 100upx;
line-height: 100upx;
background: #1ECFA7;
position: fixed;
left: 10upx;
bottom: 100upx;
color: white;
border-radius: 50%;
}
</style>