给定三个点画三角形底部固定横着放
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>