最近要做一个六边形组成的功能菜单 ,上一个成品图,有用到类似的可以参考一下,基本上属于不规则按钮了

最开始用div加背景图做按钮,但是因为div有重叠部分,做出来体验并不好,

后俩查资料有这种写法

<imgsrc="../assets/img/menubtn_bg.png"border="0" :usemap="'#map'+btntext"alt="Planets" /><map :name="'map'+btntext" :id="'map'+btntext"><area @click="onClick" shape="poly" coords="1,56,96,1,192,56,192,162,96,220,1,162" href="#" /></map>

给一张大图画很多个热点区域作为按钮,前提是需要一整张图片作为背景,感觉并不太好,只是试了试并没有选择这种方式
然后就想起来不常用却经常见到的canvas,

<!--style="background: rgba(225, 225, 225, .1)"--><!--宽度是193 * 7 + 100--><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)"@touchstart="canvasDown($event)"@touchend="canvasUp($event)"@touchmove="canvasMove($event)"></canvas></div>

首先画出来按钮

mounted () {const that = thisthis.mycanvas = this.$refs.mycanvas // 指定canvasthis.ctx = this.mycanvas.getContext('2d') // 设置2D渲染区域this.canvas = this.$refs.canvas // 指定canvasthis.hctx = this.canvas.getContext('2d') // 设置2D渲染区域let btns1 = [{text: 'xx', tag: {}, path: 'Index', icon: require('../assets/img/menu/sy.png'), offset: {x: 0, y: 0}},{text: 'xx', tag: {}, path: 'Integrated', icon: require('../assets/img/menu/zzdw.png'), offset: {x: 5, y: 0}}, // 图标原图不是居中,补偿xy{text: 'xx', tag: {}, path: 'KeyManagement', icon: require('../assets/img/menu/zdgl.png'), offset: {x: 5, y: 0}},{text: 'xx', tag: {}, path: 'Security', icon: require('../assets/img/menu/zagl.png'), offset: {x: 5, y: 0}},{text: 'xx', tag: {}, path: 'Contradiction', icon: require('../assets/img/menu/mdpc.png'), offset: {x: 5, y: 0}},{text: 'xx', tag: {}, path: 'x', icon: require('../assets/img/menu/pacj.png'), offset: {x: 5, y: 0}},{text: 'xx', tag: {}, path: 'x', icon: require('../assets/img/menu/pasq.png'), offset: {x: 5, y: 0}}]let btns2 = [{text: 'xx', tag: {}, path: 'myProtectionLineX', icon: require('../assets/img/menu/hlhx.png'), offset: {x: 5, y: 0}},{text: 'xx', tag: {}, path: 'School', icon: require('../assets/img/menu/xyzb.png'), offset: {x: 5, y: 0}},{text: 'xx', tag: {}, path: 'OA', icon: require('../assets/img/menu/bggl.png'), offset: {x: 5, y: 0}},{text: 'xx', tag: {}, path: 'x', icon: require('../assets/img/menu/tsgz.png'), offset: {x: 5, y: 0}},{text: 'xx', tag: {}, path: 'x', icon: require('../assets/img/menu/zzkh.png'), offset: {x: 5, y: 0}},{text: 'xx', tag: {}, path: 'x', icon: require('../assets/img/menu/tsfx.png'), offset: {x: 5, y: 0}}]// 1,56  96.5,1   193,96.5   193,162   96.5,221  1,162// 96.5, 110.5// 画按钮for (let i = 0; i < btns1.length; i++) {let baseX = 50 + i * 193let baseY = 100this.btnAdd(baseX, baseY, btns1[i].text, btns1[i].tag, btns1[i].path, btns1[i].icon, btns1[i].offset)}for (let i = 0; i < btns2.length; i++) {let baseX = 50 + 96.5 + i * 193let baseY = 270this.btnAdd(baseX, baseY, btns2[i].text, btns2[i].tag, btns2[i].path, btns2[i].icon, btns2[i].offset)}setTimeout(function () {that.cIsLoad = true}, 1000)},
drawImage (x, y, w, h, onSuccess) {let _this = this// 创建新的图片对象let img = new Image()// 指定图片的URLimg.src = imgSrc// 浏览器加载图片完毕后再绘制图片img.onload = function () {// 以Canvas画布上的坐标( x, y)为起始点,绘制图像_this.ctx.drawImage(img, x, y, w, h)if (onSuccess) {onSuccess()}}},drawIcon (x, y, maxX, maxH, icon, offset) {let _this = this// 创建新的图片对象let img = new Image()// 指定图片的URLimg.src = icon// 浏览器加载图片完毕后再绘制图片img.onload = function () {// 以Canvas画布上的坐标( x, y)为起始点,绘制图像let w = img.widthlet h = img.heightif (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}_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 = '#FFFFFF'// 设置水平对齐方式_this.ctx.textAlign = 'center'// 设置垂直对齐方式_this.ctx.textBaseline = 'middle'// 绘制文字(参数:要写的字,x坐标,y坐标)_this.ctx.fillText(text, x, y)},
drawPath (x, y, n, r) {let i, angang = Math.PI * 2 / n // 旋转的角度this.hctx.save() // 保存状态this.hctx.fillStyle = 'rgba(255,255,255,0.1)' // 填充红色,半透明 阴影效果this.hctx.strokeStyle = 'rgba(28, 182, 255, .3)' // 填充绿色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.offsetXlet offsetY = e.offsetYif (e.type === 'touchstart' || e.type === 'touchend') {// touch 没有 offsetX Ylet tX = e.changedTouches[0].clientXlet tY = e.changedTouches[0].clientYlet rect = this.canvas.getBoundingClientRect()offsetX = tX - rect.xoffsetY = tY - rect.y}if (e.type !== 'mousemove') {console.log('=====修正offsetX=', offsetX)console.log('=====修正offsetY=', offsetY)}let obj = nullfor (let i = 0; i < this.buttons.length; i++) {// 判断在哪个个buttonlet find = truefor (let j = 0; j < this.buttons[i].points.length; j++) {// 如果不是最后一个,和下一个点组合,如果是最后一个,和第一个组合let res = falseif (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 = falsebreak}}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, zu = (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) {let _this = thisthis.drawImage(baseX, baseY, 193, 221, function () {_this.drawIcon(baseX, baseY, 193, 180, icon, offset)_this.drawText(baseX + 96.5, baseY + 160, text)})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}]})}
// 鼠标移动时绘制canvasMove (e) {// console.log('=======canvasMove=======', e)// client是基于整个页面的坐标,offset是cavas距离pictureDetail顶部以及左边的距离// let canvasX = e.clientX - e.target.parentNode.offsetLeft// let canvasY = e.clientY - e.target.parentNode.offsetTop// this.ctx.stroke()if (this.cIsLoad) {let btn = this.getBtnObj(e)if (btn) {this.isBtn = true// 缓存图片前如果有已经缓存的图片先加载,避免光圈不能消失// if (this.cContentImg) {// this.hctx.clearRect(0, 0, this.cWidth, this.cHeight)// this.drawCanvasImage()// this.ctx.putImageData(this.cContentImg, 0, 0)// }// 缓存图片// this.cContentImg = this.ctx.getImageData(0, 0, this.cWidth, this.cHeight) // this.mycanvas.toDataURL('image/png') //this.hctx.clearRect(0, 0, this.cWidth, this.cHeight)this.drawPath(btn.center.x, btn.center.y, 6, 102) // 画一个半径为40的六边形} else {this.isBtn = falsethis.hctx.clearRect(0, 0, this.cWidth, this.cHeight)// if (this.cContentImg) {// this.ctx.clearRect(0, 0, this.cWidth, this.cHeight)// this.drawCanvasImage()// this.ctx.putImageData(this.cContentImg, 0, 0)// }}}},
// 鼠标抬起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},

可以看到最初我用的是一个canvas做的,用this.cContentImg = this.ctx.getImageData(0, 0, this.cWidth, this.cHeight)缓存图片数据,但是这种方法在vue打包本地非服务状态下并不行,好像是浏览器本地存储图片数据有跨域问题,然后就换了一种方式实现

应该注意的是在getBtnObj 方法里,因为触屏的设备触发的是touch事件,没有鼠标的offsetxy,这里处理了一下

在写的过程中有了解到canvas面向对象的写法,应该会简单很多,有时间试一试

vue中用canvas 画一个六边形 类似蜂窝形的功能导航主菜单相关推荐

  1. vue中用canvas画移动端电量显示 实现充电动画

    写一个电量组件 battery.vue <template><div><canvas ref="mycanvas" :width="cWid ...

  2. 在小程序中用canvas画一个仪表盘

    本意是打算写一个类似f2的半圆进度条,但是感觉小程序中引入太难调了,所以就自己用canvas随便画了一个实现了类似的效果. 最终效果图 wxml <view class="top-bo ...

  3. 新H5中用canvas画一个数字钟表

    此文章是参考以往大牛代码,在大牛代码基础上加以更改而成. 本人暂时还是一名技术菜鸟,刚刚踏上web前端这条路才五个月,希望大牛们多多提携... 效果图如下: 话不多说,上代码: <body st ...

  4. python程序、画一个笑脸_如何使用canvas画一个微笑的表情(代码示例)

    本篇文章给大家带来的内容是关于如何使用canvas画一个微笑的表情(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 实习期间让我用canvas画一个表情,比较简单,话不多说 ...

  5. [html] 请使用canvas画一个椭圆

    [html] 请使用canvas画一个椭圆 <script> var c=document.getElementById("myCanvas"); var ctx=c. ...

  6. [html] 请使用canvas画一个渐变的长方形

    [html] 请使用canvas画一个渐变的长方形 // 普通canvas绘图工具类// umd适配多种引入方式 (function(root, factory) {if (typeof define ...

  7. 在c++中用编程画一个圆

    在c++中用编程画一个圆 #include<graphics.h> //引用EasyX图形库 #include<Windows.h> int main(void){ initg ...

  8. 用canvas画一个太极图(八卦图)

    用canvas画一个太极图(八卦图) 源码展示链接: https://yanhappiness.github.io/Notes/canvas_src.html 理解什么是canvas canvas是H ...

  9. 如何利用canvas画一个圆,并且填充颜色

    如何利用canvas画一个圆,并且填充颜色(小白专用,大佬勿看) canvas基础 相信在此之前,你对canvas已经有一定的了解了,接下来我将介绍,如何利用canvas画一个圆. 1.新建一个htm ...

  10. 用canvas画一个五星红旗

    利用canvas画一个好看的五星红旗!!! 简单,容易上手! <!DOCTYPE html> <html lang="utf-8"> <meta ht ...

最新文章

  1. 基于DSP的数字振荡器C语言编程,基于DSP的数字振荡器的设计与实现.pdf
  2. 原生js实现对象的深拷贝及循环引用
  3. python数据处理实例-Python数据预处理实例详解
  4. Ext JS 3.0 RC1.1 Released发布了
  5. ZOJ - 2706 Thermal Death of the Universe(线段树)
  6. python数学计算模块_Python-NumPy模块数学和统计方法(4)
  7. redis 备份导出rdb_Redis持久化知识点—RDB+AOF ,你了解多少
  8. oracle 断电 瞬间 同步,Oracle 11g 测试ogg中断之后,重新同步操作
  9. 后缀是php,后缀php是什么文件
  10. vb 6.0 获取重定向的url_免费CloudFlare CDN真香,个人站点加速+Https证书+重定向
  11. Split()函数的作用
  12. 面试(软件实施工程师)
  13. Ghost参数使用详解
  14. 苹果或3月8日开新品发布会是真的吗?
  15. 牛客小白月赛65个人题解A-E
  16. 混合个人训练第四十六场(A,B题解) A 数论线代全家桶 (all) B: 看星星 (stars)
  17. python找出只出现一次的字符_在一个字符串中找到第一个只出现一次的字符。
  18. pikachu通关笔记(九)
  19. 蘑菇导航源码安装教程,wordpress导航主题免费下载[Wordpress主题]
  20. Network of Thrones --基于社区发现算法和图分析Neo4j解读《权力的游戏》(详细!)

热门文章

  1. git上传详细步骤和提交代码的各种报错解决方式.
  2. Aruba 无线调试(Instant AP)
  3. lowB三人组算法-冒泡排序-选择排序-插入排序
  4. 同一服务器部署多个web应用 登录互相影响
  5. td中添加多个input在一行
  6. 阿里P9首次公开:阿里面试官都问些什么?
  7. 2019年区块链安全事件总结,全球损失超60亿美元 | 盘点
  8. Sails.js自动化Api实践与测试
  9. 腾讯地图 地图组件 缩放级别
  10. win10系统点击开始菜单没反应