You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Konva绘制2D房间:实现带厚度的外墙生成需求

React Konva 2D房间外墙生成解决方案

针对你需要生成厚度为20的外墙需求,以下是基于凸多边形外偏移的通用实现方案,完全匹配你给出的正方形示例,同时支持任意凸多边形房间的外墙生成:

核心思路

  1. 将原始内部点集转换为顶点数组(去除重复的闭合起点)
  2. 对每条边计算向外的单位法向量
  3. 基于相邻边的法向量平均方向,偏移每个顶点得到外墙顶点
  4. 连接内外对应顶点,形成契合的墙体

实现代码

工具函数

// 转换原始点数组为顶点坐标对(去除最后一个重复的闭合点)
const getVertices = (points) => {
  const vertices = [];
  for (let i = 0; i < points.length - 2; i += 2) {
    vertices.push([points[i], points[i + 1]]);
  }
  return vertices;
};

// 获取边的单位法向量(isOutward控制向外方向,适配顺时针顶点排列)
const getUnitNormal = (edge, isOutward) => {
  // 顺时针多边形的向外法向量为 [edge.y, -edge.x]
  const normal = isOutward ? [edge[1], -edge[0]] : [-edge[1], edge[0]];
  const length = Math.hypot(normal[0], normal[1]);
  return [normal[0] / length, normal[1] / length];
};

// 生成外偏移后的多边形点集
const offsetPolygon = (points, offset) => {
  const vertices = getVertices(points);
  const offsetVertices = [];
  const vertexCount = vertices.length;

  for (let i = 0; i < vertexCount; i++) {
    const current = vertices[i];
    const next = vertices[(i + 1) % vertexCount];
    const prev = vertices[(i - 1 + vertexCount) % vertexCount];

    // 计算当前边和前一条边的向量
    const edge1 = [next[0] - current[0], next[1] - current[1]];
    const edge2 = [current[0] - prev[0], current[1] - prev[1]];

    // 获取两条边的向外单位法向量
    const normal1 = getUnitNormal(edge1, true);
    const normal2 = getUnitNormal(edge2, true);

    // 计算顶点偏移的平均方向并归一化
    const avgNormal = [(normal1[0] + normal2[0]) / 2, (normal1[1] + normal2[1]) / 2];
    const avgLength = Math.hypot(avgNormal[0], avgNormal[1]);
    const unitAvgNormal = [avgNormal[0] / avgLength, avgNormal[1] / avgLength];

    // 计算偏移后的顶点坐标
    offsetVertices.push([
      current[0] + unitAvgNormal[0] * offset,
      current[1] + unitAvgNormal[1] * offset
    ]);
  }

  // 转换回原始点集格式并闭合
  const offsetPoints = [];
  offsetVertices.forEach(v => offsetPoints.push(v[0], v[1]));
  offsetPoints.push(offsetVertices[0][0], offsetVertices[0][1]);
  return offsetPoints;
};

React Konva组件集成

import { useState } from 'react';
import { Stage, Layer, Line } from 'react-konva';

const RoomDesigner = () => {
  // 内部房间点集(用户绘制完成后的闭合点)
  const [innerPoints, setInnerPoints] = useState([
    100, 100, 500, 100, 500, 500, 100, 500, 100, 100,
  ]);
  // 生成外墙点集(偏移量20)
  const outerPoints = offsetPolygon(innerPoints, 20);

  // 生成连接内外墙的墙体线段点集
  const wallSegments = [];
  const innerVertices = getVertices(innerPoints);
  const outerVertices = getVertices(outerPoints);
  innerVertices.forEach((inner, idx) => {
    const outer = outerVertices[idx];
    wallSegments.push(inner[0], inner[1], outer[0], outer[1], null);
  });

  return (
    <Stage width={800} height={600}>
      <Layer>
        {/* 内部空间轮廓 */}
        <Line
          points={innerPoints}
          stroke="#3498db"
          strokeWidth={2}
          closed
        />
        {/* 外墙轮廓 */}
        <Line
          points={outerPoints}
          stroke="#2c3e50"
          strokeWidth={2}
          closed
        />
        {/* 墙体实体(厚度20) */}
        <Line
          points={wallSegments}
          stroke="#7f8c8d"
          strokeWidth={20}
          lineCap="round"
        />
      </Layer>
    </Stage>
  );
};

export default RoomDesigner;

关键注意事项

  • 顶点排列顺序:代码默认内部点集为顺时针排列,若用户绘制是逆时针,需修改getUnitNormal中的法向量为[-edge[1], edge[0]]
  • 适用范围:该方案仅适用于凸多边形房间(绝大多数实际场景),若需支持凹多边形,需额外引入多边形偏移的专用算法库
  • 厚度匹配:墙体的strokeWidth需与偏移量保持一致(示例中为20),确保视觉上的厚度符合需求

内容的提问来源于stack exchange,提问作者Ahmad Alshurafa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 10:05:23