Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
B
byq_pc
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张伯涛
byq_pc
Commits
4e21d926
Commit
4e21d926
authored
Apr 28, 2025
by
jiaxu.yan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
新增组态展示
parent
1832971f
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
1233 additions
and
25 deletions
+1233
-25
.env.development
.env.development
+1
-1
app.html
src/app.html
+287
-0
index.vue
src/views/index/index.vue
+32
-24
index copy.vue
src/views/makePie/index copy.vue
+379
-0
index.vue
src/views/makePie/index.vue
+534
-0
No files found.
.env.development
View file @
4e21d926
# just a flag
ENV = 'development'
port= 8080
# base api
VUE_APP_BASE_API = '/dev-api'
VUE_CLI_BABEL_TRANSPILE_MODULES = true
...
...
src/app.html
0 → 100644
View file @
4e21d926
<!DOCTYPE html>
<html
lang=
"zh-CN"
>
<head>
<meta
charset=
"UTF-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<title>
Canvas点位与交互效果
</title>
<style>
body
{
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
height
:
100vh
;
margin
:
0
;
background-color
:
#f0f0f0
;
font-family
:
Arial
,
sans-serif
;
}
.container
{
text-align
:
center
;
}
canvas
{
background-color
:
white
;
box-shadow
:
0
0
10px
rgba
(
0
,
0
,
0
,
0.1
);
cursor
:
crosshair
;
}
.controls
{
margin-top
:
20px
;
}
button
{
padding
:
8px
16px
;
margin
:
0
5px
;
cursor
:
pointer
;
}
.mode-indicator
{
margin-top
:
10px
;
font-weight
:
bold
;
color
:
#333
;
}
</style>
</head>
<body>
<div
class=
"container"
>
<h1>
Canvas点位交互演示
</h1>
<canvas
id=
"myCanvas"
width=
"600"
height=
"400"
></canvas>
<div
class=
"controls"
>
<button
id=
"addPoint"
>
添加随机点位
</button>
<button
id=
"clearAll"
>
清除所有点位
</button>
<button
id=
"toggleMode"
>
切换模式: 点击选择
</button>
<p
id=
"info"
>
当前模式: 点击选择点位
</p>
<p
class=
"mode-indicator"
id=
"modeInfo"
>
在拖拽创建模式下,按住鼠标拖动可以创建点位
</p>
</div>
</div>
<script>
document
.
addEventListener
(
'DOMContentLoaded'
,
function
()
{
const
canvas
=
document
.
getElementById
(
'myCanvas'
);
const
ctx
=
canvas
.
getContext
(
'2d'
);
const
addPointBtn
=
document
.
getElementById
(
'addPoint'
);
const
clearAllBtn
=
document
.
getElementById
(
'clearAll'
);
const
toggleModeBtn
=
document
.
getElementById
(
'toggleMode'
);
const
infoText
=
document
.
getElementById
(
'info'
);
const
modeInfoText
=
document
.
getElementById
(
'modeInfo'
);
// 存储所有点位的数组
let
points
=
[];
let
isDragCreationMode
=
false
;
let
isDragging
=
false
;
let
dragStartX
,
dragStartY
;
let
currentDragPoint
=
null
;
// 点位类
class
Point
{
constructor
(
x
,
y
,
radius
=
10
)
{
this
.
x
=
x
;
this
.
y
=
y
;
this
.
radius
=
radius
;
this
.
originalRadius
=
radius
;
this
.
color
=
this
.
getRandomColor
();
this
.
isActive
=
false
;
}
getRandomColor
()
{
// const r = Math.floor(Math.random() * 200 + 55);
// const g = Math.floor(Math.random() * 200 + 55);
// const b = Math.floor(Math.random() * 200 + 55);
return
`rgb(0, 0 ,0)`
;
}
draw
()
{
ctx
.
beginPath
();
ctx
.
arc
(
this
.
x
,
this
.
y
,
this
.
radius
,
0
,
Math
.
PI
*
2
);
ctx
.
fillStyle
=
this
.
isActive
?
'#ff0000'
:
this
.
color
;
ctx
.
fill
();
ctx
.
strokeStyle
=
'#333'
;
ctx
.
stroke
();
// 如果是激活状态,添加脉冲动画效果
if
(
this
.
isActive
)
{
this
.
radius
=
this
.
originalRadius
*
(
1
+
Math
.
sin
(
Date
.
now
()
/
200
)
*
0.2
);
}
else
{
this
.
radius
=
this
.
originalRadius
;
}
}
contains
(
x
,
y
)
{
const
distance
=
Math
.
sqrt
((
x
-
this
.
x
)
**
2
+
(
y
-
this
.
y
)
**
2
);
return
distance
<=
this
.
radius
;
}
}
// 添加随机点位
function
addRandomPoint
()
{
const
radius
=
Math
.
floor
(
Math
.
random
()
*
15
+
5
);
const
x
=
Math
.
random
()
*
(
canvas
.
width
-
radius
*
2
)
+
radius
;
const
y
=
Math
.
random
()
*
(
canvas
.
height
-
radius
*
2
)
+
radius
;
const
point
=
new
Point
(
x
,
y
,
radius
);
points
.
push
(
point
);
drawAllPoints
();
}
// 在指定位置添加点位
function
addPointAt
(
x
,
y
,
radius
=
10
)
{
const
point
=
new
Point
(
x
,
y
,
radius
);
points
.
push
(
point
);
return
point
;
}
// 清除所有点位
function
clearAllPoints
()
{
points
=
[];
ctx
.
clearRect
(
0
,
0
,
canvas
.
width
,
canvas
.
height
);
infoText
.
textContent
=
"已清除所有点位"
;
}
// 绘制所有点位
function
drawAllPoints
()
{
ctx
.
clearRect
(
0
,
0
,
canvas
.
width
,
canvas
.
height
);
// 绘制所有已存在的点位
points
.
forEach
(
point
=>
point
.
draw
());
// 如果在拖拽创建模式下正在拖动,绘制临时点位
if
(
isDragCreationMode
&&
isDragging
&&
currentDragPoint
)
{
currentDragPoint
.
draw
();
// 绘制拖拽引导线
ctx
.
beginPath
();
ctx
.
moveTo
(
dragStartX
,
dragStartY
);
ctx
.
lineTo
(
currentDragPoint
.
x
,
currentDragPoint
.
y
);
ctx
.
strokeStyle
=
'rgba(0, 0, 0, 0.3)'
;
ctx
.
setLineDash
([
5
,
3
]);
ctx
.
stroke
();
ctx
.
setLineDash
([]);
}
requestAnimationFrame
(
drawAllPoints
);
}
// 切换模式
function
toggleMode
()
{
isDragCreationMode
=
!
isDragCreationMode
;
// 更新UI
if
(
isDragCreationMode
)
{
toggleModeBtn
.
textContent
=
"切换模式: 拖拽创建"
;
infoText
.
textContent
=
"当前模式: 拖拽创建点位"
;
modeInfoText
.
style
.
display
=
'block'
;
canvas
.
style
.
cursor
=
'crosshair'
;
}
else
{
toggleModeBtn
.
textContent
=
"切换模式: 点击选择"
;
infoText
.
textContent
=
"当前模式: 点击选择点位"
;
modeInfoText
.
style
.
display
=
'none'
;
canvas
.
style
.
cursor
=
'default'
;
}
// 取消任何进行中的拖拽
isDragging
=
false
;
currentDragPoint
=
null
;
}
// 检查点击是否在点位上
function
handleClick
(
x
,
y
)
{
if
(
isDragCreationMode
)
return
;
let
clickedPoint
=
null
;
// 先取消所有点位的激活状态
points
.
forEach
(
point
=>
{
point
.
isActive
=
false
;
});
// 检查点击是否在某个点位上
for
(
let
i
=
points
.
length
-
1
;
i
>=
0
;
i
--
)
{
if
(
points
[
i
].
contains
(
x
,
y
))
{
clickedPoint
=
points
[
i
];
clickedPoint
.
isActive
=
true
;
infoText
.
textContent
=
`点击了点位 (
${
Math
.
round
(
clickedPoint
.
x
)}
,
${
Math
.
round
(
clickedPoint
.
y
)}
)`
;
break
;
}
}
if
(
!
clickedPoint
)
{
infoText
.
textContent
=
"点击空白区域"
;
}
}
// 事件监听
addPointBtn
.
addEventListener
(
'click'
,
addRandomPoint
);
clearAllBtn
.
addEventListener
(
'click'
,
clearAllPoints
);
toggleModeBtn
.
addEventListener
(
'click'
,
toggleMode
);
canvas
.
addEventListener
(
'click'
,
function
(
e
)
{
const
rect
=
canvas
.
getBoundingClientRect
();
const
x
=
e
.
clientX
-
rect
.
left
;
const
y
=
e
.
clientY
-
rect
.
top
;
handleClick
(
x
,
y
);
});
// 拖拽创建点位相关事件
canvas
.
addEventListener
(
'mousedown'
,
function
(
e
)
{
if
(
!
isDragCreationMode
)
return
;
const
rect
=
canvas
.
getBoundingClientRect
();
dragStartX
=
e
.
clientX
-
rect
.
left
;
dragStartY
=
e
.
clientY
-
rect
.
top
;
// 检查是否点击了现有点位
let
clickedExisting
=
false
;
for
(
let
i
=
points
.
length
-
1
;
i
>=
0
;
i
--
)
{
if
(
points
[
i
].
contains
(
dragStartX
,
dragStartY
))
{
clickedExisting
=
true
;
break
;
}
}
if
(
!
clickedExisting
)
{
isDragging
=
true
;
currentDragPoint
=
addPointAt
(
dragStartX
,
dragStartY
,
5
);
infoText
.
textContent
=
"拖动鼠标调整点位大小"
;
}
});
canvas
.
addEventListener
(
'mousemove'
,
function
(
e
)
{
if
(
!
isDragCreationMode
||
!
isDragging
||
!
currentDragPoint
)
return
;
const
rect
=
canvas
.
getBoundingClientRect
();
const
x
=
e
.
clientX
-
rect
.
left
;
const
y
=
e
.
clientY
-
rect
.
top
;
// 计算拖拽距离作为半径
const
distance
=
Math
.
sqrt
(
Math
.
pow
(
x
-
dragStartX
,
2
)
+
Math
.
pow
(
y
-
dragStartY
,
2
));
currentDragPoint
.
originalRadius
=
Math
.
min
(
Math
.
max
(
distance
,
5
),
50
);
// 限制半径范围
currentDragPoint
.
x
=
dragStartX
;
currentDragPoint
.
y
=
dragStartY
;
});
canvas
.
addEventListener
(
'mouseup'
,
function
(
e
)
{
if
(
!
isDragCreationMode
||
!
isDragging
)
return
;
isDragging
=
false
;
infoText
.
textContent
=
`已创建点位 (
${
Math
.
round
(
currentDragPoint
.
x
)}
,
${
Math
.
round
(
currentDragPoint
.
y
)}
), 半径:
${
Math
.
round
(
currentDragPoint
.
originalRadius
)}
`
;
currentDragPoint
=
null
;
});
canvas
.
addEventListener
(
'mouseleave'
,
function
()
{
if
(
isDragging
)
{
// 如果鼠标离开画布时正在拖拽,取消当前拖拽
if
(
currentDragPoint
)
{
points
.
pop
();
// 移除未完成的点位
}
isDragging
=
false
;
currentDragPoint
=
null
;
}
});
// 初始绘制
drawAllPoints
();
modeInfoText
.
style
.
display
=
'none'
;
// 添加几个初始点位
// for (let i = 0; i
<
5
;
i
++
)
{
// addRandomPoint();
// }
});
</script>
</body>
</html>
src/views/index/index.vue
View file @
4e21d926
<
template
>
<div>
<div
class=
"pageindex
"
>
<div
class=
"index-top"
>
<div
class=
"top-left"
>
图
</div
>
<div
class=
"top-right"
>
状态
</div
>
</div
>
<div
class=
"index-bottom"
>
冷却器状态
</div>
</div
>
<div
class=
"left-bar
"
>
<el-button
:type=
"type == 1"
@
click=
"getType(1)"
>
第一视角
</el-button
>
<el-button
:type=
"type == 2"
@
click=
"getType(2)"
>
第二视角
</el-button
>
<el-button
:type=
"type == 3"
@
click=
"getType(3)"
>
第三视角
</el-button
>
<el-button
:type=
"type == 4"
@
click=
"getType(4)"
>
第四视角
</el-button
>
<el-button
:type=
"type == 5"
@
click=
"getType(5)"
>
第五视角
</el-button
>
<el-button
:type=
"type == 6"
@
click=
"getType(6)"
>
第六视角
</el-button>
</div>
<pie>
</pie
>
</div>
</
template
>
<
script
>
import
pie
from
'@/views/makePie/index.vue'
export
default
{
components
:
{
pie
:
pie
},
data
()
{
return
{
name
:
'特变电压器'
name
:
'特变电压器'
,
type
:
1
}
},
getType
(
e
)
{
console
.
log
(
e
)
this
.
type
=
e
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
.pageindex
{
.pageindex
{
width
:
100%
;
height
:
100%
;
.index-top
{
.index-top
{
display
:
flex
;
.top-left
{
.top-left
{
display
:
flex
;
width
:
40%
;
align-items
:center
;
align-items
:
center
;
justify-content
:
center
;
height
:
400px
;
border
:
1px
solid
#
EEE
;
border
:
1px
solid
#
eee
;
}
.top-right
{
.top-right
{
display
:
flex
;
width
:
60%
;
align-items
:center
;
align-items
:
center
;
justify-content
:
center
;
height
:
400px
;
border
:
1px
solid
#
EEE
;
border
:
1px
solid
#
eee
;
}
}
.index-bottom
{
.index-bottom
{
display
:
flex
;
align-items
:center
;
align-items
:
center
;
justify-content
:
center
;
height
:
300px
;
border
:
1px
solid
#EEE
;
height
:
300px
;
border
:
1px
solid
#eee
;
}
}
</
style
>
src/views/makePie/index copy.vue
0 → 100644
View file @
4e21d926
<
template
>
<div
class=
"container"
>
<canvas
ref=
"canvas"
:width=
"width"
:height=
"height"
@
click=
"handleClick"
@
mousedown=
"handleMouseDown"
@
mousemove=
"handleMouseMove"
@
mouseup=
"handleMouseUp"
@
mouseleave=
"handleMouseLeave"
></canvas>
<div
class=
"left-bar"
>
<el-button
@
click=
"getType(1)"
>
第一视角
</el-button>
<el-button
@
click=
"getType(2)"
>
第二视角
</el-button>
<el-button
@
click=
"getType(3)"
>
第三视角
</el-button>
<el-button
@
click=
"getType(4)"
>
第四视角
</el-button>
<el-button
@
click=
"getType(5)"
>
第五视角
</el-button>
<el-button
@
click=
"getType(6)"
>
第六视角
</el-button>
</div>
<div
class=
"right-bar"
>
<el-upload
ref=
"upload"
:action=
"upload_url"
:before-upload=
"beforeUpload"
:show-file-list=
"false"
:auto-upload=
"true"
:headers=
"headers"
accept=
"image/*"
:on-success=
"handleSuccess"
>
<el-button
slot=
"trigger"
>
上传文件
</el-button>
</el-upload>
</div>
<div
class=
"controls"
>
<el-button
@
click=
"clearAllPoints"
>
清除所有点位
</el-button>
<el-button
@
click=
"toggleMode"
>
切换模式:
{{
isDragCreationMode
?
'点击选择'
:
'拖拽创建'
}}
</el-button>
<p
id=
"info"
>
当前模式:
{{
isDragCreationMode
?
'拖拽创建点位'
:
'点击选择点位'
}}
</p>
<p
v-if=
"isDragCreationMode"
class=
"mode-indicator"
>
在拖拽创建模式下,按住鼠标拖动可以创建点位
</p>
</div>
</div>
</
template
>
<
script
>
import
{
getToken
}
from
'@/utils/auth'
export
default
{
name
:
'PointCanvas'
,
props
:
{
width
:
{
type
:
Number
,
default
:
600
},
height
:
{
type
:
Number
,
default
:
400
},
initialPoints
:
{
type
:
Array
,
default
:
()
=>
[]
}
},
data
()
{
return
{
// 设置上传的请求头部
headers
:
{
Authorization
:
'Bearer '
+
getToken
()
},
// 上传的地址
upload_url
:
process
.
env
.
VUE_APP_BASE_API
+
'/system/user/importExcel'
,
ctx
:
null
,
points
:
[],
isDragCreationMode
:
false
,
isDragging
:
false
,
dragStartX
:
0
,
dragStartY
:
0
,
currentDragPoint
:
null
,
animationFrameId
:
null
,
infoText
:
'点击画布上的点位会有交互效果'
}
},
mounted
()
{
this
.
initCanvas
()
this
.
drawAllPoints
()
// 添加初始点位
if
(
this
.
initialPoints
.
length
>
0
)
{
this
.
points
=
this
.
initialPoints
.
map
(
p
=>
new
Point
(
p
.
x
,
p
.
y
,
p
.
radius
))
}
else
{
for
(
let
i
=
0
;
i
<
5
;
i
++
)
{
this
.
addRandomPoint
()
}
}
},
beforeDestroy
()
{
cancelAnimationFrame
(
this
.
animationFrameId
)
},
methods
:
{
handleSuccess
(
e
,
file
)
{
const
bgImg
=
new
Image
();
bgImg
.
src
=
URL
.
createObjectURL
(
file
.
raw
)
bgImg
.
crossOrigin
=
'Anonymous'
// 解决跨域问题(如果需要)
bgImg
.
onload
=
function
()
{
// 绘制背景(铺满 Canvas)
this
.
ctx
.
drawImage
(
bgImg
,
0
,
0
,
this
.
width
,
this
.
height
)
// 在背景上绘制其他内容
this
.
ctx
.
fillStyle
=
'rgba(255, 255, 255, 0.7)'
this
.
ctx
.
fillRect
(
50
,
50
,
200
,
100
)
this
.
ctx
.
fillStyle
=
'black'
this
.
ctx
.
font
=
'24px Arial'
this
.
ctx
.
fillText
(
'Canvas Background'
,
70
,
100
)
}
// 处理图片加载错误
bgImg
.
onerror
=
function
()
{
console
.
error
(
'背景图片加载失败!'
)
// 可以设置一个纯色备用背景
this
.
ctx
.
fillStyle
=
'lightgray'
this
.
ctx
.
fillRect
(
0
,
0
,
this
.
width
,
this
.
height
)
}
},
beforeUpload
(
file
)
{
const
isImage
=
file
.
type
.
startsWith
(
'image/'
)
if
(
!
isImage
)
{
this
.
$message
.
error
(
'只能上传图片文件!'
)
}
return
isImage
// 返回 false 会阻止上传
},
initCanvas
()
{
const
canvas
=
this
.
$refs
.
canvas
this
.
ctx
=
canvas
.
getContext
(
'2d'
)
},
addRandomPoint
()
{
const
radius
=
Math
.
floor
(
Math
.
random
()
*
15
+
5
)
const
x
=
Math
.
random
()
*
(
this
.
width
-
radius
*
2
)
+
radius
const
y
=
Math
.
random
()
*
(
this
.
height
-
radius
*
2
)
+
radius
this
.
points
.
push
(
new
Point
(
x
,
y
,
radius
))
this
.
infoText
=
'添加了随机点位'
},
addPointAt
(
x
,
y
,
radius
=
10
)
{
const
point
=
new
Point
(
x
,
y
,
radius
)
this
.
points
.
push
(
point
)
return
point
},
clearAllPoints
()
{
this
.
points
=
[]
this
.
ctx
.
clearRect
(
0
,
0
,
this
.
width
,
this
.
height
)
this
.
infoText
=
'已清除所有点位'
},
toggleMode
()
{
this
.
isDragCreationMode
=
!
this
.
isDragCreationMode
// 取消任何进行中的拖拽
this
.
isDragging
=
false
this
.
currentDragPoint
=
null
this
.
infoText
=
`当前模式:
${
this
.
isDragCreationMode
?
'拖拽创建点位'
:
'点击选择点位'
}
`
},
drawAllPoints
()
{
this
.
ctx
.
clearRect
(
0
,
0
,
this
.
width
,
this
.
height
)
// 绘制所有已存在的点位
this
.
points
.
forEach
(
point
=>
point
.
draw
(
this
.
ctx
))
// 如果在拖拽创建模式下正在拖动,绘制临时点位
if
(
this
.
isDragCreationMode
&&
this
.
isDragging
&&
this
.
currentDragPoint
)
{
this
.
currentDragPoint
.
draw
(
this
.
ctx
)
// 绘制拖拽引导线
this
.
ctx
.
beginPath
()
this
.
ctx
.
moveTo
(
this
.
dragStartX
,
this
.
dragStartY
)
this
.
ctx
.
lineTo
(
this
.
currentDragPoint
.
x
,
this
.
currentDragPoint
.
y
)
this
.
ctx
.
strokeStyle
=
'rgba(0, 0, 0, 0.3)'
this
.
ctx
.
setLineDash
([
5
,
3
])
this
.
ctx
.
stroke
()
this
.
ctx
.
setLineDash
([])
}
this
.
animationFrameId
=
requestAnimationFrame
(
this
.
drawAllPoints
)
},
handleClick
(
e
)
{
if
(
this
.
isDragCreationMode
)
return
const
rect
=
this
.
$refs
.
canvas
.
getBoundingClientRect
()
const
x
=
e
.
clientX
-
rect
.
left
const
y
=
e
.
clientY
-
rect
.
top
let
clickedPoint
=
null
// 先取消所有点位的激活状态
this
.
points
.
forEach
(
point
=>
{
point
.
isActive
=
false
})
// 检查点击是否在某个点位上
for
(
let
i
=
this
.
points
.
length
-
1
;
i
>=
0
;
i
--
)
{
if
(
this
.
points
[
i
].
contains
(
x
,
y
))
{
clickedPoint
=
this
.
points
[
i
]
clickedPoint
.
isActive
=
true
this
.
infoText
=
`点击了点位 (
${
Math
.
round
(
clickedPoint
.
x
)}
,
${
Math
.
round
(
clickedPoint
.
y
)}
)`
break
}
}
if
(
!
clickedPoint
)
{
this
.
infoText
=
'点击空白区域'
}
},
handleMouseDown
(
e
)
{
if
(
!
this
.
isDragCreationMode
)
return
const
rect
=
this
.
$refs
.
canvas
.
getBoundingClientRect
()
this
.
dragStartX
=
e
.
clientX
-
rect
.
left
this
.
dragStartY
=
e
.
clientY
-
rect
.
top
// 检查是否点击了现有点位
let
clickedExisting
=
false
for
(
let
i
=
this
.
points
.
length
-
1
;
i
>=
0
;
i
--
)
{
if
(
this
.
points
[
i
].
contains
(
this
.
dragStartX
,
this
.
dragStartY
))
{
clickedExisting
=
true
break
}
}
if
(
!
clickedExisting
)
{
this
.
isDragging
=
true
this
.
currentDragPoint
=
this
.
addPointAt
(
this
.
dragStartX
,
this
.
dragStartY
,
5
)
this
.
infoText
=
'拖动鼠标调整点位大小'
}
},
handleMouseMove
(
e
)
{
if
(
!
this
.
isDragCreationMode
||
!
this
.
isDragging
||
!
this
.
currentDragPoint
)
return
const
rect
=
this
.
$refs
.
canvas
.
getBoundingClientRect
()
const
x
=
e
.
clientX
-
rect
.
left
const
y
=
e
.
clientY
-
rect
.
top
// 计算拖拽距离作为半径
const
distance
=
Math
.
sqrt
(
Math
.
pow
(
x
-
this
.
dragStartX
,
2
)
+
Math
.
pow
(
y
-
this
.
dragStartY
,
2
)
)
this
.
currentDragPoint
.
originalRadius
=
Math
.
min
(
Math
.
max
(
distance
,
5
),
50
)
// 限制半径范围
this
.
currentDragPoint
.
x
=
this
.
dragStartX
this
.
currentDragPoint
.
y
=
this
.
dragStartY
},
handleMouseUp
()
{
if
(
!
this
.
isDragCreationMode
||
!
this
.
isDragging
)
return
this
.
isDragging
=
false
this
.
infoText
=
`已创建点位 (
${
Math
.
round
(
this
.
currentDragPoint
.
x
)}
,
${
Math
.
round
(
this
.
currentDragPoint
.
y
)}
), 半径:
${
Math
.
round
(
this
.
currentDragPoint
.
originalRadius
)}
`
this
.
currentDragPoint
=
null
},
handleMouseLeave
()
{
if
(
this
.
isDragging
)
{
// 如果鼠标离开画布时正在拖拽,取消当前拖拽
if
(
this
.
currentDragPoint
)
{
this
.
points
.
pop
()
// 移除未完成的点位
}
this
.
isDragging
=
false
this
.
currentDragPoint
=
null
}
}
}
}
// Point 类定义
class
Point
{
constructor
(
x
,
y
,
radius
=
10
)
{
this
.
x
=
x
this
.
y
=
y
this
.
radius
=
radius
this
.
originalRadius
=
radius
this
.
color
=
this
.
getRandomColor
()
this
.
isActive
=
false
}
getRandomColor
()
{
const
r
=
Math
.
floor
(
Math
.
random
()
*
200
+
55
)
const
g
=
Math
.
floor
(
Math
.
random
()
*
200
+
55
)
const
b
=
Math
.
floor
(
Math
.
random
()
*
200
+
55
)
return
`rgb(0,0,0)`
}
draw
(
ctx
)
{
ctx
.
beginPath
()
ctx
.
arc
(
this
.
x
,
this
.
y
,
this
.
radius
,
0
,
Math
.
PI
*
2
)
ctx
.
fillStyle
=
this
.
isActive
?
'#ff0000'
:
this
.
color
ctx
.
fill
()
ctx
.
strokeStyle
=
'#333'
ctx
.
stroke
()
// 如果是激活状态,添加脉冲动画效果
if
(
this
.
isActive
)
{
this
.
radius
=
this
.
originalRadius
*
(
1
+
Math
.
sin
(
Date
.
now
()
/
200
)
*
0.2
)
}
else
{
this
.
radius
=
this
.
originalRadius
}
}
contains
(
x
,
y
)
{
const
distance
=
Math
.
sqrt
((
x
-
this
.
x
)
**
2
+
(
y
-
this
.
y
)
**
2
)
return
distance
<=
this
.
radius
}
}
</
script
>
<
style
scoped
>
.container
{
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
justify-content
:
center
;
text-align
:
center
;
}
canvas
{
background-color
:
white
;
box-shadow
:
0
0
10px
rgba
(
0
,
0
,
0
,
0.1
);
cursor
:
default
;
}
.controls
{
margin-top
:
20px
;
}
.right-bar
{
position
:
absolute
;
right
:
150px
;
top
:
20px
;
}
button
{
padding
:
8px
16px
;
margin
:
0
5px
;
cursor
:
pointer
;
}
.mode-indicator
{
margin-top
:
10px
;
font-weight
:
bold
;
color
:
#333
;
}
#info
{
margin-top
:
10px
;
color
:
#333
;
}
</
style
>
src/views/makePie/index.vue
0 → 100644
View file @
4e21d926
<
template
>
<div
class=
"container"
>
<h1>
Canvas点位交互演示
</h1>
<canvas
ref=
"canvas"
:width=
"width"
:height=
"height"
@
click=
"handleClick"
@
mousedown=
"handleMouseDown"
@
mousemove=
"handleMouseMove"
@
mouseup=
"handleMouseUp"
@
mouseleave=
"handleMouseLeave"
@
contextmenu
.
prevent=
"handleContextMenu"
></canvas>
<div
class=
"right-bar"
>
<el-upload
ref=
"upload"
:action=
"upload_url"
:before-upload=
"beforeUpload"
:show-file-list=
"false"
:auto-upload=
"true"
:headers=
"headers"
accept=
"image/*"
:on-success=
"handleSuccess"
>
<el-button
slot=
"trigger"
>
上传文件
</el-button>
</el-upload>
</div>
<!-- 右键菜单 -->
<div
v-if=
"contextMenu.visible"
class=
"context-menu"
:style=
"
{
left: `${contextMenu.x}px`,
top: `${contextMenu.y}px`
}"
>
<div
class=
"menu-item"
@
click=
"changePointColor"
>
更改颜色
</div>
<div
class=
"menu-item"
@
click=
"deletePoint"
>
删除点位
</div>
<div
class=
"menu-item"
@
click=
"perintPoint"
>
设置点位看板
</div>
</div>
<div
class=
"controls"
>
<!--
<button
@
click=
"addRandomPoint"
>
添加随机点位
</button>
-->
<button
@
click=
"clearAllPoints"
>
清除所有点位
</button>
<button
@
click=
"toggleMode"
>
切换模式:
{{
isDragCreationMode
?
'点击选择'
:
'拖拽创建'
}}
</button>
<p
id=
"info"
>
{{
infoText
}}
</p>
<p
v-if=
"isDragCreationMode"
class=
"mode-indicator"
>
在拖拽创建模式下,按住鼠标拖动可以创建点位
</p>
</div>
<!-- 颜色选择器弹窗 -->
<div
v-if=
"colorPicker.visible"
class=
"color-picker-modal"
>
<div
class=
"color-picker"
>
<h3>
选择新颜色
</h3>
<input
type=
"color"
v-model=
"colorPicker.selectedColor"
/>
<div
class=
"button-group"
>
<button
@
click=
"confirmColorChange"
>
确定
</button>
<button
@
click=
"cancelColorChange"
>
取消
</button>
</div>
</div>
</div>
</div>
</
template
>
<
script
>
export
default
{
name
:
'PointCanvasWithContextMenu'
,
props
:
{
width
:
{
type
:
Number
,
default
:
600
},
height
:
{
type
:
Number
,
default
:
400
},
initialPoints
:
{
type
:
Array
,
default
:
()
=>
[]
}
},
data
()
{
return
{
ctx
:
null
,
points
:
[],
isDragCreationMode
:
false
,
isDragging
:
false
,
dragStartX
:
0
,
dragStartY
:
0
,
currentDragPoint
:
null
,
animationFrameId
:
null
,
infoText
:
'点击画布上的点位会有交互效果'
,
// 右键菜单相关
contextMenu
:
{
visible
:
false
,
x
:
0
,
y
:
0
,
point
:
null
},
// 颜色选择器相关
colorPicker
:
{
visible
:
false
,
selectedColor
:
'#ff0000'
,
point
:
null
}
}
},
mounted
()
{
this
.
initCanvas
()
this
.
drawAllPoints
()
// 添加初始点位
if
(
this
.
initialPoints
.
length
>
0
)
{
this
.
points
=
this
.
initialPoints
.
map
(
p
=>
new
Point
(
p
.
x
,
p
.
y
,
p
.
radius
))
}
else
{
for
(
let
i
=
0
;
i
<
5
;
i
++
)
{
this
.
addRandomPoint
()
}
}
// 点击其他地方关闭右键菜单
document
.
addEventListener
(
'click'
,
this
.
closeContextMenu
)
},
beforeDestroy
()
{
cancelAnimationFrame
(
this
.
animationFrameId
)
document
.
removeEventListener
(
'click'
,
this
.
closeContextMenu
)
},
methods
:
{
perintPoint
()
{
const
index
=
this
.
points
.
indexOf
(
this
.
contextMenu
.
point
)
this
.
$emit
(
'perintPoint'
,
this
.
points
[
index
])
},
initCanvas
()
{
const
canvas
=
this
.
$refs
.
canvas
this
.
ctx
=
canvas
.
getContext
(
'2d'
)
},
addRandomPoint
()
{
const
radius
=
Math
.
floor
(
Math
.
random
()
*
15
+
5
)
const
x
=
Math
.
random
()
*
(
this
.
width
-
radius
*
2
)
+
radius
const
y
=
Math
.
random
()
*
(
this
.
height
-
radius
*
2
)
+
radius
this
.
points
.
push
(
new
Point
(
x
,
y
,
radius
))
this
.
infoText
=
'添加了随机点位'
},
addPointAt
(
x
,
y
,
radius
=
10
)
{
const
point
=
new
Point
(
x
,
y
,
radius
)
this
.
points
.
push
(
point
)
return
point
},
clearAllPoints
()
{
this
.
points
=
[]
this
.
ctx
.
clearRect
(
0
,
0
,
this
.
width
,
this
.
height
)
this
.
infoText
=
'已清除所有点位'
},
toggleMode
()
{
this
.
isDragCreationMode
=
!
this
.
isDragCreationMode
this
.
isDragging
=
false
this
.
currentDragPoint
=
null
this
.
infoText
=
`当前模式:
${
this
.
isDragCreationMode
?
'拖拽创建点位'
:
'点击选择点位'
}
`
},
drawAllPoints
()
{
this
.
ctx
.
clearRect
(
0
,
0
,
this
.
width
,
this
.
height
)
this
.
points
.
forEach
(
point
=>
point
.
draw
(
this
.
ctx
))
if
(
this
.
isDragCreationMode
&&
this
.
isDragging
&&
this
.
currentDragPoint
)
{
this
.
currentDragPoint
.
draw
(
this
.
ctx
)
this
.
ctx
.
beginPath
()
this
.
ctx
.
moveTo
(
this
.
dragStartX
,
this
.
dragStartY
)
this
.
ctx
.
lineTo
(
this
.
currentDragPoint
.
x
,
this
.
currentDragPoint
.
y
)
this
.
ctx
.
strokeStyle
=
'rgba(0, 0, 0, 0.3)'
this
.
ctx
.
setLineDash
([
5
,
3
])
this
.
ctx
.
stroke
()
this
.
ctx
.
setLineDash
([])
}
this
.
animationFrameId
=
requestAnimationFrame
(
this
.
drawAllPoints
)
},
handleClick
(
e
)
{
if
(
this
.
isDragCreationMode
)
return
const
rect
=
this
.
$refs
.
canvas
.
getBoundingClientRect
()
const
x
=
e
.
clientX
-
rect
.
left
const
y
=
e
.
clientY
-
rect
.
top
let
clickedPoint
=
null
this
.
points
.
forEach
(
point
=>
{
point
.
isActive
=
false
})
for
(
let
i
=
this
.
points
.
length
-
1
;
i
>=
0
;
i
--
)
{
if
(
this
.
points
[
i
].
contains
(
x
,
y
))
{
clickedPoint
=
this
.
points
[
i
]
clickedPoint
.
isActive
=
true
this
.
infoText
=
`点击了点位 (
${
Math
.
round
(
clickedPoint
.
x
)}
,
${
Math
.
round
(
clickedPoint
.
y
)}
)`
break
}
}
if
(
!
clickedPoint
)
{
this
.
infoText
=
'点击空白区域'
}
},
handleMouseDown
(
e
)
{
if
(
!
this
.
isDragCreationMode
)
return
const
rect
=
this
.
$refs
.
canvas
.
getBoundingClientRect
()
this
.
dragStartX
=
e
.
clientX
-
rect
.
left
this
.
dragStartY
=
e
.
clientY
-
rect
.
top
let
clickedExisting
=
false
for
(
let
i
=
this
.
points
.
length
-
1
;
i
>=
0
;
i
--
)
{
if
(
this
.
points
[
i
].
contains
(
this
.
dragStartX
,
this
.
dragStartY
))
{
clickedExisting
=
true
break
}
}
if
(
!
clickedExisting
)
{
this
.
isDragging
=
true
this
.
currentDragPoint
=
this
.
addPointAt
(
this
.
dragStartX
,
this
.
dragStartY
,
5
)
this
.
infoText
=
'拖动鼠标调整点位大小'
}
},
handleMouseMove
(
e
)
{
if
(
!
this
.
isDragCreationMode
||
!
this
.
isDragging
||
!
this
.
currentDragPoint
)
return
const
rect
=
this
.
$refs
.
canvas
.
getBoundingClientRect
()
const
x
=
e
.
clientX
-
rect
.
left
const
y
=
e
.
clientY
-
rect
.
top
const
distance
=
Math
.
sqrt
(
Math
.
pow
(
x
-
this
.
dragStartX
,
2
)
+
Math
.
pow
(
y
-
this
.
dragStartY
,
2
)
)
this
.
currentDragPoint
.
originalRadius
=
Math
.
min
(
Math
.
max
(
distance
,
5
),
50
)
this
.
currentDragPoint
.
x
=
this
.
dragStartX
this
.
currentDragPoint
.
y
=
this
.
dragStartY
},
handleMouseUp
()
{
if
(
!
this
.
isDragCreationMode
||
!
this
.
isDragging
)
return
this
.
isDragging
=
false
this
.
infoText
=
`已创建点位 (
${
Math
.
round
(
this
.
currentDragPoint
.
x
)}
,
${
Math
.
round
(
this
.
currentDragPoint
.
y
)}
), 半径:
${
Math
.
round
(
this
.
currentDragPoint
.
originalRadius
)}
`
this
.
currentDragPoint
=
null
},
handleMouseLeave
()
{
if
(
this
.
isDragging
)
{
if
(
this
.
currentDragPoint
)
{
this
.
points
.
pop
()
}
this
.
isDragging
=
false
this
.
currentDragPoint
=
null
}
},
// 右键菜单相关方法
handleContextMenu
(
e
)
{
const
rect
=
this
.
$refs
.
canvas
.
getBoundingClientRect
()
const
x
=
e
.
clientX
-
rect
.
left
const
y
=
e
.
clientY
-
rect
.
top
// 查找点击的点位
for
(
let
i
=
this
.
points
.
length
-
1
;
i
>=
0
;
i
--
)
{
if
(
this
.
points
[
i
].
contains
(
x
,
y
))
{
e
.
preventDefault
()
// 显示右键菜单
this
.
contextMenu
=
{
visible
:
true
,
x
:
e
.
clientX
,
y
:
e
.
clientY
,
point
:
this
.
points
[
i
]
}
// 高亮选中的点位
this
.
points
.
forEach
(
p
=>
(
p
.
isActive
=
false
))
this
.
points
[
i
].
isActive
=
true
this
.
infoText
=
`右键点击了点位 (
${
Math
.
round
(
this
.
points
[
i
].
x
)}
,
${
Math
.
round
(
this
.
points
[
i
].
y
)}
)`
return
}
}
// 如果点击的是空白区域,隐藏右键菜单
this
.
closeContextMenu
()
},
closeContextMenu
()
{
this
.
contextMenu
.
visible
=
false
},
// 右键菜单操作
changePointColor
()
{
this
.
colorPicker
=
{
visible
:
true
,
selectedColor
:
this
.
contextMenu
.
point
.
color
,
point
:
this
.
contextMenu
.
point
}
this
.
closeContextMenu
()
},
deletePoint
()
{
const
index
=
this
.
points
.
indexOf
(
this
.
contextMenu
.
point
)
if
(
index
!==
-
1
)
{
this
.
points
.
splice
(
index
,
1
)
this
.
infoText
=
`已删除点位 (
${
Math
.
round
(
this
.
contextMenu
.
point
.
x
)}
,
${
Math
.
round
(
this
.
contextMenu
.
point
.
y
)}
)`
}
this
.
closeContextMenu
()
},
clonePoint
()
{
const
original
=
this
.
contextMenu
.
point
const
newPoint
=
new
Point
(
original
.
x
+
original
.
radius
*
1.5
,
original
.
y
+
original
.
radius
*
1.5
,
original
.
originalRadius
)
newPoint
.
color
=
original
.
color
this
.
points
.
push
(
newPoint
)
this
.
infoText
=
`已复制点位到 (
${
Math
.
round
(
newPoint
.
x
)}
,
${
Math
.
round
(
newPoint
.
y
)}
)`
this
.
closeContextMenu
()
},
addRandomPointNearby
()
{
const
original
=
this
.
contextMenu
.
point
const
radius
=
Math
.
floor
(
Math
.
random
()
*
10
+
5
)
const
angle
=
Math
.
random
()
*
Math
.
PI
*
2
const
distance
=
original
.
radius
*
2
+
Math
.
random
()
*
50
const
x
=
original
.
x
+
Math
.
cos
(
angle
)
*
distance
const
y
=
original
.
y
+
Math
.
sin
(
angle
)
*
distance
this
.
addPointAt
(
x
,
y
,
radius
)
this
.
infoText
=
`已在附近添加随机点位 (
${
Math
.
round
(
x
)}
,
${
Math
.
round
(
y
)}
)`
this
.
closeContextMenu
()
},
// 颜色选择器相关方法
confirmColorChange
()
{
this
.
colorPicker
.
point
.
color
=
this
.
colorPicker
.
selectedColor
this
.
colorPicker
.
visible
=
false
this
.
infoText
=
`已更改点位颜色为
${
this
.
colorPicker
.
selectedColor
}
`
},
cancelColorChange
()
{
this
.
colorPicker
.
visible
=
false
}
}
}
class
Point
{
constructor
(
x
,
y
,
radius
=
10
)
{
this
.
x
=
x
this
.
y
=
y
this
.
radius
=
radius
this
.
originalRadius
=
radius
this
.
color
=
this
.
getRandomColor
()
this
.
isActive
=
false
}
getRandomColor
()
{
const
r
=
Math
.
floor
(
Math
.
random
()
*
200
+
55
)
const
g
=
Math
.
floor
(
Math
.
random
()
*
200
+
55
)
const
b
=
Math
.
floor
(
Math
.
random
()
*
200
+
55
)
return
`rgb(
${
r
}
,
${
g
}
,
${
b
}
)`
}
draw
(
ctx
)
{
ctx
.
beginPath
()
ctx
.
arc
(
this
.
x
,
this
.
y
,
this
.
radius
,
0
,
Math
.
PI
*
2
)
ctx
.
fillStyle
=
this
.
isActive
?
'#ff0000'
:
this
.
color
ctx
.
fill
()
ctx
.
strokeStyle
=
'#333'
ctx
.
stroke
()
if
(
this
.
isActive
)
{
this
.
radius
=
this
.
originalRadius
*
(
1
+
Math
.
sin
(
Date
.
now
()
/
200
)
*
0.2
)
}
else
{
this
.
radius
=
this
.
originalRadius
}
}
contains
(
x
,
y
)
{
const
distance
=
Math
.
sqrt
((
x
-
this
.
x
)
**
2
+
(
y
-
this
.
y
)
**
2
)
return
distance
<=
this
.
radius
}
}
</
script
>
<
style
scoped
>
.container
{
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
justify-content
:
center
;
text-align
:
center
;
position
:
relative
;
}
canvas
{
background-color
:
white
;
box-shadow
:
0
0
10px
rgba
(
0
,
0
,
0
,
0.1
);
cursor
:
default
;
}
.controls
{
margin-top
:
20px
;
}
button
{
padding
:
8px
16px
;
margin
:
0
5px
;
cursor
:
pointer
;
}
.mode-indicator
{
margin-top
:
10px
;
font-weight
:
bold
;
color
:
#333
;
}
#info
{
margin-top
:
10px
;
color
:
#333
;
}
/* 右键菜单样式 */
.context-menu
{
position
:
fixed
;
background-color
:
white
;
border
:
1px
solid
#ddd
;
box-shadow
:
0
2px
10px
rgba
(
0
,
0
,
0
,
0.2
);
border-radius
:
4px
;
z-index
:
1000
;
min-width
:
150px
;
}
.menu-item
{
padding
:
8px
12px
;
cursor
:
pointer
;
}
.menu-item
:hover
{
background-color
:
#f5f5f5
;
}
/* 颜色选择器弹窗 */
.color-picker-modal
{
position
:
fixed
;
top
:
0
;
left
:
0
;
right
:
0
;
bottom
:
0
;
background-color
:
rgba
(
0
,
0
,
0
,
0.5
);
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
z-index
:
1001
;
}
.color-picker
{
background-color
:
white
;
padding
:
20px
;
border-radius
:
8px
;
box-shadow
:
0
0
20px
rgba
(
0
,
0
,
0
,
0.3
);
}
.color-picker
h3
{
margin-top
:
0
;
}
.color-picker
input
[
type
=
'color'
]
{
width
:
100%
;
margin
:
10px
0
;
}
.button-group
{
display
:
flex
;
justify-content
:
space-between
;
}
.button-group
button
{
flex
:
1
;
margin
:
0
5px
;
}
.right-bar
{
position
:
absolute
;
right
:
150px
;
top
:
20px
;
}
</
style
>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment