說明:這個是先畫出一個72度菱形,長中長線和短中長線按照一定比例,然后把菱形分層十份,最后再把菱形進行旋轉形成五角星,最后顯示標簽,因為一直對不上所以對標簽做了點操作
<template><view class="container"><canvas canvas-id="diamondCanvas" class="canvas":style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"></canvas></view>
</template><script lang="ts">
import { Vue, Component, Prop, Watch } from 'vue-property-decorator';interface DiamondItem {name: string;value: number;
}@Component
export default class DiamondCanvas extends Vue {private canvasWidth: number = 350;private canvasHeight: number = 350;private scale: number = 40;private longAxis: number = 3;private shortAxis: number = 1.5;private distance: number = 1;private colors: string[] = ['#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF','#FF9F40', '#8AC249', '#EA5F89', '#0D98BA', '#D27D46'];// 新增:定義橙紅色邊框顏色private borderColor: string = '#FF6B35'; // 橙紅色private borderWidth: number = 3; // 線條粗細(默認3px,可按需調整)@Prop({type: Array,default: () => [{ name: '指標1', value: 5 },{ name: '指標2', value: 7 },{ name: '指標3', value: 6 },{ name: '指標4', value: 8 },{ name: '指標5', value: 4 }],validator: (items: DiamondItem[]) =>items.length === 5 && items.every(item =>item.value >= 0 && item.value <= 10 && Number.isInteger(item.value))}) readonly diamondData!: DiamondItem[];mounted() {this.drawDiamondsWithLabels();}@Watch('diamondData', { deep: true })onDiamondDataChange() {this.drawDiamondsWithLabels();}private drawDiamondsWithLabels() {const ctx = wx.createCanvasContext('diamondCanvas', this);ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);const centerX = this.canvasWidth / 2;const centerY = this.canvasHeight / 2;const pivotY = (this.longAxis / 2) * this.scale;const labelRadius = this.scale * 3.2;const initialRotation = -36 * Math.PI / 180;const angleInterval = 72 * Math.PI / 180;for (let i = 0; i < 5; i++) {const diamondAngle = initialRotation + (i * angleInterval);ctx.save();ctx.translate(centerX, centerY);ctx.rotate(diamondAngle);ctx.translate(0, pivotY);this.drawSingleDiamond(ctx, this.diamondData[i].value, this.colors[i]);ctx.restore();const prevIndex = (i - 1 + 5) % 5;const labelAngle = initialRotation + (prevIndex * angleInterval) + Math.PI;ctx.save();ctx.translate(centerX, centerY);const labelX = Math.cos(labelAngle) * labelRadius;const labelY = Math.sin(labelAngle) * labelRadius;ctx.setFontSize(12);ctx.setFillStyle('#333333');ctx.setTextAlign('center');ctx.setTextBaseline('middle');ctx.fillText(this.diamondData[i].name, labelX, labelY - 10);ctx.setFontSize(14);ctx.setFillStyle(this.colors[i]);ctx.fillText(this.diamondData[i].value.toString(), labelX, labelY + 10);ctx.restore();}ctx.draw();}private drawSingleDiamond(ctx: any, value: number, color: string) {const top = { x: 0, y: this.longAxis / 2 };const bottom = { x: 0, y: -this.longAxis / 2 };const right = { x: this.shortAxis / 2, y: top.y - this.distance };const left = { x: -this.shortAxis / 2, y: top.y - this.distance };// 繪制邊框 - 使用橙紅色ctx.beginPath();ctx.moveTo(top.x * this.scale, -top.y * this.scale);ctx.lineTo(right.x * this.scale, -right.y * this.scale);ctx.lineTo(bottom.x * this.scale, -bottom.y * this.scale);ctx.lineTo(left.x * this.scale, -left.y * this.scale);ctx.closePath();ctx.setStrokeStyle(this.borderColor); // 關鍵修改:使用橙紅色邊框ctx.setLineWidth(this.borderWidth); // 關鍵:設置線條粗細ctx.stroke();// 分段計算(保持不變)const h1 = top.y - right.y;const h2 = right.y - bottom.y;const totalSegments = 10;const ratio = h1 / (h1 + h2);const n1 = Math.max(1, Math.round(totalSegments * ratio));const n2 = totalSegments - n1;const segmentH1 = h1 / n1;const segmentH2 = h2 / n2;// 上半部分繪制(修正邊緣銜接)for (let i = 0; i < n1; i++) {// 關鍵修正:讓當前段的y2與下一段的y1完全一致(避免縫隙)const y1 = top.y - i * segmentH1;const y2 = top.y - (i + 1) * segmentH1; // 精確計算,無偏差const width1 = this.shortAxis * ((top.y - y1) / h1);const width2 = this.shortAxis * ((top.y - y2) / h1);const segmentColor = i < value ? color : '#FFFFFF';// 繪制時增加0.5px偏移,抵消抗鋸齒導致的白邊this.drawDiamondSegment(ctx, y1, y2, width1, width2, segmentColor);}// 下半部分繪制(修正邊緣銜接)for (let i = 0; i < n2; i++) {// 關鍵修正:讓當前段的y2與下一段的y1完全一致const y1 = right.y - i * segmentH2;const y2 = right.y - (i + 1) * segmentH2; // 精確計算,無偏差const width1 = this.shortAxis * ((y1 - bottom.y) / h2);const width2 = this.shortAxis * ((y2 - bottom.y) / h2);const segmentColor = (n1 + i) < value ? color : '#FFFFFF';// 繪制時增加0.5px偏移,抵消抗鋸齒導致的白邊this.drawDiamondSegment(ctx, y1, y2, width1, width2, segmentColor);}}// 繪制單個顏色塊(增加偏移抵消白邊)private drawDiamondSegment(ctx: any, y1: number, y2: number, width1: number, width2: number, color: string) {ctx.beginPath();// 關鍵修正:y坐標增加0.5px偏移,避免Canvas抗鋸齒產生的白邊const offset = 0.5;ctx.moveTo(-width1 / 2 * this.scale, -y1 * this.scale + offset);ctx.lineTo(width1 / 2 * this.scale, -y1 * this.scale + offset);ctx.lineTo(width2 / 2 * this.scale, -y2 * this.scale + offset);ctx.lineTo(-width2 / 2 * this.scale, -y2 * this.scale + offset);ctx.closePath();ctx.setFillStyle(color);ctx.fill();// 確保沒有隱藏的線條繪制(如果有stroke()會導致邊緣線,這里保持注釋)// ctx.stroke(); // 徹底移除線條繪制,避免產生邊界線}
}
</script><style scoped>
.container {display: flex;justify-content: center;align-items: center;width: 100%;height: 100vh;
}.canvas {border: 1px solid #eee;background-color: #f9f9f9;
}
</style>