在现代Web和移动应用开发中,Canvas技术为我们提供了强大的图形绘制能力。本文将介绍如何使用uni-app框架结合Canvas技术,创建一个美观的健康风险评估报告页面,并实现长按保存功能。
实现思路
1. Canvas基础设置
首先,我们需要设置Canvas画布并获取其实际尺寸:
2. 获取Canvas尺寸
使用uni-app的API(createSelectorQuery)获取Canvas的实际尺寸:
3. 绘制双列表格
核心的绘制函数drawCanvas实现了双列表格布局,左侧key右侧对应value
完整代码如下
TypeScript
<template>
<view class="canvaspage">
<canvas
style="width: 100%; height: 100vh"
class="canvas"
ref="canvasbox"
canvas-id="canvasbox"
@longpress="downloadPic(state.createImgPath)"
></canvas>
</view>
</template>
<script setup lang="ts">
import { getCanvasSize } from "@/utils/canvas";
import { ref, onMounted, getCurrentInstance, nextTick, reactive } from "vue";
import { downloadPic } from "@/utils/common";
const canvasbox = ref<HTMLCanvasElement | null | any>(null);
const instance = getCurrentInstance();
const state = reactive({
createImgPath: "",
});
onMounted(() => {
console.log("onMounted", instance);
nextTick(() => {
drawCanvas();
});
});
async function drawCanvas() {
const context = uni.createCanvasContext("canvasbox");
const res = await getCanvasSize(".canvas", instance?.proxy);
context.setFillStyle("white"); //全局背景
const width = res.width;
const startY = 50; // 脑卒中标准 Y轴起始位置 30为上下间距
setCutLine(context, { x: 12, y: 30 }, width); // 分割线
// 重新计算布局
const leftMargin = 12; // 左边距
const rightMargin = 12; // 右边距
const tableWidth = width - leftMargin - rightMargin; // 表格总宽度
const columnWidth = tableWidth / 2; // 每列宽度(平均分配)
// 左列和右列的起始位置
const leftColumnStart = leftMargin;
const rightColumnStart = leftMargin + columnWidth;
// 更精确的文本宽度估算函数
const estimateTextWidth = (text: string) => {
let width = 0;
for (let i = 0; i < text.length; i++) {
const char = text[i];
if (/[\u0000-\u00FF]/.test(char)) {
// 英文字符或数字
width += 7;
} else {
// 中文字符
width += 14;
}
}
return width;
};
standardList().forEach((item: any, index: any) => {
const y = index ? index * 30 + startY : startY;
// 估算文本宽度
const keyWidth = estimateTextWidth(item.key);
const valueWidth = estimateTextWidth(item.value);
// 计算左侧文本X坐标,使其在左列居中
const tNumX = leftColumnStart + (columnWidth - keyWidth) / 2;
// 计算右侧文本X坐标,使其在右列居中
const vNumX = rightColumnStart + (columnWidth - valueWidth) / 2;
context.setFillStyle("#333333");
context.setFontSize(14); // 设置字体大小
// 调整Y坐标,使文本垂直居中
const textY = y - 2; // 向上偏移一点,使文本在行中垂直居中
context.fillText(item.key, tNumX, textY);
context.fillText(item.value, vNumX, textY);
// 分割线
setCutLine(context, { x: 12, y: y + 8 }, width);
});
// 画竖线
context.lineWidth = 1;
context.strokeStyle = "#E5E5E5";
context.beginPath();
const lineY = 30;
const endY = standardList().length * 30 - 2 + lineY;
// 竖线
context.moveTo(leftColumnStart, lineY); // 左
context.lineTo(leftColumnStart, endY);
context.moveTo(rightColumnStart, lineY); // 中
context.lineTo(rightColumnStart, endY);
context.moveTo(width - rightMargin, lineY); // 右
context.lineTo(width - rightMargin, endY);
context.stroke();
context.setFontSize(42);
context.setFillStyle("red");
context.setTextAlign("center");
context.setTextBaseline("middle");
context.fillText("长按点击下载", width / 2, res.height - 300);
context.draw(false, () => {
uni.canvasToTempFilePath({
canvasId: "canvasbox",
success: (result: any) => {
state.createImgPath = result.tempFilePath;
},
});
});
}
// 分割线
const setCutLine = (ctx: any, xy: any, width?: any) => {
const startX = 12;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(xy.x, xy.y + 0.5); // + 0.5 线像素为1px
ctx.lineTo(width ? width - startX : startX, xy.y + 0.5);
ctx.closePath();
ctx.strokeStyle = "#E5E5E5";
ctx.stroke();
};
// 标准列表
const standardList = () => {
return [
{
key: "高血压",
value: "≥140/90mmHg",
},
{
key: "血脂情况",
value: "血脂异常或不知道",
},
{
key: "糖尿病",
value: "有",
},
{
key: "心房颤动",
value: "心跳不规则",
},
{
key: "吸烟",
value: "有",
},
{
key: "体重",
value: "明显超重或肥胖",
},
{
key: "运动",
value: "缺乏运动",
},
{
key: "是否抽烟",
value: "是的",
},
{
key: "是否喝酒",
value: "是的",
},
{
key: "是否患有精神洁癖",
value: "不曾",
},
];
};
</script>
<style scoped lang="scss">
.canvaspage {
width: 100%;
min-height: 100vh;
box-sizing: border-box;
padding: 32rpx;
}
</style>
应用场景
这种技术可以广泛应用于:
健康评估报告生成
电子病历可视化
体检结果展示
医疗数据报告
引入方法补充
getCanvasSize方法
TypeScript
//获取canvas画布的实际尺寸
export const getCanvasSize = (classname: any, nodeInfo: any): Promise<any> => {
return new Promise((resolve, reject) => {
const query = uni.createSelectorQuery().in(nodeInfo);
query
.select(classname)
.fields({ size: true }, (res: any) => {
if (res) {
resolve(res);
} else {
reject("获取画布尺寸失败");
}
})
.exec();
});
};
downpic方法--参考本篇文章:
uni微信小程序实现保存图片到本地_uniapp保存图片到相册-CSDN博客
希望本文对你在uni-app中使用Canvas有所帮助!
