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
bc5d2b9a
Commit
bc5d2b9a
authored
Apr 29, 2025
by
jiaxu.yan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat:修改查看模式
parent
53460834
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
535 additions
and
0 deletions
+535
-0
card2.vue
src/components/card2.vue
+532
-0
index.vue
src/views/makePie/index.vue
+3
-0
No files found.
src/components/card2.vue
0 → 100644
View file @
bc5d2b9a
<
template
>
<div
class=
"canvas-container"
>
<!-- 工具栏 -->
<div
class=
"toolbar"
>
<button
@
click=
"toggleViewMode"
:class=
"
{ 'active': isViewMode }">
{{
isViewMode
?
'退出查看模式'
:
'进入查看模式'
}}
</button>
<button
@
click=
"addRandomPoint"
:disabled=
"isViewMode"
>
添加随机点
</button>
<button
@
click=
"clearAll"
:disabled=
"isViewMode"
>
清除所有
</button>
<span
class=
"info-text"
>
{{
infoText
}}
</span>
</div>
<!-- 画布 -->
<canvas
ref=
"canvas"
:width=
"width"
:height=
"height"
@
click=
"handleCanvasClick"
@
mousemove=
"handleMouseMove"
></canvas>
<!-- 查看模式弹窗 -->
<div
v-if=
"viewModal.visible"
class=
"view-modal"
:style=
"
{
left: `${viewModal.position.x}px`,
top: `${viewModal.position.y}px`
}"
>
<div
class=
"modal-header"
>
<h3>
点位详情
</h3>
<button
class=
"close-btn"
@
click=
"closeViewModal"
>
×
</button>
</div>
<div
class=
"modal-content"
>
<div
class=
"detail-row"
>
<span
class=
"detail-label"
>
位置:
</span>
<span
class=
"detail-value"
>
(
{{
Math
.
round
(
viewModal
.
point
.
x
)
}}
,
{{
Math
.
round
(
viewModal
.
point
.
y
)
}}
)
</span>
</div>
<div
class=
"detail-row"
>
<span
class=
"detail-label"
>
大小:
</span>
<span
class=
"detail-value"
>
{{
viewModal
.
point
.
radius
}}
px
</span>
</div>
<div
class=
"detail-row"
>
<span
class=
"detail-label"
>
颜色:
</span>
<span
class=
"color-preview"
:style=
"
{ backgroundColor: viewModal.point.color }"
>
</span>
</div>
</div>
</div>
</div>
</
template
>
<
script
>
export
default
{
name
:
'InteractiveCanvas'
,
props
:
{
width
:
{
type
:
Number
,
default
:
800
},
height
:
{
type
:
Number
,
default
:
600
},
initialPoints
:
{
type
:
Array
,
default
:
()
=>
[]
}
},
data
()
{
return
{
ctx
:
null
,
points
:
[],
isViewMode
:
false
,
animationFrameId
:
null
,
infoText
:
'点击画布添加点位或查看详情'
,
viewModal
:
{
visible
:
false
,
point
:
null
,
position
:
{
x
:
0
,
y
:
0
}
},
hoveredPoint
:
null
}
},
mounted
()
{
this
.
initCanvas
()
// 初始化点位
this
.
points
=
this
.
initialPoints
.
length
?
this
.
initialPoints
.
map
(
p
=>
({
...
p
}))
:
this
.
generateRandomPoints
(
5
)
this
.
startAnimation
()
},
beforeDestroy
()
{
cancelAnimationFrame
(
this
.
animationFrameId
)
},
methods
:
{
initCanvas
()
{
const
canvas
=
this
.
$refs
.
canvas
this
.
ctx
=
canvas
.
getContext
(
'2d'
)
},
startAnimation
()
{
const
animate
=
()
=>
{
this
.
drawCanvas
()
this
.
animationFrameId
=
requestAnimationFrame
(
animate
)
}
animate
()
},
drawCanvas
()
{
// 清空画布
this
.
ctx
.
clearRect
(
0
,
0
,
this
.
width
,
this
.
height
)
// 绘制所有点位
this
.
points
.
forEach
(
point
=>
{
this
.
drawPoint
(
point
)
})
// 在查看模式下绘制连线
if
(
this
.
isViewMode
&&
this
.
viewModal
.
visible
)
{
this
.
drawConnectorLine
(
this
.
viewModal
.
point
)
}
// 绘制悬停效果
if
(
this
.
hoveredPoint
)
{
this
.
drawHoverEffect
(
this
.
hoveredPoint
)
}
// 绘制模式提示
this
.
drawModeHint
()
},
drawPoint
(
point
)
{
this
.
ctx
.
beginPath
()
this
.
ctx
.
arc
(
point
.
x
,
point
.
y
,
point
.
radius
,
0
,
Math
.
PI
*
2
)
// 查看模式下选中点位的特殊样式
if
(
this
.
isViewMode
&&
this
.
viewModal
.
point
===
point
)
{
this
.
ctx
.
fillStyle
=
this
.
lightenColor
(
point
.
color
,
20
)
this
.
ctx
.
strokeStyle
=
'#e74c3c'
this
.
ctx
.
lineWidth
=
3
}
// 悬停点位的样式
else
if
(
this
.
hoveredPoint
===
point
)
{
this
.
ctx
.
fillStyle
=
this
.
lightenColor
(
point
.
color
,
10
)
this
.
ctx
.
strokeStyle
=
'#3498db'
this
.
ctx
.
lineWidth
=
2
}
// 普通点位的样式
else
{
this
.
ctx
.
fillStyle
=
point
.
color
this
.
ctx
.
strokeStyle
=
'#2c3e50'
this
.
ctx
.
lineWidth
=
1
}
this
.
ctx
.
fill
()
this
.
ctx
.
stroke
()
// 激活状态的脉冲动画
if
(
point
.
isActive
)
{
point
.
radius
=
point
.
baseRadius
*
(
1
+
Math
.
sin
(
Date
.
now
()
/
300
)
*
0.2
)
}
else
{
point
.
radius
=
point
.
baseRadius
}
},
drawConnectorLine
(
point
)
{
const
canvasPos
=
this
.
$refs
.
canvas
.
getBoundingClientRect
()
const
pointScreenX
=
canvasPos
.
left
+
point
.
x
const
pointScreenY
=
canvasPos
.
top
+
point
.
y
// 计算弹窗连接点位置 (右侧中间)
const
modalConnectorX
=
this
.
viewModal
.
position
.
x
const
modalConnectorY
=
this
.
viewModal
.
position
.
y
+
40
// 40是标题高度
// 创建离屏canvas绘制箭头
const
lineCanvas
=
document
.
createElement
(
'canvas'
)
const
lineCtx
=
lineCanvas
.
getContext
(
'2d'
)
// 计算线长和角度
const
dx
=
modalConnectorX
-
pointScreenX
const
dy
=
modalConnectorY
-
pointScreenY
const
lineLength
=
Math
.
sqrt
(
dx
*
dx
+
dy
*
dy
)
const
angle
=
Math
.
atan2
(
dy
,
dx
)
lineCanvas
.
width
=
lineLength
lineCanvas
.
height
=
lineLength
// 绘制连线
lineCtx
.
beginPath
()
lineCtx
.
moveTo
(
0
,
0
)
lineCtx
.
lineTo
(
lineLength
,
0
)
// 绘制箭头
const
arrowSize
=
10
lineCtx
.
moveTo
(
lineLength
,
0
)
lineCtx
.
lineTo
(
lineLength
-
arrowSize
*
Math
.
cos
(
angle
-
Math
.
PI
/
6
),
-
arrowSize
*
Math
.
sin
(
angle
-
Math
.
PI
/
6
)
)
lineCtx
.
moveTo
(
lineLength
,
0
)
lineCtx
.
lineTo
(
lineLength
-
arrowSize
*
Math
.
cos
(
angle
+
Math
.
PI
/
6
),
-
arrowSize
*
Math
.
sin
(
angle
+
Math
.
PI
/
6
)
)
lineCtx
.
strokeStyle
=
'#3498db'
lineCtx
.
lineWidth
=
2
lineCtx
.
stroke
()
// 在主canvas上绘制
this
.
ctx
.
save
()
this
.
ctx
.
translate
(
pointScreenX
-
canvasPos
.
left
,
pointScreenY
-
canvasPos
.
top
)
this
.
ctx
.
rotate
(
angle
)
this
.
ctx
.
drawImage
(
lineCanvas
,
0
,
-
1
)
// -1是为了让线居中
this
.
ctx
.
restore
()
},
drawHoverEffect
(
point
)
{
this
.
ctx
.
beginPath
()
this
.
ctx
.
arc
(
point
.
x
,
point
.
y
,
point
.
radius
+
8
,
0
,
Math
.
PI
*
2
)
this
.
ctx
.
strokeStyle
=
'rgba(52, 152, 219, 0.5)'
this
.
ctx
.
lineWidth
=
2
this
.
ctx
.
stroke
()
},
drawModeHint
()
{
this
.
ctx
.
fillStyle
=
'rgba(0, 0, 0, 0.7)'
this
.
ctx
.
font
=
'16px Arial'
if
(
this
.
isViewMode
)
{
this
.
ctx
.
fillText
(
'查看模式: 点击点位查看详情'
,
10
,
30
)
if
(
this
.
viewModal
.
visible
)
{
this
.
ctx
.
fillText
(
'点击空白处关闭弹窗'
,
10
,
60
)
}
}
else
{
this
.
ctx
.
fillText
(
'编辑模式: 点击添加点位'
,
10
,
30
)
}
},
toggleViewMode
()
{
this
.
isViewMode
=
!
this
.
isViewMode
this
.
viewModal
.
visible
=
false
this
.
infoText
=
this
.
isViewMode
?
'查看模式: 点击点位查看详情'
:
'编辑模式: 点击添加点位'
},
handleCanvasClick
(
e
)
{
const
rect
=
this
.
$refs
.
canvas
.
getBoundingClientRect
()
const
x
=
e
.
clientX
-
rect
.
left
const
y
=
e
.
clientY
-
rect
.
top
if
(
this
.
isViewMode
)
{
// 查看模式下显示点位详情
const
clickedPoint
=
this
.
getPointAtPosition
(
x
,
y
)
if
(
clickedPoint
)
{
this
.
openViewModal
(
clickedPoint
,
e
.
clientX
,
e
.
clientY
)
}
else
{
this
.
closeViewModal
()
}
}
else
{
// 编辑模式下添加点位
this
.
addPoint
(
x
,
y
)
}
},
handleMouseMove
(
e
)
{
const
rect
=
this
.
$refs
.
canvas
.
getBoundingClientRect
()
const
x
=
e
.
clientX
-
rect
.
left
const
y
=
e
.
clientY
-
rect
.
top
// 更新悬停状态
this
.
hoveredPoint
=
this
.
isViewMode
?
this
.
getPointAtPosition
(
x
,
y
)
:
null
},
openViewModal
(
point
,
clickX
,
clickY
)
{
// 计算弹窗位置 (确保在视口内)
const
modalWidth
=
250
const
modalHeight
=
150
const
padding
=
20
let
modalX
=
clickX
+
padding
let
modalY
=
clickY
+
padding
// 如果右边超出屏幕,向左显示
if
(
modalX
+
modalWidth
>
window
.
innerWidth
)
{
modalX
=
clickX
-
modalWidth
-
padding
}
// 如果底部超出屏幕,向上显示
if
(
modalY
+
modalHeight
>
window
.
innerHeight
)
{
modalY
=
clickY
-
modalHeight
-
padding
}
// 确保不会超出屏幕左边和上边
modalX
=
Math
.
max
(
padding
,
modalX
)
modalY
=
Math
.
max
(
padding
,
modalY
)
this
.
viewModal
=
{
visible
:
true
,
point
:
point
,
position
:
{
x
:
modalX
,
y
:
modalY
}
}
// 激活点位效果
this
.
points
.
forEach
(
p
=>
p
.
isActive
=
false
)
point
.
isActive
=
true
},
closeViewModal
()
{
this
.
viewModal
.
visible
=
false
if
(
this
.
viewModal
.
point
)
{
this
.
viewModal
.
point
.
isActive
=
false
}
},
addPoint
(
x
,
y
)
{
const
newPoint
=
{
x
:
x
,
y
:
y
,
baseRadius
:
15
+
Math
.
random
()
*
10
,
radius
:
15
+
Math
.
random
()
*
10
,
color
:
this
.
getRandomColor
(),
isActive
:
false
}
this
.
points
.
push
(
newPoint
)
this
.
infoText
=
`添加了点位 (
${
Math
.
round
(
x
)}
,
${
Math
.
round
(
y
)}
)`
},
addRandomPoint
()
{
const
x
=
Math
.
random
()
*
(
this
.
width
-
40
)
+
20
const
y
=
Math
.
random
()
*
(
this
.
height
-
40
)
+
20
this
.
addPoint
(
x
,
y
)
},
generateRandomPoints
(
count
)
{
return
Array
.
from
({
length
:
count
},
()
=>
{
const
x
=
Math
.
random
()
*
(
this
.
width
-
40
)
+
20
const
y
=
Math
.
random
()
*
(
this
.
height
-
40
)
+
20
return
{
x
:
x
,
y
:
y
,
baseRadius
:
10
+
Math
.
random
()
*
10
,
radius
:
10
+
Math
.
random
()
*
10
,
color
:
this
.
getRandomColor
(),
isActive
:
false
}
})
},
clearAll
()
{
this
.
points
=
[]
this
.
infoText
=
'已清除所有点位'
},
getPointAtPosition
(
x
,
y
)
{
// 从最上层开始检查
for
(
let
i
=
this
.
points
.
length
-
1
;
i
>=
0
;
i
--
)
{
const
point
=
this
.
points
[
i
]
const
distance
=
Math
.
sqrt
(
Math
.
pow
(
x
-
point
.
x
,
2
)
+
Math
.
pow
(
y
-
point
.
y
,
2
)
if
(
distance
<=
point
.
radius
)
{
return
point
}
}
return
null
},
getRandomColor
()
{
const
hue
=
Math
.
floor
(
Math
.
random
()
*
360
)
return
`hsl(
${
hue
}
, 70%, 60%)`
},
lightenColor
(
color
,
percent
)
{
// 简单实现颜色变亮效果
if
(
color
.
startsWith
(
'hsl'
))
{
return
color
.
replace
(
/
\d
+%
\)
/
,
`
${
Math
.
min
(
100
,
percent
+
60
)}
%`
)
}
return
color
}
}
}
</
script
>
<
style
scoped
>
.canvas-container
{
font-family
:
Arial
,
sans-serif
;
max-width
:
100%
;
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
}
.toolbar
{
margin-bottom
:
15px
;
display
:
flex
;
gap
:
10px
;
align-items
:
center
;
flex-wrap
:
wrap
;
justify-content
:
center
;
}
.toolbar
button
{
padding
:
8px
16px
;
background-color
:
#3498db
;
color
:
white
;
border
:
none
;
border-radius
:
4px
;
cursor
:
pointer
;
font-size
:
14px
;
transition
:
background-color
0.2s
;
}
.toolbar
button
:hover:not
(
:disabled
)
{
background-color
:
#2980b9
;
}
.toolbar
button
.active
{
background-color
:
#2ecc71
;
}
.toolbar
button
.active
:hover
{
background-color
:
#27ae60
;
}
.toolbar
button
:disabled
{
background-color
:
#95a5a6
;
cursor
:
not-allowed
;
}
.info-text
{
margin-left
:
15px
;
color
:
#7f8c8d
;
font-size
:
14px
;
}
canvas
{
border
:
1px
solid
#bdc3c7
;
box-shadow
:
0
2px
10px
rgba
(
0
,
0
,
0
,
0.1
);
background-color
:
white
;
cursor
:
crosshair
;
}
.view-modal
{
position
:
fixed
;
background-color
:
white
;
border-radius
:
8px
;
box-shadow
:
0
4px
20px
rgba
(
0
,
0
,
0
,
0.15
);
z-index
:
1000
;
width
:
250px
;
overflow
:
hidden
;
animation
:
modalFadeIn
0.2s
ease-out
;
}
@keyframes
modalFadeIn
{
from
{
opacity
:
0
;
transform
:
translateY
(
10px
);
}
to
{
opacity
:
1
;
transform
:
translateY
(
0
);
}
}
.modal-header
{
padding
:
12px
15px
;
background-color
:
#3498db
;
color
:
white
;
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
}
.modal-header
h3
{
margin
:
0
;
font-size
:
16px
;
}
.close-btn
{
background
:
none
;
border
:
none
;
color
:
white
;
font-size
:
20px
;
cursor
:
pointer
;
line-height
:
1
;
padding
:
0
0
4px
0
;
width
:
24px
;
height
:
24px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
border-radius
:
50%
;
}
.close-btn
:hover
{
background-color
:
rgba
(
255
,
255
,
255
,
0.2
);
}
.modal-content
{
padding
:
15px
;
}
.detail-row
{
display
:
flex
;
margin-bottom
:
10px
;
align-items
:
center
;
}
.detail-label
{
font-weight
:
bold
;
color
:
#7f8c8d
;
width
:
60px
;
flex-shrink
:
0
;
}
.detail-value
{
color
:
#2c3e50
;
}
.color-preview
{
width
:
20px
;
height
:
20px
;
border-radius
:
4px
;
border
:
1px
solid
#ddd
;
display
:
inline-block
;
}
</
style
>
src/views/makePie/index.vue
View file @
bc5d2b9a
...
...
@@ -45,6 +45,9 @@
<
script
>
import
CanvasWithExternalBackground
from
'@/components/CanvasWithExternalBackground.vue'
import
CanvasWithExternalBackground
from
'@/components/CanvasWithExternalBackground.vue'
import
pic1
from
'@/assets/image/变压器抠图-1.png'
import
pic2
from
'@/assets/image/变压器抠图-2.png'
export
default
{
...
...
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