Commit b54c6275 authored by hucy's avatar hucy

fix:不重要的提交

parent 9a82021a
/**
* 计算从x1y1到x2y2的直线,与水平线形成的夹角
* 计算规则为顺时针从左侧0°到与该直线形成的夹角
* @param {Object} x1
* @param {Object} y1
* @param {Object} x2
* @param {Object} y2
* @param {Boolean} toAngle 是否转换为角度值,默认false
*/
export const getAngle = function (
x1: number,
y1: number,
x2: number,
y2: number,
toAngle = false
) {
const x = x1 - x2;
const y = y1 - y2;
if (!x && !y) {
return 0;
}
// 弧度 radian = 角度 * Math.PI / 180
// 角度 angle = 弧度 * 180 / Math.PI
let res;
// 角度
const angle = (180 + (Math.atan2(-y, -x) * 180) / Math.PI + 360) % 360;
res = 360 - angle;
if (!toAngle) {
res = (res * Math.PI) / 180;
}
return res;
};
import Decimal from 'decimal.js';
interface Path {
x: number;
y: number;
[proppName: string]: any;
}
/**
* 计算2D多边形包围盒
* path: [
* { x: 100, y: 100 },
* { x: 200, y: 100 },
* { x: 200, y: 200 },
* { x: 0, y: 300 },
* ],
* @param {Array} path
*/
export const getBoundingBox = function (path: Path[]) {
let min_x = 0,
min_y = 0,
max_x = 0,
max_y = 0;
const center = { x: 0, y: 0 };
let resPath: Path[] = [];
const length = path.length;
let index = 0;
for (const item of path) {
const x1 = item.x;
const y1 = item.y;
if (index === 0) {
min_x = x1;
min_y = y1;
max_x = x1;
max_y = y1;
path = [];
} else {
if (x1 > max_x) {
max_x = x1;
}
if (x1 < min_x) {
min_x = x1;
}
if (y1 > max_y) {
max_y = y1;
}
if (y1 < min_y) {
min_y = y1;
}
}
index++;
}
if (length === 1) {
center.x = min_x;
center.y = min_y;
} else if (length >= 2) {
if (min_x === max_x) {
// 垂直的线
center.x = min_x;
center.y = Decimal.sub(max_y, min_y).div(2).plus(min_y).toNumber();
} else if (min_y === max_y) {
// 水平的线
center.y = min_y;
center.x = Decimal.sub(max_x, min_x).div(2).plus(min_x).toNumber();
} else {
center.x = Decimal.sub(max_x, min_x).div(2).plus(min_x).toNumber();
center.y = Decimal.sub(max_y, min_y).div(2).plus(min_y).toNumber();
const point1 = {
x: min_x,
y: min_y,
};
const point2 = {
x: max_x,
y: min_y,
};
const point3 = {
x: max_x,
y: max_y,
};
const point4 = {
x: min_x,
y: max_y,
};
resPath = [point1, point2, point3, point4];
}
}
return {
min_x,
min_y,
max_x,
max_y,
path: resPath,
center,
};
};
......@@ -10,6 +10,8 @@ export * from './maths';
export * from './myCloneDeep';
export * from './get-type';
export * from './is';
export * from './get-angle';
export * from './getBoundingBox';
export {
cloneDeep,
orderBy,
......
......@@ -73,7 +73,7 @@ onMounted(() => {
state.layer = new Konva.Layer();
state.stage.add(state.layer);
for (var i = 0; i < 3; i++) {
for (var i = 0; i < 10; i++) {
state.layer.add(createShape());
}
......@@ -102,8 +102,8 @@ function createShape() {
y: state.boundingBox.y,
width: state.boundingBox.width,
height: state.boundingBox.height,
stroke: 'red',
strokeWidth: 1,
stroke: 'black',
strokeWidth: 10,
});
state.group.add(state.box);
return state.group;
......@@ -156,7 +156,7 @@ function layerDragmove(e: any) {
if (group._id == target._id) {
return;
}
// r1当前循环的可视矩形区域 r2当前移动目标的可视矩形区域
// r1当前循环目标的可视矩形区域 r2当前移动目标的可视矩形区域
let r1 = group.getClientRect();
let r2 = targetRect;
let result = haveIntersection(r1, r2);
......@@ -165,7 +165,17 @@ function layerDragmove(e: any) {
// 有碰撞
console.log('1', target);
if (r2.x < r1.x + r1.width / 2) {
target.x(r1.x - r2.width);
if (r2.x + r2.width <= r1.x + r1.width / 2) {
// 10边框宽度
target.x(r1.x - r2.width + 10);
} else {
if (r1.y + r1.height / 2 >= r2.y) {
// 10边框宽度
target.y(r1.y - r2.height + 10);
} else if (r1.y + r1.height / 2 < r2.y) {
target.y(r1.y + r1.height);
}
}
} else {
target.x(r1.x + r1.width);
}
......
<!--
* @FileDescription: vue-konva
* @Author: hcy
* @Date: 2023-03-29
-->
<script setup lang="ts">
import { reactive, onMounted } from 'vue';
import { getBoundingBox } from 'src/common/utils';
import Konva from 'konva';
import Decimal from 'decimal.js';
const state = reactive({
stage: null as any,
layer: null as any,
group: null as any,
// shape: null as any,
// boundingBox: null as any,
// box: null as any,
shapePath: [
{
name: 'test1',
color: '#00D2FF',
path: [
{ x: 100, y: 100 },
{ x: 200, y: 100 },
{ x: 200, y: 200 },
{ x: 0, y: 300 },
],
},
{
name: 'test2',
color: '#4CAF50',
path: [
{ x: 300, y: 200 },
{ x: 200, y: 400 },
{ x: 100, y: 600 },
{ x: 400, y: 600 },
{ x: 500, y: 200 },
],
},
{
name: 'test3',
color: '#FF8A80',
path: [
{ x: 600, y: 200 },
{ x: 500, y: 400 },
{ x: 300, y: 100 },
],
},
] as any[],
});
const stageSize = reactive({
width: 1200,
height: 800,
gridGap: 100,
});
onMounted(() => {
// const hit = Collide2D.collidePointPoint(100, 100, 100, 100);
// console.log('hit', Collide2D);
drawGrid();
handleData();
console.log('数据', state.shapePath);
state.stage = new Konva.Stage({
container: 'stage-container',
width: stageSize.width,
height: stageSize.height,
});
state.layer = new Konva.Layer();
state.stage.add(state.layer);
for (const item of state.shapePath) {
let shape = createShape(item);
if (shape) {
state.layer.add(shape);
}
}
state.layer.on('dragmove', layerDragmove);
});
function createShape(itemData: any) {
const path = itemData.path || [];
const fillColor = itemData.color || '#000000';
const name = itemData.name;
const boundingBoxPath = itemData.boundingBox?.path || [];
if (boundingBoxPath.length < 2) {
console.warn('小于两个点,不能构成多边形,没有边界框');
} else {
let group = new Konva.Group({
x: boundingBoxPath[0].x,
y: boundingBoxPath[0].y,
draggable: true,
});
// let rectBox = new Konva.Rect({
// x: group.x(),
// y: group.y(),
// width: state.boundingBox.width,
// height: state.boundingBox.height,
// stroke: 'black',
// strokeWidth: 10,
// });
let shape: any = new Konva.Shape({
sceneFunc: function (context, shape) {
context.beginPath();
let index = 0;
for (const i of path) {
const x = i.x;
const y = i.y;
if (index === 0) {
context.moveTo(x, y);
} else {
context.lineTo(x, y);
}
// context.quadraticCurveTo(150, 100, 260, 170); 弧线
index++;
}
context.closePath();
// (!) Konva specific method, it is very important
context.fillStrokeShape(shape);
},
fill: fillColor,
name,
// stroke: 'white',
// strokeWidth: 10,
});
group.add(shape);
return group;
}
}
function layerDragmove(e: any) {
let target = e.target;
state.layer.children.forEach(function (group: any) {
// do not check intersection with itself
// 不检查与自身相交
if (group._id == target._id) {
return;
}
});
}
function handleData() {
for (const item of state.shapePath) {
item.path = removeAngleRepeat(item.path || []);
item.boundingBox = getBoundingBox(item.path || []);
}
}
// 一条线上有多个点,只保留起点和终点
function removeAngleRepeat(path: any[]) {
let length = path.length;
if (length >= 2) {
let myMap = new Map();
let index = 1;
for (const item of path) {
let x1, y1, x2, y2;
if (index < length) {
x1 = item.x;
y1 = item.y;
x2 = path[index].x;
y2 = path[index].y;
} else {
// 最后一个点连第一个点
x1 = item.x;
y1 = item.y;
x2 = path[0].x;
y2 = path[0].y;
}
let du = getAngle(x1, y1, x2, y2, true);
if (!myMap.has(du)) {
myMap.set(du, item);
}
// console.log('du', du);
index++;
}
let newArr: any[] = [];
for (const value of myMap.values()) {
newArr.push(value);
}
return newArr;
} else {
return path;
}
}
/**
* 计算从x1y1到x2y2的直线,与水平线形成的夹角
* 计算规则为顺时针从左侧0°到与该直线形成的夹角
* @param {Object} x1
* @param {Object} y1
* @param {Object} x2
* @param {Object} y2
* @param {Boolean} toAngle 是否转换为角度值,默认false
*/
function getAngle(
x1: number,
y1: number,
x2: number,
y2: number,
toAngle = false
) {
let x = Decimal.sub(x1, x2);
let y = Decimal.sub(y1, y2);
if (!x.toNumber() && !y.toNumber()) {
return 0;
}
// 弧度 radian = 角度 * Math.PI / 180
// 角度 angle = 弧度 * 180 / Math.PI
let res;
// 角度
// let angle = (180 + (Math.atan2(-y, -x) * 180) / Math.PI + 360) % 360;
let _atan2 = Decimal.atan2(y.negated(), x.negated());
let _angle = Decimal.div(Decimal.mul(_atan2, 180), Math.PI);
let angle = Decimal.mod(Decimal.add(180, _angle).plus(360), 360);
res = Decimal.sub(360, angle);
if (!toAngle) {
res = Decimal.mul(res, Math.PI).div(180);
}
return res.toNumber();
}
// 网格线
function drawGrid() {
let canvas: any = document.getElementById('canvas-grid');
let pen = canvas.getContext('2d');
// 绘制网格
const step = stageSize.gridGap;
const h = stageSize.height;
const w = stageSize.width;
const w_l = w / step;
const h_l = h / step;
// 横着的线
for (let i = 0; i <= h_l; i++) {
pen.beginPath();
pen.moveTo(0, i * step);
pen.lineTo(w, i * step);
pen.stroke();
}
// 竖着的线
for (let i = 0; i <= w_l; i++) {
pen.beginPath();
pen.moveTo(i * step, 0);
pen.lineTo(i * step, h);
pen.stroke();
}
}
</script>
<template>
<div class="konva-main-page container-height center">
<div class="canvas-box">
<canvas
id="canvas-grid"
:width="stageSize.width"
:height="stageSize.height"
style="position: absolute"
></canvas>
<div id="stage-container"></div>
</div>
</div>
</template>
<style lang="scss" scoped>
.konva-main-page {
}
.canvas-box {
box-sizing: border-box;
width: 1200px;
height: 800px;
border: 1px solid #000;
background: pink;
}
</style>
......@@ -2,7 +2,7 @@ export default [
{
path: 'vue-konva',
name: 'VUE_KONVA',
component: () => import('./IndexPage.vue'),
component: () => import('./IndexPage2.vue'),
meta: {
title: 'Vue Konva',
permission: ['*'],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment