vue + canvas 实现九宮格手势解锁器

这篇具有很好参考价值的文章主要介绍了vue + canvas 实现九宮格手势解锁器。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

专栏分享:vue2源码专栏,vue router源码专栏,玩具项目专栏,硬核💪推荐🙌
欢迎各位 ITer 关注点赞收藏🌸🌸🌸

此篇文章用于记录柏成从零开发一个canvas九宮格手势解锁器的历程,最终效果如下:

vue + canvas 实现九宮格手势解锁器

  1. 设置图案密码时,需进行两次绘制图案操作,若两次绘制图案一致,则密码设置成功;若不一致,则需重新设置密码

  2. 输入图案密码时,密码一致则验证通过;密码不一致则提示图案密码错误,请重试

介绍

我们基于 canvas 实现了一款简单的九宫格手势解锁器,用户可以通过在九宫格中绘制特定的手势来解锁

我们可以通过 new Locker 创建一个图案解锁器,其接收一个容器作为第一个参数,第二个参数为选项,下面是个基本例子:

<template>
  <div class="pattren-locker">
    <div id="container" ref="container" style="width: 360px; height: 600px"></div>
  </div>
</template>

<script setup>
  import { ref, onMounted } from 'vue'
  import Locker from '@/canvas/locker'

  const container = ref(null)
  onMounted(() => {
    // 新建一个解锁器
    new Locker(container.value,{
      radius: 30, // 圆圈半径
      columnSpacing: 50, // 圆圈列间距
      rowsSpacing: 90, // 圆圈行间距
      stroke: '#b5b5b5', // 圆圈描边颜色
      lineStroke: '#237fb4', // 路径描边颜色
      selectedFill: '#237fb4', // 图案选中填充颜色
      backgroundColor: '#f7f7f7', // 画布背景颜色
    })
  })
</script>

初始化

Locker 的实现是一个类,在 src/canvas/locker.js中定义。

new Locker(container,{...})时做了什么?我们在构造函数中创建一个 canvas 画布追加到了 container 容器中,并定义了一系列属性,最后执行了 init 初始化方法。

在初始化方法中,我们绘制了9个宫格圆圈,作为解锁单元;并注册监听了鼠标事件,用于绘制解锁轨迹。

// 初始化
init() {
  this.drawCellGrids()
  this.drawText('请绘制新的图案密码')
  this.canvas.addEventListener('contextmenu', (e) => e.preventDefault())
  this.canvas.addEventListener('mousedown', this.mousedownEvent.bind(this))
}

// 绘制9个宫格圆圈
drawCellGrids() {
  const columns = 3
  const rows = 3
  const width = this.canvas.width
  const height = this.canvas.height
  const paddingTop = (height - rows * 2 * this.radius - (rows - 1) * this.rowsSpacing) / 2
  const paddingLeft = (width - columns * 2 * this.radius - (columns - 1) * this.columnSpacing) / 2
  for (let i = 0; i < rows; i++) {
    for (let j = 0; j < columns; j++) {
      const data = {
        x: paddingLeft + (2 * j + 1) * this.radius + j * this.columnSpacing,
        y: paddingTop + (2 * i + 1) * this.radius + i * this.rowsSpacing,
        id: i * columns + j
      }

      this.lockerCells.push(data)

      this.ctx.beginPath()
      this.ctx.arc(data.x, data.y, this.radius, 0, 2 * Math.PI, true)
      this.ctx.strokeStyle = this.stroke
      this.ctx.lineWidth = 3
      this.ctx.stroke()
    }
  }
  this.cellImageData = this.lastImageData = this.getImageData()
}

自定义鼠标事件

我们之前在 init 初始化方法中注册了 onmousedown 鼠标按下事件,需要在此处实现鼠标按下拖拽可以绘制解锁轨迹的逻辑

鼠标按下:先执行 selectCellAt 方法(如果在圆圈内按下鼠标,会立即绘制选中样式,并保存选中样式之后的画布快照)

鼠标移动:先恢复快照,再绘制路径中最后一个点到当前鼠标坐标的轨迹,最后再执行 selectCellAt 方法,一直重复此过程。。。直到鼠标移动到圆圈内部(则先恢复快照,然后绘制点的选中样式,绘制路径中最后一个点到当前点的路径,最后保存绘制路径之后的画布快照)

鼠标抬起:清空onmousemove、onmouseup事件,并校验密码

此时我们小小的脑袋里可能有两个大大的问号??

  1. selectCellAt 方法作用是什么?
    如果鼠标移动到圆圈内部,则会将图案路径连接到当前圆圈,并绘制选中样式

  2. 快照是什么?
    快照是当前画布的像素点信息。我们永远会在激活一个解锁单元后(即鼠标移动到圆圈内部时),先恢复画布快照,然后去绘制圆圈的选中样式,并将图案路径延伸连接到当前圆圈,然后!会保存此时此刻的画布快照!
    之后,我们会在鼠标移动时,不停的恢复快照,然后绘制最后一个圆圈到当前鼠标坐标的连线轨迹,直到我们激活下一个解锁单元(即鼠标移动到下一个圆圈内部)。我们会又会重复上面的过程,这就构成一个一个的循环

mousedownEvent(e) {
  const that = this
  // 选中宫格,并绘制点到点路径
  const selected = this.selectCellAt(e.offsetX, e.offsetY)
  if (!selected) return

  // 鼠标移动事件
  this.canvas.onmousemove = function (e) {
    // 路径的最后一个点
    const lastData = that.currentPath[that.currentPath.length - 1]
    // 恢复快照
    that.restoreImageData(that.lastImageData)

    // 绘制路径
    that.drawLine(lastData, { x: e.offsetX, y: e.offsetY })
    // 选中宫格,并绘制点到点路径
    that.selectCellAt(e.offsetX, e.offsetY)
  }

  // 鼠标抬起/移出事件
  this.canvas.onmouseup = this.canvas.onmouseout = function () {
    const canvas = this
    canvas.onmousemove = null
    canvas.onmouseup = null
    canvas.onmouseout = null

    const currentPathIds = that.currentPath.map((item) => item.id)
    let text = ''
    if (that.password.length === 0) {
      that.password = currentPathIds
      text = '请再次绘制图案进行确认'
    } else if (that.confirmPassword.length === 0) {
      that.confirmPassword = currentPathIds
      if (that.password.join('') === that.confirmPassword.join('')) {
        text = '图案密码设置成功,请输入您的密码'
      } else {
        text = '与上次绘制不一致,请重试'
        that.password = []
        that.confirmPassword = []
      }
    } else {
      if (that.password.join('') === currentPathIds.join('')) {
        text = '图案密码正确 (づ ̄3 ̄)づ╭❤~'
      } else {
        text = '图案密码错误,请重试'
      }
    }

    that.ctx.clearRect(0, 0, canvas.width, canvas.height) // 清空画布
    that.restoreImageData(that.cellImageData) // 恢复背景宫格快照
    that.drawText(text) // 绘制提示文字
    that.currentPath = [] // 清空当前绘制路径
    that.lastImageData = that.cellImageData // 重置上一次绘制的画布快照
  }
}

绘制路径及选中样式

我们会在鼠标按下(onmousedown)、鼠标移动(onmousemove)事件中调用 selectCellAt 方法,并传入当前鼠标坐标信息

  1. 若当前坐标在宫格圆圈内 且 改圆圈未被连接过,则先恢复画布快照,然后绘制圆圈选中样式,绘制路径中最后一个圆圈到当前圆圈的路径,最后保存此时此刻的画布快照,返回true

  2. 若当前坐标不在宫格圆圈内 或者 该圆圈被连接过,则返回false

selectCellAt(x, y) {
  // 当前坐标点是否在圆内
  const data = this.lockerCells.find((item) => {
    return Math.pow(item.x - x, 2) + Math.pow(item.y - y, 2) <= Math.pow(this.radius, 2)
  })
  const existing = this.currentPath.some((item) => item.id === data?.id)
  if (!data || existing) return false

  // 恢复画布快照
  this.restoreImageData(this.lastImageData)

  // 绘制选中样式
  this.drawCircle(data.x, data.y, this.radius / 1.5, 'rgba(0,0,0,0.2)')
  this.drawCircle(data.x, data.y, this.radius / 2.5, this.selectedFill)

  // 绘制路径 从最后一个点到当前点
  const lastData = this.currentPath[this.currentPath.length - 1]
  if (lastData) {
    this.drawLine(lastData, data)
  }

  // 保存画布快照
  this.lastImageData = this.getImageData()

  // 保存当前点
  this.currentPath.push(data)
  return true
}

// 绘制选中样式
drawCircle(x, y, radius, fill) {
  this.ctx.beginPath()
  this.ctx.arc(x, y, radius, 0, 2 * Math.PI, true)
  this.ctx.fillStyle = fill
  this.ctx.fill()
}

// 绘制路径
drawLine(start, end, stroke = this.lineStroke) {
  this.ctx.beginPath()
  this.ctx.moveTo(start.x, start.y)
  this.ctx.lineTo(end.x, end.y)
  this.ctx.strokeStyle = stroke
  this.ctx.lineWidth = 3
  this.ctx.lineCap = 'round'
  this.ctx.lineJoin = 'round'
  this.ctx.stroke()
}

画布快照

我们如何获取到当前画布快照?又如何根据快照数据恢复画布呢?

查阅 canvas官方API文档 得知,获取快照 API 为 getImageData;通过快照恢复画布的 API 为 putImageData

// 获取画布快照
getImageData() {
  return this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height)
}

// 恢复画布快照
restoreImageData(imageData) {
  if (!imageData) return
  this.ctx.putImageData(imageData, 0, 0)
}

源码

涂鸦面板demo代码:vue-canvas文章来源地址https://www.toymoban.com/news/detail-699617.html

到了这里,关于vue + canvas 实现九宮格手势解锁器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • vue + canvas 实现涂鸦面板

    专栏分享:vue2源码专栏,vue router源码专栏,玩具项目专栏,硬核 💪 推荐 🙌 欢迎各位 ITer 关注点赞收藏 🌸🌸🌸 此篇文章用于记录柏成从零开发一个 canvas涂鸦面板 的历程,最终效果如下: 我们基于 canvas 实现了一款简单的涂鸦面板,用于在网页上进行绘图和创作。其支

    2024年02月14日
    浏览(42)
  • 奇舞周刊第497期:解锁 PDF 文件:使用 JavaScript 和 Canvas 渲染 PDF 内容

    记得点击文章末尾的“ 阅读原文 ”查看哟~ 下面先一起看下本期周刊 摘要 吧~  解锁 PDF 文件:使用 JavaScript 和 Canvas 渲染 PDF 内容 最近研究了 Web 的 FileSystemAccess Api,它弥补了 Web 长期以来缺少的能力:操作用户设备中的文件;而如今通过这个 Api 我们能够实现常见的文件

    2024年02月11日
    浏览(48)
  • 基于Vue+Canvas实现的画板绘画以及保存功能,解决保存没有背景问题

    本文内容设计到的画板的js部分内容来源于 灵感来源引用地址,然后我在此基础上,根据自己的需求做了修改,增加了其他功能。 下面展示了完整的前后端代码 这个代码,接收一个容器参数,创建了一个画板类,里面实现了画板会用到的基本方法,保存到单独的js文件,在

    2024年01月20日
    浏览(53)
  • vue中使用html2canvas+jsPDF实现pdf的导出

    html2canvas依赖 jspdf依赖 pdf导出 以导出横向,A4大小的pdf为例 规律:1. html2canvas 中,在保持jsPDF中的宽高不变的情况下,设置html2canvas中的 width 和 height 值越小,导出的pdf越显示不全(会被放大,只能看到局部),反之值越大,导出的pdf越显示完整(值也不能过大,过大在pdf中就显

    2024年02月12日
    浏览(48)
  • vue-element使用html2canvas实现网页指定区域(指定dom元素)截图

    直接上代码: ** 如果要截取的dom元素、区域涉及到v-if或者v-show的条件表达式时,截取的方法请在nextTick里面调用----例如: this.$nextTick(() = { this.saveImageNew() }) 否之获取dom元素的时候会获取不到!!!!!!!!

    2024年02月04日
    浏览(66)
  • 前端vue基于html2canva jspdf 实现前端页面加水印 并导出页面PDF

    随着技术的发展,开发的复杂度也越来越高,传统开发方式将一个系统做成了整块应用,经常出现的情况就是一个小小的改动或者一个小功能的增加可能会引起整体逻辑的修改,造成牵一发而动全身。通过组件化开发,可以有效实现单独开发,单独维护,而且他们之间可以随

    2024年02月03日
    浏览(48)
  • vue使用html2canvas实现一键截图并赋值到剪贴板,只截取当前显示器上可视的内容

    使用 html2canvas 和 clipboard API 实现整页截图并填充至剪切板。 访问剪切板的api只支持在https或者本地localhost上使用,如果是http,则无法使用 首先需要从npm安装html2canvas 然后在代码中导入这个包: 之后绑定一个按钮来实现该功能,比如点击一个按钮,然后就开始截图当前页面并

    2024年02月15日
    浏览(59)
  • vue前端实现将页面显示内容生成pdf文件的几种方法,html2canvas、dom-to-image、jspdf(带分页)基本使用以及介绍

    实际开发需求:vue项目中,根据数据结构生成echarts图表组件,生成带有样式的图表以后,点击下载按钮,把图表以pdf格式的文件下载到本地 实现思路:将vue界面的echarts组件生成图片,然后使用插件将生成的图片放入pdf中,再实现pdf文件的下载 涉及框架以及插件:vue、echar

    2024年01月25日
    浏览(53)
  • vue - vue中使用canvas进行前端图片合并

    如何合成一个二维码和背景图片:其实就是多次调用canvas的 drawImage 方法进行不同坐标的图像;但是因为涉及到微信长按分享图片或保存到本地,所以不能直接使用canvas来放置图片(因为微信中长按识别不出来),只能使用 img 标签来引入canvas合成的base64路径。 Canvas API 提供了

    2024年02月02日
    浏览(42)
  • 用python实现实现手势音量控制

    要实现手势音量控制,您可以使用Python中的PyAutoGUI和pynput库。PyAutoGUI可以模拟鼠标和键盘操作,而pynput可以检测用户的输入事件。 以下是一个简单的示例代码,可以实现通过手势控制音量的功能: python复制代码 import pyautogui import pynput.mouse as mouse import pynput.keyboard as keyboard

    2024年01月19日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包