外观
Canvas 画布
约 1163 字大约 4 分钟
2025-10-31
Canvas 画布组件,支持2D和WebGL渲染,提供统一的跨平台画布API。
基本使用
<template>
<view>
<u-canvas :width="300" :height="200" ref="canvas"></u-canvas>
<un-button @click="drawBasicShapes">绘制基本图形</un-button>
</view>
</template>
<script>
export default {
methods: {
async drawBasicShapes() {
const canvasContext = await this.$refs.canvas.getCanvasContext();
const { canvas } = canvasContext;
// 绘制矩形
canvas.fillStyle = '#ff6b6b';
canvas.fillRect(20, 20, 80, 60);
// 绘制描边矩形
canvas.strokeStyle = '#4ecdc4';
canvas.lineWidth = 3;
canvas.strokeRect(120, 20, 80, 60);
// 绘制圆形
canvas.fillStyle = '#45b7d1';
canvas.beginPath();
canvas.arc(60, 120, 30, 0, 2 * Math.PI);
canvas.fill();
// 绘制线条
canvas.strokeStyle = '#f9ca24';
canvas.lineWidth = 4;
canvas.beginPath();
canvas.moveTo(120, 120);
canvas.lineTo(200, 120);
canvas.lineTo(160, 160);
canvas.closePath();
canvas.stroke();
canvas.draw();
}
}
};
</script>绘制图形
<template>
<view>
<u-canvas
:width="300"
:height="200"
ref="basicCanvas"
></u-canvas>
<un-button @click="drawBasicShapes">绘制基本图形</un-button>
<un-button @click="clearCanvas">清空画布</un-button>
</view>
</template>
<script>
export default {
methods: {
async drawBasicShapes() {
const canvasContext = await this.$refs.basicCanvas.getCanvasContext();
const { canvas } = canvasContext;
// 绘制矩形
canvas.fillStyle = '#ff6b6b';
canvas.fillRect(20, 20, 80, 60);
// 绘制描边矩形
canvas.strokeStyle = '#4ecdc4';
canvas.lineWidth = 3;
canvas.strokeRect(120, 20, 80, 60);
// 绘制圆形
canvas.fillStyle = '#45b7d1';
canvas.beginPath();
canvas.arc(60, 120, 30, 0, 2 * Math.PI);
canvas.fill();
// 绘制线条
canvas.strokeStyle = '#f9ca24';
canvas.lineWidth = 4;
canvas.beginPath();
canvas.moveTo(120, 120);
canvas.lineTo(200, 120);
canvas.lineTo(160, 160);
canvas.closePath();
canvas.stroke();
canvas.draw();
},
async clearCanvas() {
const canvasContext = await this.$refs.basicCanvas.getCanvasContext();
const { canvas, width, height } = canvasContext;
canvas.clearRect(0, 0, width, height);
canvas.draw();
}
}
};
</script>绘制文字
<template>
<view>
<u-canvas
:width="300"
:height="200"
ref="textCanvas"
></u-canvas>
<un-button @click="drawText">绘制文字</un-button>
<un-button @click="clearTextCanvas">清空画布</un-button>
</view>
</template>
<script>
export default {
methods: {
async drawText() {
const canvasContext = await this.$refs.textCanvas.getCanvasContext();
const { canvas } = canvasContext;
// 设置字体样式
canvas.font = '20px Arial';
canvas.fillStyle = '#333';
canvas.textAlign = 'center';
canvas.textBaseline = 'middle';
// 绘制填充文字
canvas.fillText('Hello uView Canvas', 150, 50);
// 绘制描边文字
canvas.strokeStyle = '#ff6b6b';
canvas.lineWidth = 2;
canvas.strokeText('Stroke Text', 150, 100);
// 设置阴影
canvas.shadowColor = 'rgba(0,0,0,0.3)';
canvas.shadowBlur = 8;
canvas.shadowOffsetX = 2;
canvas.shadowOffsetY = 2;
canvas.fillStyle = '#4ecdc4';
canvas.fillText('Shadow Text', 150, 150);
canvas.draw();
},
async clearTextCanvas() {
const canvasContext = await this.$refs.textCanvas.getCanvasContext();
const { canvas, width, height } = canvasContext;
canvas.clearRect(0, 0, width, height);
canvas.draw();
}
}
};
</script>图表绘制
柱状图
<template>
<view>
<u-canvas
:width="300"
:height="200"
ref="chartCanvas"
></u-canvas>
<un-button @click="drawChart">绘制柱状图</un-button>
</view>
</template>
<script>
export default {
methods: {
async drawChart() {
const canvasContext = await this.$refs.chartCanvas.getCanvasContext();
const { canvas, width, height } = canvasContext;
const data = [60, 80, 45, 90, 70];
const colors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#f9ca24', '#6c5ce7'];
const barWidth = 30;
const barSpacing = 20;
const startX = 30;
const maxHeight = 120;
// 清空画布
canvas.clearRect(0, 0, width, height);
// 绘制柱状图
data.forEach((value, index) => {
const barHeight = (value / 100) * maxHeight;
const x = startX + index * (barWidth + barSpacing);
const y = height - 30 - barHeight;
canvas.fillStyle = colors[index];
canvas.fillRect(x, y, barWidth, barHeight);
// 绘制数值标签
canvas.fillStyle = '#333';
canvas.font = '12px Arial';
canvas.textAlign = 'center';
canvas.fillText(value.toString(), x + barWidth / 2, y - 5);
});
canvas.draw();
}
}
};
</script>饼图
<template>
<view>
<u-canvas
:width="300"
:height="200"
ref="pieCanvas"
></u-canvas>
<un-button @click="drawPieChart">绘制饼图</un-button>
</view>
</template>
<script>
export default {
methods: {
async drawPieChart() {
const canvasContext = await this.$refs.pieCanvas.getCanvasContext();
const { canvas, width, height } = canvasContext;
const data = [30, 25, 20, 15, 10];
const colors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#f9ca24', '#6c5ce7'];
const centerX = width / 2;
const centerY = height / 2;
const radius = 60;
// 清空画布
canvas.clearRect(0, 0, width, height);
let currentAngle = -Math.PI / 2; // 从顶部开始
data.forEach((value, index) => {
const sliceAngle = (value / 100) * 2 * Math.PI;
// 绘制扇形
canvas.fillStyle = colors[index];
canvas.beginPath();
canvas.moveTo(centerX, centerY);
canvas.arc(centerX, centerY, radius, currentAngle, currentAngle + sliceAngle);
canvas.closePath();
canvas.fill();
// 绘制标签
const labelAngle = currentAngle + sliceAngle / 2;
const labelX = centerX + Math.cos(labelAngle) * (radius + 20);
const labelY = centerY + Math.sin(labelAngle) * (radius + 20);
canvas.fillStyle = '#333';
canvas.font = '12px Arial';
canvas.textAlign = 'center';
canvas.fillText(`${value}%`, labelX, labelY);
currentAngle += sliceAngle;
});
canvas.draw();
}
}
};
</script>图片处理
绘制图片
<template>
<view>
<u-canvas
:width="300"
:height="200"
ref="imageCanvas"
></u-canvas>
<un-button @click="drawImage">绘制图片</un-button>
</view>
</template>
<script>
export default {
methods: {
async drawImage() {
const canvasContext = await this.$refs.imageCanvas.getCanvasContext();
const { canvas, width, height } = canvasContext;
// 清空画布
canvas.clearRect(0, 0, width, height);
// 绘制真实图片
const imageUrl = 'https://uview-unix.d3u.cn/web/static/uview/images/50c02fb7gy1i0vn1f8mbuj20qo13eal2.jpg';
canvas.drawImage(imageUrl, 27, 0, 246, 364);
canvas.draw();
}
}
};
</script>导出图片
<template>
<view>
<u-canvas
:width="300"
:height="200"
ref="exportCanvas"
></u-canvas>
<un-button @click="exportImage">导出图片</un-button>
</view>
</template>
<script>
export default {
methods: {
async exportImage() {
try {
const tempFilePath = await this.$refs.exportCanvas.canvasToTempFilePath({
x: 0,
y: 0,
width: 300,
height: 200,
destWidth: 300,
destHeight: 200,
fileType: 'png',
quality: 1
});
console.log('图片已保存到:', tempFilePath);
uni.showToast({
title: '图片导出成功',
icon: 'success'
});
} catch (error) {
console.error('导出失败:', error);
uni.showToast({
title: '导出失败',
icon: 'error'
});
}
}
}
};
</script>API
Props
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| width | 画布宽度 | String | Number | 375 |
| height | 画布高度 | String | Number | - |
| type | 画布类型 | Enum | 2d(微信小程序/抖音小程序) webgl(其他平台) |
| disableScroll | 是否禁用滚动 | Boolean | false |
| hidpi | 是否启用高清 | Boolean | true |
| customStyle | 自定义样式 | Object | String | - |
Events
| 事件名 | 说明 | 回调参数 |
|---|---|---|
| onTouchstart | 触摸开始 | event |
| onTouchmove | 触摸移动 | event |
| onTouchend | 触摸结束 | event |
| onTouchcancel | 触摸取消 | event |
| onLongtap | 长按 | event |
| onError | 错误事件 | event |
Methods
| 方法名 | 说明 | 参数 | 返回值 |
|---|---|---|---|
| getCanvasContext | 获取画布上下文 | - | Promise<{canvas, width, height, canvasId, use2D}> |
| queryCanvas | 查询画布节点信息 | - | Promise<{node, size}> |
| canvasToTempFilePath | 导出画布为临时文件 | options | Promise<string> |
canvasToTempFilePath 参数
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| x | 画布x轴起点 | Number | 0 |
| y | 画布y轴起点 | Number | 0 |
| width | 画布宽度 | Number | canvas宽度 |
| height | 画布高度 | Number | canvas高度 |
| destWidth | 输出图片宽度 | Number | width |
| destHeight | 输出图片高度 | Number | height |
| fileType | 图片格式 | String | png |
| quality | 图片质量 | Number | 1 |
