存在缺陷
下面是完整的组件代码,有想要查看效果的,直接复制到vue项目中,替换到图片地址既可(代码较low,勿喷~~~):
<template>
<div class="canvas_box" id="canvas_box">
<div class="content_box" id="content_box">
<div
style="position: relative;margin-left: 50%;margin-right: 50%;"
:width="cWidth"
:height="cHeight"
:style="'left:' + (- cWidth / 2 ) + 'px'"
>
<canvas ref="mycanvas" :width="cWidth" :height="cHeight"></canvas>
<canvas
ref="canvas"
:width="cWidth"
:height="cHeight"
:style="isBtn ? 'cursor: pointer' : 'cursor: default'"
style="position: absolute;left: 0;top: 0;z-index: 999"
@mousedown="canvasDown($event)"
@mouseup="canvasUp($event)"
@mousemove="canvasMove($event)"
></canvas>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'canvasHive',
data () {
return {
cWidth: null,
cHeight: null,
isBtn: false,
buttons: [],
isMoveDown: null,
mycanvas: null,
canvas: null,
cIsLoad: false,
ctx: null,
hctx: null,
hiveRadius: 0
}
},
mounted () {
this.cWidth = document.getElementById('content_box').clientWidth
this.cHeight = document.getElementById('content_box').clientHeight
this.hiveRadius = Math.floor(this.cWidth / 10 - this.cHeight / 10)
console.log(this.hiveRadius)
let that = this
that.drawBtnInit(this.hiveRadius)
setTimeout(function () {
that.cIsLoad = true
}, 1000)
// 添加对窗口大小的监听,同时需要调用绘制函数
window.addEventListener('resize', function () {
that.cWidth = document.getElementById('content_box').clientWidth
that.cHeight = document.getElementById('content_box').clientHeight
that.ctx.fillRect(0, 0, that.cWidth, that.cHeight)
that.hctx.fillRect(0, 0, that.cWidth, that.cHeight)
that.hiveRadius = Math.floor(that.cWidth / 10 - that.cHeight / 10)
that.drawBtnInit(that.hiveRadius)
})
},
methods: {
// 绘制按钮函数
drawBtnInit () {
const that = this
that.mycanvas = that.$refs.mycanvas // 指定canvas
that.ctx = that.mycanvas.getContext('2d') // 设置2D渲染区域
that.canvas = that.$refs.canvas // 指定canvas
that.hctx = that.canvas.getContext('2d') // 设置2D渲染区域
// 修改数据,简化代码
let btnsArray = [
[
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
],
[
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 0, y: 20 }, isRealBtn: false },
// { text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/qqcutTect.png'), offset: { x: 0, y: 20 } },
{ text: 'xx', tag: {}, path: 'Index', icon: require('../../assets/svg-for-middleware/Elasticsearch.svg'), offset: { x: 50, y: 20 }, isRealBtn: true },
{ text: 'xx', tag: {}, path: 'Integrated', icon: require('../../assets/svg-for-middleware/Kafka.svg'), offset: { x: 50, y: 20 }, isRealBtn: true }, // 图标原图不是居中,补偿xy
{ text: 'xx', tag: {}, path: 'KeyManagement', icon: require('../../assets/svg-for-middleware/MySQL (1).svg'), offset: { x: 50, y: 20 }, isRealBtn: true },
{ text: 'xx', tag: {}, path: 'Security', icon: require('../../assets/svg-for-middleware/Spark 2.svg'), offset: { x: 50, y: 20 }, isRealBtn: true },
{ text: 'xx', tag: {}, path: 'Contradiction', icon: require('../../assets/svg-for-middleware/Zookeeper (1).svg'), offset: { x: 50, y: 20 }, isRealBtn: true },
{ text: 'xx', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/logstash.svg'), offset: { x: 50, y: 20 }, isRealBtn: true },
{ text: 'xx', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/neo4j.svg'), offset: { x: 50, y: 20 }, isRealBtn: true },
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 0, y: 20 }, isRealBtn: false },
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 0, y: 20 }, isRealBtn: false },
],
[
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
{ text: 'xx', tag: {}, path: 'myProtectionLineX', icon: require('../../assets/svg-for-middleware/spring.svg'), offset: { x: 50, y: 20 }, isRealBtn: true },
{ text: 'xx', tag: {}, path: 'School', icon: require('../../assets/svg-for-middleware/nginx.svg'), offset: { x: 50, y: 20 }, isRealBtn: true },
{ text: 'xx', tag: {}, path: 'OA', icon: require('../../assets/svg-for-middleware/redis (1).svg'), offset: { x: 50, y: 20 }, isRealBtn: true },
{ text: 'xx', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/spring-logo.svg'), offset: { x: 50, y: 20 }, isRealBtn: true },
{ text: 'xx', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/表格.svg'), offset: { x: 50, y: 20 }, isRealBtn: true },
{ text: 'xx', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/MySQL.svg'), offset: { x: 50, y: 20 }, isRealBtn: true },
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
],
[
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
{ text: '', tag: {}, path: 'x', icon: require('../../assets/svg-for-middleware/透明图片.png'), offset: { x: 50, y: 20 }, isRealBtn: false },
]
]
let baseX
let baseY
for (let i = 0, iLen = btnsArray.length; i < iLen; i++) {
for (let j = 0, jLen = btnsArray[i].length; j < jLen; j++) {
if (i % 2 === 0) {
baseX = -50 + j * 193
} else {
baseX = 50 + j * 193
}
baseY = 50 + i * 170
that.btnAdd(baseX, baseY, btnsArray[i][j].text, btnsArray[i][j].tag, btnsArray[i][j].path, btnsArray[i][j].icon, btnsArray[i][j].offset, btnsArray[i][j].isRealBtn)
}
}
},
drawImage (x, y, w, h, icon, onSuccess) {
let _this = this
// 创建新的图片对象
let img = new Image()
// 指定图片的URL
// img.src = imgSrc
img.src = icon //
// 浏览器加载图片完毕后再绘制图片
// _this.ctx.drawImage(img, x, y, w, h)
img.onload = function () {
// 以Canvas画布上的坐标( x, y)为起始点,绘制图像
if (onSuccess) {
onSuccess()
}
}
},
drawIcon (x, y, maxX, maxH, icon, offset) {
// console.log(offset)
let _this = this
// 创建新的图片对象
let img = new Image()
// 指定图片的URL
img.src = icon
// 浏览器加载图片完毕后再绘制图片
img.onload = function () {
// 以Canvas画布上的坐标( x, y)为起始点,绘制图像
let w = img.width
let h = img.height
if (maxX > img.width) {
x = x + (maxX - img.width) / 2
} else {
w = maxX
}
if (maxH > img.height) {
y = y + (maxH - img.height) / 2
} else {
h = maxH
}
debugger
_this.ctx.drawImage(img, x + offset.x, y + offset.y, w, h)
}
},
drawText (x, y, text) {
let _this = this
_this.ctx.font = '18px bold 黑体'
// 设置颜色
_this.ctx.fillStyle = 'green'
// 设置水平对齐方式
_this.ctx.textAlign = 'center'
// 设置垂直对齐方式
_this.ctx.textBaseline = 'middle'
// 绘制文字(参数:要写的字,x坐标,y坐标)
_this.ctx.fillText(text, x, y)
},
drawlowLevelPath (x, y, n, r) {
let i, ang
ang = Math.PI * 2 / n // 旋转的角度
this.ctx.save() // 保存状态
// this.hctx.fillStyle = 'cyan' // 填充红色,半透明 阴影效果
// this.ctx.strokeStyle = 'green' // 填充边框色
// this.hctx.fillStyle = 'rgba(255,255,255,0.1)' // 填充红色,半透明 阴影效果
this.ctx.fillStyle = 'transparent' // 填充红色,半透明 阴影效果
this.ctx.strokeStyle = 'rgba(28, 182, 255, .3)' // 填充边框色
this.ctx.lineWidth = 6 // 设置线宽
this.ctx.translate(x, y) // 原点移到x,y处,即要画的多边形中心
this.ctx.moveTo(0, -r) // 据中心r距离处画点
this.ctx.beginPath()
for (i = 0; i < n; i++) {
this.ctx.rotate(ang) // 旋转
this.ctx.lineTo(0, -r) // 据中心r距离处连线
}
this.ctx.closePath()
this.ctx.stroke()
this.ctx.fill()
this.ctx.restore() // 返回原始状态
},
drawLinearGradient () {
let grd = this.ctx.createLinearGradient(0, 0, this.cWidth, 0);
grd.addColorStop(0, "white");
grd.addColorStop(0.5, "#88ded2");
grd.addColorStop(1, "white");
// 填充渐变
this.ctx.fillStyle = grd;
this.ctx.fillRect(0, 0, this.cWidth, this.cHeight);
},
drawPath (x, y, n, r) {
let i, ang
ang = Math.PI * 2 / n // 旋转的角度
this.hctx.save() // 保存状态
this.hctx.fillStyle = 'rgba(255,255,255,0.3)' // 填充红色,半透明 阴影效果
this.hctx.strokeStyle = 'rgba(23, 97, 105, .8)' // 填充边框色
this.hctx.lineWidth = 6 // 设置线宽
this.hctx.translate(x, y) // 原点移到x,y处,即要画的多边形中心
this.hctx.moveTo(0, -r) // 据中心r距离处画点
this.hctx.beginPath()
for (i = 0; i < n; i++) {
this.hctx.rotate(ang) // 旋转
this.hctx.lineTo(0, -r) // 据中心r距离处连线
}
this.hctx.closePath()
this.hctx.stroke()
this.hctx.fill()
this.hctx.restore() // 返回原始状态
},
getBtnObj (e) {
let offsetX = e.offsetX
let offsetY = e.offsetY
if (e.type === 'touchstart' || e.type === 'touchend') {
// touch 没有 offsetX Y
let tX = e.changedTouches[0].clientX
let tY = e.changedTouches[0].clientY
let rect = this.canvas.getBoundingClientRect()
offsetX = tX - rect.x
offsetY = tY - rect.y
}
if (e.type !== 'mousemove') {
console.log('=====修正offsetX=', offsetX)
console.log('=====修正offsetY=', offsetY)
}
let obj = null
for (let i = 0; i < this.buttons.length; i++) {
// 判断在哪个个button
let find = true
for (let j = 0; j < this.buttons[i].points.length; j++) {
// 如果不是最后一个,和下一个点组合,如果是最后一个,和第一个组合
let res = false
if (j === this.buttons[i].points.length - 1) {
res = this.judgeIntersect(offsetX, offsetY, this.buttons[i].center.x, this.buttons[i].center.y, this.buttons[i].points[j].x, this.buttons[i].points[j].y, this.buttons[i].points[0].x, this.buttons[i].points[0].y)
} else {
res = this.judgeIntersect(offsetX, offsetY, this.buttons[i].center.x, this.buttons[i].center.y, this.buttons[i].points[j].x, this.buttons[i].points[j].y, this.buttons[i].points[j + 1].x, this.buttons[i].points[j + 1].y)
}
if (res) {
find = false
break
}
}
if (find) {
// console.log('================' + i)
obj = this.buttons[i]
break
}
}
return obj
},
// 判断两条线是否相交
judgeIntersect (x1, y1, x2, y2, x3, y3, x4, y4) {
if (!(Math.min(x1, x2) <= Math.max(x3, x4) && Math.min(y3, y4) <= Math.max(y1, y2) && Math.min(x3, x4) <= Math.max(x1, x2) && Math.min(y1, y2) <= Math.max(y3, y4))) {
return false
}
let u, v, w, z
u = (x3 - x1) * (y2 - y1) - (x2 - x1) * (y3 - y1)
v = (x4 - x1) * (y2 - y1) - (x2 - x1) * (y4 - y1)
w = (x1 - x3) * (y4 - y3) - (x4 - x3) * (y1 - y3)
z = (x2 - x3) * (y4 - y3) - (x4 - x3) * (y2 - y3)
return (u * v <= 0.00000001 && w * z <= 0.00000001)
},
btnAdd (baseX, baseY, text, tag, path, icon, offset, isRealBtn) {
let _this = this
this.drawImage(baseX, baseY, 193, 221, icon, () => {
_this.drawLinearGradient()
setTimeout(() => {
_this.drawIcon(baseX, baseY, 80, 80, icon, offset)
_this.drawText(baseX + 90.5, baseY + 135, text)
_this.drawlowLevelPath(baseX + 95, baseY + 75, 6, 102)
}, 500)
})
this.buttons.push({
text: text,
tag: tag,
path: path,
basePoint: { x: baseX, y: baseY },
center: { x: baseX + 96.5, y: baseY + 110.5 },
points: [{ x: baseX + 1, y: baseY + 56 }, { x: baseX + 96.5, y: baseY + 1 }, { x: baseX + 193, y: baseY + 96.5 }, { x: baseX + 193, y: baseY + 162 }, { x: baseX + 96.5, y: baseY + 221 }, { x: baseX + 1, y: baseY + 162 }],
isRealBtn // 增加一个属性,由于标记是否是真实的按钮
})
},
// 鼠标移动时绘制
canvasMove (e) {
if (this.cIsLoad) {
let btn = this.getBtnObj(e)
if (btn) {
this.isBtn = true
this.hctx.clearRect(0, 0, this.cWidth, this.cHeight)
// if (!btn.isRealBtn) { return }
this.drawPath(btn.center.x, btn.center.y - 36, 6, 102) // 画一个六边形
} else {
this.isBtn = false
this.hctx.clearRect(0, 0, this.cWidth, this.cHeight)
}
}
},
// 鼠标抬起
canvasUp (e) {
// console.log('=======canvasUp=======', e)
let btn = this.getBtnObj(e)
if (btn) {
this.menuClick(btn.text, btn.path)
}
// console.log('=======canvasUp=======', btn)
this.isMoveDown = false
},
menuClick (text, path) {
console.log(text)
},
canvasDown (e) {
console.log(e)
}
}
}
</script>
<style scoped>
.canvas_box {
background-color: #f5f5f5;
}
.content_box {
margin: 10px auto;
height: 97%;
box-shadow: 2px 2px 5px 5px #999;
width: 98%;
background-color: #ffffff;
overflow: hidden;
}
</style>
至此就基本上实现了效果。