vue+微信小程序 五角星

发布于:2025-08-16 ⋅ 阅读:(24) ⋅ 点赞:(0)

在这里插入图片描述
说明:这个是先画出一个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>


网站公告

今日签到

点亮在社区的每一天
去签到