在 Uni-app 中绘制多边形可以通过使用 Canvas API 来实现。Uni-app 是一个使用 Vue.js 开发所有前端应用的框架,同时支持编译为 H5、小程序等多个平台。由于 Canvas 是 H5 和小程序中都支持的 API,所以通过 Canvas 绘制多边形是一个比较通用的方法。
1. 创建一个新的 Uni-app 项目(如果还没有的话)
vue create -p dcloudio/uni-preset-vue my-uni-app
cd my-uni-app
2. 开始绘制
背景:在image上绘制多边形,并将点位回传。回传后的数据格式'(1,2),(3,4)',所以需要一些方法进行处理。如你没有转换需求,可自行删除。
<template>
<view class="layout-wrap">
<view class="draw-wrap">
<view class="camera-wrap">
<img
:style="{ width: `${canvasWidth}px`, height: `${canvasHeight}px` }"
:src="cameraUrl"
mode="aspectFill"
class="popup-img"
/>
<canvas
id="myCanvas"
canvas-id="myCanvas"
@touchstart="onTouchStart"
@touchend="onTouchEnd"
:style="{ width: `${canvasWidth}px`, height: `${canvasHeight}px` }"
></canvas>
</view>
</view>
<view class="btn-wrap">
<view class="btn reset-btn" @click="handleClear">清 除</view>
<view class="btn" @click="handleSubmit">确 定</view>
</view>
</view>
</template>
<script>
import { addCameraRegion, getCameraId } from "@/api/cabinet";
import config from "@/config";
const baseUrl = config.baseUrl;
export default {
name: "draw",
data() {
return {
points: [], // 存储触摸点
canvasWidth: "",
canvasHeight: "",
ctx: "",
cameraUrl: "",
rate: "",
touchNum: 0,
};
},
onShow() {
this.init();
},
methods: {
init() {
// 获取配置及绘制图形
getCameraId(config.hostInfoId).then((res) => {
const data = res.data;
this.monitorPoints = data.monitorPoints;
this.rate = data.monitorWidth / data.monitorHeight;
this.canvasWidth = 1000
this.canvasHeight = this.canvasWidth / this.rate;
this.cameraUrl =
baseUrl +
"/api/monitor/player?cameraId=" +
data.monitorCameraId +
"&time=" +
new Date().getTime();
const ctx = uni.createCanvasContext("myCanvas");
this.ctx = ctx;
this.touchNum = 0
this.setRect();
});
},
onTouchStart(e) {
if (this.touchNum === 0) {
this.handleClear()
}
this.touchNum++;
const touch = e.touches[0];
this.points.push({ x: touch.x, y: touch.y });
},
onTouchEnd(e) {
this.drawPolygon();
this.ctx.draw();
},
drawPolygon() {
const ctx = this.ctx;
ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight); // 清除画布
ctx.setStrokeStyle("#00ff00"); // 设置多边形边框颜色
// 设置填充样式和透明度
ctx.setLineWidth(10); // 设置多边形边框宽度
ctx.beginPath();
this.points.forEach((point, index) => {
if (index === 0) {
ctx.moveTo(point.x, point.y);
} else {
ctx.lineTo(point.x, point.y);
}
});
// 如果需要闭合多边形,取消注释以下行
ctx.closePath();
ctx.stroke();
// 绘制填充
ctx.setFillStyle("rgba(0, 255, 0, 0.4)");
ctx.fill();
},
// 根据之前的数据回显多边形(如果有的话)
setRect() {
try {
const pointsArr = this.monitorPoints.slice(1, -1).split("),(");
if (pointsArr && pointsArr.length > 1) {
pointsArr.map((p) => {
this.points.push({
x: Math.round(p.split(",")[0] / this.rate),
y: Math.round(p.split(",")[1] / this.rate),
});
});
console.log(this.canvasWidth, this.canvasHeight);
console.log(this.points);
this.drawPolygon();
setTimeout(() => {
//必须延迟执行 不然H5不显示
this.ctx.draw(); //必须加上 uniapp 没这儿玩意儿 显示不出来不比原生 不加可以显示
}, 200);
}
} catch (error) {
console.error("绘制多边形时出错:", error);
}
},
// 提交
handleSubmit() {
if (this.points.length > 1) {
// 转换并发送多边形的顶点坐标
const scaledPoints = [];
this.points.map((point) => {
scaledPoints.push(
`(${Math.round(point.x * this.rate)},${Math.round(
point.y * this.rate
)})`
);
});
// 提交请求
} else {
uni.showToast({
title: "请绘制",
icon: "none",
});
}
},
// 清除
handleClear() {
this.points = [];
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight); // 清除画布
this.ctx.draw();
},
},
};
</script>
<style lang="scss" scoped>
.draw-wrap {
position: relative;
margin-top: 2vh;
left: 7vw;
}
.camera-wrap {
height: 76vh;
}
.popup-img {
position: absolute;
top: 0;
left: 0;
object-fit: contain;
border: 6rpx solid #093763;
box-sizing: border-box;
}
#myCanvas {
position: absolute;
top: 0;
left: 0;
}
</style>
3. 效果图
4. 解释
uni.createCanvasContext('myCanvas')
用于获取 Canvas 的绘图上下文。ctx.setStrokeStyle('red')
和ctx.setLineWidth(
10)
用于设置描边颜色和宽度。ctx.beginPath()
开始一个新的路径。ctx.moveTo(points[0].x, points[0].y)
和ctx.lineTo(points[i].x, points[i].y)
用于绘制线段。ctx.closePath()
闭合路径,使之成为一个多边形。ctx.stroke()
描边。ctx.setFillStyle('
rgba(30, 144, 255,0.5)')
和ctx.fill()
用于填充多边形。ctx.draw()
将所有绘图操作提交到 Canvas 上。
5. 坑
回显的时候,苦恼了很久,为什么点位已经传入,不能回显。后来发现,是draw方法需要加延时。