Skip to content
给定三个点画三角形底部固定横着放

html版本,单位px

html
<!DOCTYPE html>
<html>
<head>
    <title>原生HTML三角形绘制</title>
    <style>
        #container {
            width: 300px;
            height: 250px;
            position: relative;
            border: 1px solid #ddd;
            margin: 20px;
        }
        canvas {
            position: absolute;
            left: 0;
            top: 0;
        }
    </style>
</head>
<body>
    <div id="container">
        <canvas id="triangleCanvas"></canvas>
    </div>

    <script>
        // 初始化画布[1,3](@ref)
        const canvas = document.getElementById('triangleCanvas');
        const container = document.getElementById('container');
        const ctx = canvas.getContext('2d');
        
        // 设置画布尺寸匹配容器
        canvas.width = container.clientWidth;
        canvas.height = container.clientHeight;
        
        // 固定三点坐标[9](@ref)
        const basePoints = [
            { x: canvas.width/2, y: 20 },    // 顶部
            { x: 20, y: canvas.height-20 },  // 左下
            { x: canvas.width-20, y: canvas.height-20 } // 右下
        ];
        
        // 示例数值(可修改)
        const values = [400, 100, 200];

        function drawTriangle() {
            // 清空画布[3](@ref)
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            // 计算最大延伸比例[4](@ref)
            const maxVal = Math.max(...values);
            const weights = values.map(v => v/maxVal * 0.8);
            
            // 计算中心点[4](@ref)
            const center = {
                x: (basePoints[0].x + basePoints[1].x + basePoints[2].x)/3,
                y: (basePoints[0].y + basePoints[1].y + basePoints[2].y)/3
            };
            
            // 生成延伸点[4](@ref)
            const points = basePoints.map((p, i) => ({
                x: center.x + (p.x - center.x) * weights[i],
                y: center.y + (p.y - center.y) * weights[i]
            }));

            // 绘制三角形[2,3](@ref)
            ctx.beginPath();
            ctx.moveTo(points[0].x, points[0].y);
            ctx.lineTo(points[1].x, points[1].y);
            ctx.lineTo(points[2].x, points[2].y);
            ctx.closePath();
            
            // 填充颜色
            ctx.fillStyle = 'rgba(74, 144, 226, 0.5)';
            ctx.fill();
            
            // 绘制边框
            ctx.strokeStyle = '#2c6cbf';
            ctx.lineWidth = 2;
            ctx.stroke();
            
            // 标注数值[3](@ref)
            ctx.fillStyle = '#333';
            ctx.font = '14px Arial';
            points.forEach((p, i) => {
                ctx.fillText(values[i], p.x - 15, p.y + (i===0 ? 20 : -10));
            });
        }
        
        // 初始绘制
        drawTriangle();
        
        // 点击重置数值
        container.onclick = function() {
            values.forEach((v,i) => values[i] = parseInt(Math.random()*500));
            drawTriangle();
        }
    </script>
</body>
</html>

uniapp版本,单位rpx(vue2版本)

html
<template>
  <view class="container">
    <canvas canvas-id="triangleCanvas" class="canvas"></canvas>
    <button @click="redrawTriangle">重新绘制</button>
  </view>
</template>

<script>
export default {
  data() {
    return {
      // 固定三个点的数值(示例:顶部=400,左下=100,右下=200)
      values: [400, 100, 200], 
      canvasWidth: 300,
      canvasHeight: 250,
      padding: 20 // 安全边距
    };
  },
  onReady() {
    this.drawTriangle();
  },
  methods: {
    // 核心方法:基于固定三点位置计算凸出点
    calculateExtendedPoints() {
      // 1. 固定三个点的初始位置(无论数值如何,位置关系不变)
      const fixedPoints = [
        [this.canvasWidth / 2, this.padding],           // 顶部(400)
        [this.padding, this.canvasHeight - this.padding], // 左下(100)
        [this.canvasWidth - this.padding, this.canvasHeight - this.padding] // 右下(200)
      ];

      // 2. 计算中心点(用于控制凸出方向)
      const center = [
        (fixedPoints[0][0] + fixedPoints[1][0] + fixedPoints[2][0]) / 3,
        (fixedPoints[0][1] + fixedPoints[1][1] + fixedPoints[2][1]) / 3
      ];

      // 3. 归一化数值(0~1范围)
      const maxVal = Math.max(...this.values);
      const weights = this.values.map(v => v / maxVal);

      // 4. 计算每个点的最大允许延伸距离(确保不超界)
      const extendedPoints = fixedPoints.map((point, i) => {
        // 方向向量(从中心指向原始点)
        const dirX = point[0] - center[0];
        const dirY = point[1] - center[1];

        // 计算到容器边界的最大安全距离
        const maxExtension = Math.min(
          (point[0] >= center[0] ? 
            (this.canvasWidth - this.padding - center[0]) / dirX : 
            (this.padding - center[0]) / dirX),
          (point[1] >= center[1] ? 
            (this.canvasHeight - this.padding - center[1]) / dirY : 
            (this.padding - center[1]) / dirY)
        ) * 0.95; // 安全系数

        // 应用数值权重(400比100/200凸出更多)
        const extension = weights[i] * maxExtension * 0.8;

        // 返回延伸后的点
        return [
          center[0] + dirX * extension,
          center[1] + dirY * extension
        ];
      });

      return extendedPoints;
    },

    drawTriangle() {
      const ctx = uni.createCanvasContext('triangleCanvas', this);
      
      // 获取凸出后的点(自动适应容器)
      const points = this.calculateExtendedPoints();

      // 绘制三角形
      ctx.beginPath();
      ctx.moveTo(points[0][0], points[0][1]); // 顶部(400)
      ctx.lineTo(points[1][0], points[1][1]); // 左下(100)
      ctx.lineTo(points[2][0], points[2][1]); // 右下(200)
      ctx.closePath();
      
      ctx.setFillStyle('#4a90e2');
      ctx.fill();

      // 标记数值
      ctx.setFontSize(12);
      ctx.setFillStyle('#000');
      points.forEach((p, i) => {
        ctx.fillText(this.values[i].toString(), p[0], p[1]);
      });

      ctx.draw();
    },

    redrawTriangle() {
      // 可以修改数值测试不同效果(但保持位置固定)
      this.values = [
        Math.floor(Math.random() * 500), // 顶部
        Math.floor(Math.random() * 200), // 左下
        Math.floor(Math.random() * 300)  // 右下
      ];
      this.drawTriangle();
    }
  }
};
</script>

<style>
.container {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.canvas {
  width: 300px;
  height: 250px;
  margin-bottom: 20px;
  background-color: #f5f5f5;
  border: 1px solid #ddd;
}
button {
  margin-top: 20px;
  width: 200px;
}
</style>

uniapp版本,单位rpx(vue3版本)

html
<template>
  <view class="container">
    <canvas canvas-id="triangleCanvas" class="canvas"></canvas>
    <button @click="redrawTriangle">重新绘制</button>
  </view>
</template>

<script setup>
import { ref, onMounted } from 'vue'

// 响应式数据声明(网页1/网页4)
const values = ref([400, 100, 200])
const canvasWidth = ref(300)
const canvasHeight = ref(250)
const padding = ref(20)

// 核心计算逻辑(网页3)
const calculateExtendedPoints = () => {
  const fixedPoints = [
    [canvasWidth.value / 2, padding.value],
    [padding.value, canvasHeight.value - padding.value],
    [canvasWidth.value - padding.value, canvasHeight.value - padding.value]
  ]

  const center = [
    (fixedPoints[0][0] + fixedPoints[1][0] + fixedPoints[2][0]) / 3,
    (fixedPoints[0][1] + fixedPoints[1][1] + fixedPoints[2][1]) / 3
  ]

  const maxVal = Math.max(...values.value)
  const weights = values.value.map(v => v / maxVal)

  return fixedPoints.map((point, i) => {
    const dirX = point[0] - center[0]
    const dirY = point[1] - center[1]

    const maxExtension = Math.min(
      (point[0] >= center[0] ? 
        (canvasWidth.value - padding.value - center[0]) / dirX : 
        (padding.value - center[0]) / dirX),
      (point[1] >= center[1] ? 
        (canvasHeight.value - padding.value - center[1]) / dirY : 
        (padding.value - center[1]) / dirY)
    ) * 0.95

    const extension = weights[i] * maxExtension * 0.8
    return [
      center[0] + dirX * extension,
      center[1] + dirY * extension
    ]
  })
}

// 绘制逻辑(网页6)
const drawTriangle = () => {
  const ctx = uni.createCanvasContext('triangleCanvas',getCurrentInstance())
  const points = calculateExtendedPoints()

  ctx.beginPath()
  ctx.moveTo(points[0][0], points[0][1])
  ctx.lineTo(points[1][0], points[1][1])
  ctx.lineTo(points[2][0], points[2][1])
  ctx.closePath()

  ctx.setFillStyle('#4a90e2')
  ctx.fill()

  ctx.setFontSize(12)
  ctx.setFillStyle('#000')
  points.forEach((p, i) => {
    ctx.fillText(values.value[i].toString(), p[0], p[1])
  })

  ctx.draw()
}

// 生命周期钩子(网页1/网页4)
onMounted(() => {
  drawTriangle()
})

// 重新绘制方法(网页5)
const redrawTriangle = () => {
  values.value = [
    Math.floor(Math.random() * 500),
    Math.floor(Math.random() * 200),
    Math.floor(Math.random() * 300)
  ]
  console.log('values.value',values.value)
  drawTriangle()
}
</script>

<style>
.container {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.canvas {
  width: 300px;
  height: 250px;
  margin-bottom: 20px;
  background-color: #f5f5f5;
  border: 1px solid #ddd;
}
button {
  margin-top: 20px;
  width: 200px;
}
</style>