c
复制代码
<template>
<view class="star-container">
<canvas canvas-id="starCanvas" id="starCanvas" class="star-canvas"
:style="{ width: size + 'px', height: size + 'px' }"></canvas>
</view>
</template>
<script>
export default {
props: {
size: {
type: Number,
default: 300
},
data: {
type: Array,
required: true,
validator: (value) => {
return value.length === 5 &&
value.every(item => item.percentage >= 0 && item.percentage <= 100);
},
default: () => [
{ percentage: 80, color: '#FF5252', title: '学业水平' },
{ percentage: 60, color: '#FF9800', title: '思想品德' },
{ percentage: 90, color: '#FFEB3B', title: '身心健康' },
{ percentage: 70, color: '#4CAF50', title: '艺术素养' },
{ percentage: 50, color: '#2196F3', title: '社会实践' }
]
}
},
data() {
return {
centerX: 0,
centerY: 0,
radius: 0,
ctx: null
};
},
mounted() {
this.$nextTick(() => {
this.initCanvas();
});
},
methods: {
initCanvas() {
if (!this.data || this.data.length !== 5) {
console.error('Invalid data format. Expected array of 5 items.');
return;
}
this.centerX = this.size / 2;
this.centerY = this.size / 2;
this.radius = this.size * 0.4; // 恢复原始半径
this.ctx = uni.createCanvasContext('starCanvas', this);
this.drawStar();
},
drawStar() {
if (!this.ctx) return;
this.ctx.clearRect(0, 0, this.size, this.size);
// 绘制填充区域
this.data.forEach((item, index) => {
this.drawSector(index, item.percentage, item.color);
});
// 绘制中心连线
this.drawCenterLines();
// 绘制五角星轮廓
this.drawStarOutline();
// 绘制标签
this.drawLabels();
this.ctx.draw();
},
drawSector(index, percentage, color) {
if (percentage <= 0) return;
const angleOffset = -Math.PI / 2;
const angle1 = angleOffset + (index * 2 * Math.PI) / 5;
const angle2 = angleOffset + ((index + 1) * 2 * Math.PI) / 5;
const outerX1 = this.centerX + this.radius * Math.cos(angle1);
const outerY1 = this.centerY + this.radius * Math.sin(angle1);
const outerX2 = this.centerX + this.radius * Math.cos(angle2);
const outerY2 = this.centerY + this.radius * Math.sin(angle2);
const innerAngle = angle1 + Math.PI / 5;
const innerX = this.centerX + this.radius * 0.4 * Math.cos(innerAngle);
const innerY = this.centerY + this.radius * 0.4 * Math.sin(innerAngle);
const fillRatio = percentage / 100;
const fillX1 = this.centerX + (outerX1 - this.centerX) * fillRatio;
const fillY1 = this.centerY + (outerY1 - this.centerY) * fillRatio;
const fillX2 = this.centerX + (innerX - this.centerX) * fillRatio;
const fillY2 = this.centerY + (innerY - this.centerY) * fillRatio;
const fillX3 = this.centerX + (outerX2 - this.centerX) * fillRatio;
const fillY3 = this.centerY + (outerY2 - this.centerY) * fillRatio;
this.ctx.beginPath();
this.ctx.moveTo(this.centerX, this.centerY);
this.ctx.lineTo(fillX1, fillY1);
this.ctx.lineTo(fillX2, fillY2);
this.ctx.lineTo(fillX3, fillY3);
this.ctx.closePath();
this.ctx.fillStyle = color;
this.ctx.fill();
},
drawStarOutline() {
this.ctx.beginPath();
const angleOffset = -Math.PI / 2;
for (let i = 0; i <= 5; i++) {
const angle = angleOffset + (i * 2 * Math.PI) / 5;
const x = this.centerX + this.radius * Math.cos(angle);
const y = this.centerY + this.radius * Math.sin(angle);
if (i === 0) {
this.ctx.moveTo(x, y);
} else {
this.ctx.lineTo(x, y);
}
const innerAngle = angle + Math.PI / 5;
const innerX = this.centerX + this.radius * 0.4 * Math.cos(innerAngle);
const innerY = this.centerY + this.radius * 0.4 * Math.sin(innerAngle);
this.ctx.lineTo(innerX, innerY);
}
this.ctx.closePath();
this.ctx.strokeStyle = '#333';
this.ctx.lineWidth = 2;
this.ctx.stroke();
},
drawCenterLines() {
this.ctx.beginPath();
const angleOffset = -Math.PI / 2;
for (let i = 0; i < 5; i++) {
const angle = angleOffset + (i * 2 * Math.PI) / 5;
const innerAngle = angle + Math.PI / 5;
const x = this.centerX + this.radius * 0.4 * Math.cos(innerAngle);
const y = this.centerY + this.radius * 0.4 * Math.sin(innerAngle);
this.ctx.moveTo(this.centerX, this.centerY);
this.ctx.lineTo(x, y);
}
this.ctx.strokeStyle = '#999';
this.ctx.lineWidth = 1;
this.ctx.stroke();
},
drawLabels() {
// 调整角度偏移使标签位置正确
const angleOffset = -Math.PI / 2;
const labelRadius = this.radius * 1.15;
this.data.forEach((item, index) => {
const angle1 = angleOffset + (index * 2 * Math.PI) / 5;
const angle2 = angleOffset + ((index + 1) * 2 * Math.PI) / 5;
const midAngle = (angle1 + angle2) / 2;
let labelX = this.centerX + labelRadius * Math.cos(midAngle);
let labelY = this.centerY + labelRadius * Math.sin(midAngle);
// 微调各标签位置
if (index === 0) { // 顶部标签
labelY -= -10;//学业
} else if (index === 1) { // 左上标签
labelX -= 30;//思想
labelY -= 5;
} else if (index === 2) { // 左下标签
labelX -= 5;//身心
labelY += -15;
} else if (index === 3) { // 右下标签
labelX += 25;//艺术
// labelY += 5;
} else if (index === 4) { // 右上标签
labelX += 5;
labelY -= -10;
}
this.ctx.textAlign = 'center';
this.ctx.textBaseline = 'middle';
this.ctx.font = 'bold 14px Arial';
this.ctx.fillStyle = item.color;
this.ctx.fillText(item.title, labelX, labelY - 10);
this.ctx.font = '12px Arial';
this.ctx.fillText(`${item.percentage}%`, labelX, labelY + 10);
});
}
},
watch: {
data: {
deep: true,
handler() {
this.initCanvas();
}
}
}
};
</script>
<style scoped>
.star-container {
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
background-color: #f8f8f8;
}
.star-canvas {
display: block;
margin: 0 auto;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
</style>```