阅读 144

canvas 选取一个物体

在普通画布上选取

想要在canvas上选择一个物体,首先需要鼠标在canvas的坐标,然后需要一个方法来判断鼠标坐标是否落在目标物体内.

  1. 首先我们先画一个圆

image-20211127153402174.png

    // app.js     let canvas = document.createElement('canvas');     canvas.width = 200; // canvas宽度     canvas.height = 200; // canvas高度     const ctx = canvas.getContext('2d')     document.querySelector('body').appendChild(canvas)          window.requestAnimationFrame(draw)     function draw() {         ctx.clearRect(0, 0, 200, 200)              ctx.save()         ctx.translate(100, 100)         ctx.strokeStyle = "#f60"         ctx.beginPath()         ctx.arc(0, 0, 50, 0, Math.PI*2)         ctx.closePath()         ctx.stroke()         ctx.restore()                  window.requestAnimationFrame(draw)     } 复制代码

  1. 获取鼠标在canvas的坐标

    let mouseX = 0, mouseY = 0     canvas.addEventListener('mousemove', event => {       // e.clientX 鼠标在浏览器页面的位置       // e.target.getBoundingClientRect().x 画布在浏览器页面的位置       mouseX = canvas.ratio * (event.clientX - event.target.getBoundingClientRect().x)       mouseY = canvas.ratio * (event.clientY - event.target.getBoundingClientRect().y)     }) 复制代码

  1. 判断鼠标坐标是否落在目标物体内

    相关函数ctx.isPointInPath

普通canvas选取物体成功_Trim.gif

    // app.js     let canvas = document.createElement('canvas');     canvas.width = 200; // canvas宽度     canvas.height = 200; // canvas高度     const ctx = canvas.getContext('2d')     document.querySelector('body').appendChild(canvas)          let mouseX = 0, mouseY = 0     canvas.addEventListener('mousemove', event => {       // e.clientX 鼠标在浏览器页面的位置       // e.target.getBoundingClientRect().x 画布在浏览器页面的位置       mouseX = event.clientX - event.target.getBoundingClientRect().x       mouseY = event.clientY - event.target.getBoundingClientRect().y     })          window.requestAnimationFrame(draw)     function draw() {         ctx.clearRect(0, 0, 200, 200)              ctx.save()         ctx.translate(100, 100)         ctx.strokeStyle = "#f60"         ctx.beginPath()         ctx.arc(0, 0, 50, 0, Math.PI*2)         ctx.closePath()         ctx.stroke()         ctx.restore()            const inCircle = ctx.isPointInPath(mouseX, mouseY)       console.log(inCircle, mouseX, mouseY);            if(inCircle) {         ctx.beginPath()         ctx.arc(100, 100, 50, 0, Math.PI*2)         ctx.fillStyle = "#f80"         ctx.fill()         ctx.closePath()       }                  window.requestAnimationFrame(draw)     } 复制代码

在高清画布上选取失败
  1. 可是是这个画布在高清屏上是模糊的, 所以我将对canvas进行缩放

image-20211127154641893.png

    // app.js     function createCanvas(w = 200, h = 200) {       let ratio = window.devicePixelRatio ?? 1;       let canvas = document.createElement('canvas');       canvas.style.width = `${w}px`; // 画板显示宽度       canvas.style.height = `${h}px`; // 画板显示高度       canvas.width = w * ratio; // 实际绘图宽度       canvas.height = h * ratio; // 实际绘图高度       // Q: 对画布进行线性变换真的有必要吗? 就按照300px画不行吗?       // A: 有的, 因为别人的设备ratio可能2, 也就是说生成画布宽度会是400px,       //    那么你画一个200px的进度条, 在300px画布上是66%, 在400px画布上就是50%       canvas.getContext('2d').setTransform(ratio, 0, 0, ratio, 0, 0);       canvas.w = w       canvas.h = h       canvas.ratio = ratio       return canvas;     }          let canvas = createCanvas()     // let canvas = document.createElement('canvas');     // canvas.width = 200; // canvas宽度     // canvas.height = 200; // canvas高度     const ctx = canvas.getContext('2d')     document.querySelector('body').appendChild(canvas) 复制代码

  1. 可这又导致了新问题就是: 物体选取失败了

高清canvas选取物体失败.gif

  1. 解决方案是, 对鼠标在canvas的坐标也进行缩放

高清canvas选取物体成功_Trim.gif

以下为最终代码 复制代码

    // app.js     function createCanvas(w = 200, h = 200) {       let ratio = window.devicePixelRatio ?? 1;       let canvas = document.createElement('canvas');       canvas.style.width = `${w}px`; // 画板显示宽度       canvas.style.height = `${h}px`; // 画板显示高度       canvas.width = w * ratio; // 实际绘图宽度       canvas.height = h * ratio; // 实际绘图高度       // Q: 对画布进行线性变换真的有必要吗? 就按照300px画不行吗?       // A: 有的, 因为别人的设备ratio可能2, 也就是说生成画布宽度会是400px,       //    那么你画一个200px的进度条, 在300px画布上是66%, 在400px画布上就是50%       canvas.getContext('2d').setTransform(ratio, 0, 0, ratio, 0, 0);       canvas.w = w       canvas.h = h       canvas.ratio = ratio       return canvas;     }          let canvas = createCanvas()     // let canvas = document.createElement('canvas');     // canvas.width = 200; // canvas宽度     // canvas.height = 200; // canvas高度     const ctx = canvas.getContext('2d')     document.querySelector('body').appendChild(canvas)          let mouseX = 0, mouseY = 0     canvas.addEventListener('mousemove', event => {       // e.clientX 鼠标在浏览器页面的位置       // e.target.getBoundingClientRect().x 画布在浏览器页面的位置       mouseX = canvas.ratio * (event.clientX - event.target.getBoundingClientRect().x)       mouseY = canvas.ratio * (event.clientY - event.target.getBoundingClientRect().y)     })          window.requestAnimationFrame(draw)     function draw() {         ctx.clearRect(0, 0, 200, 200)              ctx.save()         ctx.translate(100, 100)         ctx.strokeStyle = "#f60"         ctx.beginPath()         ctx.arc(0, 0, 50, 0, Math.PI*2)         ctx.closePath()         ctx.stroke()         ctx.restore()            const inCircle = ctx.isPointInPath(mouseX, mouseY)       console.log(inCircle, mouseX, mouseY);            if(inCircle) {         ctx.beginPath()         // ctx.arc(0, 0, 50, 0, Math.PI*2)         // ctx.arc(canvas.w/2, canvas.h/2, 50, 0, Math.PI*2)         ctx.arc(100, 100, 50, 0, Math.PI*2)         ctx.fillStyle = "#f80"         ctx.fill()         ctx.closePath()       }                  window.requestAnimationFrame(draw)     } 复制代码

移动物体

IMG_0637.gif

代码写得有点灾难

// app.js function createCanvas(w = 200, h = 200) {   let ratio = window.devicePixelRatio ?? 1;   let canvas = document.createElement('canvas');   canvas.style.width = `${w}px`; // 画板显示宽度   canvas.style.height = `${h}px`; // 画板显示高度   canvas.width = w * ratio; // 实际绘图宽度   canvas.height = h * ratio; // 实际绘图高度   // Q: 对画布进行线性变换真的有必要吗? 就按照300px画不行吗?   // A: 有的, 因为别人的设备ratio可能2, 也就是说生成画布宽度会是400px,   //    那么你画一个200px的进度条, 在300px画布上是66%, 在400px画布上就是50%   canvas.getContext('2d').setTransform(ratio, 0, 0, ratio, 0, 0);   canvas.w = w   canvas.h = h   canvas.ratio = ratio   return canvas; } let canvas = createCanvas(300, 200) const ctx = canvas.getContext('2d') document.querySelector('body').appendChild(canvas) let mouse = {   x:0,   y:0,   scaledX: function(){     return Math.floor(canvas.ratio * this.x)   },   scaledY: function(){     return Math.floor(canvas.ratio * this.y)   }, } let items = [] let redCircle = new Circle(ctx, { px: 100, py: 100 }) let blueCircle = new Circle(ctx, { px: 220, py: 100, strokeColor: "#28d", fillColor: "#28d", radius: 30 }) items = [redCircle, blueCircle] function draw() {   ctx.clearRect(0, 0, canvas.w, canvas.h)   redCircle.draw()   blueCircle.draw()   window.requestAnimationFrame(draw) } window.requestAnimationFrame(draw) canvas.addEventListener('mousemove', event => {   // e.clientX 鼠标在浏览器页面的位置   // e.target.getBoundingClientRect().x 画布在浏览器页面的位置   mouse.x = event.clientX - event.target.getBoundingClientRect().x   mouse.y = event.clientY - event.target.getBoundingClientRect().y }) canvas.addEventListener('click', event => {   const x = mouse.scaledX()   const y = mouse.scaledY()   items.forEach(item => item.ckeckClicked(x, y)) }) canvas.addEventListener('wheel', event => {   const step = event.wheelDelta > 0 ? 0.1 : -0.1   items.forEach(item => {     if(item.isSelected) {      item.theta += step     }   }) }) function Circle(ctx, config = {}) {   const { px, py, strokeColor, fillColor, radius } = config   this.pos = {     x: px ?? 0,     y: py ?? 0   }   this.radius = radius ?? 50   this.theta = 0   this.color = {     stroke: strokeColor ?? "#f60",     fill: fillColor ?? "#f80"   }   this.distanceToMouse = { x: 0, y: 0 }   this.isSelected = false   this.mouseHover = false   this.needCkeckClicked = false   this.mouseClickX = 0   this.mouseClickY = 0   this.ckeckClicked = function(x, y){     this.needCkeckClicked = true     this.mouseClickX = x     this.mouseClickY = y     this.draw()   }      this.containPoint = function (x, y) {   }      this.moveTo = function (x,y) {     this.pos.x = x     this.pos.y = y   }   this.draw = function () {     ctx.save()     ctx.strokeStyle = this.color.stroke     ctx.fillStyle = this.color.fill     ctx.translate(this.pos.x, this.pos.y)     ctx.rotate(this.theta)     // ctx.rotate(0)     ctx.beginPath()     // ctx.arc(0, 0, this.radius, 0, Math.PI * 2)     // ctx.fillRect(0, 0, this.radius, this.radius)     ctx.moveTo(-this.radius, this.radius);     ctx.lineTo(this.radius, this.radius);     ctx.lineTo(this.radius, -this.radius);     ctx.lineTo(-this.radius, -this.radius);     ctx.lineTo(-this.radius, this.radius);     ctx.fill()     ctx.stroke()     if(this.needCkeckClicked) {       this.needCkeckClicked = false       if(this.isSelected){           this.isSelected = false           this.moveTo(this.pos.x, this.pos.y)       } else {         if(ctx.isPointInPath(this.mouseClickX, this.mouseClickY)) {           this.distanceToMouse.x = this.pos.x - mouse.x           this.distanceToMouse.y = this.pos.y - mouse.y           this.isSelected = true         }       }     }     if (ctx.isPointInPath(mouse.scaledX(), mouse.scaledY())) {       this.mouseHover = true       ctx.globalAlpha = 0.1       ctx.fillStyle = "#fff"       ctx.fill()     }     if(this.isSelected){       ctx.strokeStyle = "#555"       ctx.globalAlpha = 0.3       ctx.lineWidth = 2       ctx.stroke()       this.moveTo(mouse.x + this.distanceToMouse.x, mouse.y + this.distanceToMouse.y)     }     ctx.closePath()     ctx.restore()   } }

 伪原创工具 SEO网站优化  https://www.237it.com/ 

作者:耿直网友郝猪皮
链接:https://juejin.cn/post/7035155955817906190


文章分类
代码人生
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐