vue 手势解锁功能

效果

实现

<script setup lang="ts">
const canvasRef = ref<HTMLCanvasElement>()
const ctx = ref<CanvasRenderingContext2D | null>(null)
const width = px2px(600)
const height = px2px(700)
const radius = ref(px2px(50))

const init = () => {
  const canvas = canvasRef.value
  if (!canvas) return
  canvas.width = width
  canvas.height = height
  ctx.value = canvas.getContext('2d')
  render()
  
}
onMounted(init)

// 圆
type CircleType = { x: number; y: number; n: number }
const circlePointList = ref<CircleType[]>([])
const circleChooseList = ref<CircleType[]>([])
const circleSolidWidth = px2px(5)
const drawCircle = (x: number, y: number, r = radius.value) => {
  // 画圆
  const c = ctx.value
  if (!c) return
  c.strokeStyle = '#CFE6FF'
  c.lineWidth = circleSolidWidth
  c.beginPath()
  c.arc(x, y, r, 0, 2 * Math.PI, true)
  c.closePath()
  c.stroke()
}
const renderCircleList = () => {
  const c = ctx.value
  if (!c) return
  c.clearRect(0, 0, width, height)
  const line_num = 3
  const row_num = 3
  const r = radius.value
  const rTotalLen = r * 2 * line_num
  // 算x的偏移量
  const paddingX = px2px(50)
  const w = width - paddingX * 2
  const marginX = (w - rTotalLen) / (line_num - 1)
  const offsetX = (w - marginX * (line_num - 1) - rTotalLen) / 2

  // 算y的偏移量
  const paddingY = px2px(50)
  const h = height - paddingY * 2
  const marginY = (h - r * 2 * row_num) / (row_num - 1)
  const offsetY = (h - marginY * (row_num - 1) - r * 2 * row_num) / 2

  // 循环画
  for (let i = 0; i < line_num; i++) {
    for (let j = 0; j < row_num; j++) {
      const x = r + j * 2 * r + marginX * j + offsetX + paddingX
      const y = r + i * 2 * r + marginY * i + offsetY + paddingY
      drawCircle(x, y)
      circlePointList.value.push({ x, y, n: circlePointList.value.length + 1 })
    }
  }
}
const drawChooseCircle = (x: number, y: number, r = radius.value, r2 = px2px(8)) => {
  const c = ctx.value
  if (!c) return
  c.strokeStyle = '#CFE6FF'
  c.lineWidth = circleSolidWidth
  c.beginPath()
  c.arc(x, y, r, 0, 2 * Math.PI, true)
  c.closePath()
  c.stroke()

  c.beginPath()
  c.arc(x, y, r2, 0, 2 * Math.PI, false)
  c.closePath()
  c.fillStyle = '#CFE6FF'
  c.fill()
  c.stroke()
}
const renderChooseCircle = () => {
  const list = circleChooseList.value
  for (let i = 0; i < list.length; i++) {
    const { x, y } = list[i]
    drawChooseCircle(x, y)
  }
}
const getIsChooseCircleByPoint = (x: number, y: number): { active: boolean; circle: CircleType | null } => {
  const list = circlePointList.value
  for (let i = 0; i < list.length; i++) {
    const { x: x1, y: y1 } = list[i]
    const r = radius.value
    const leftIs = x > x1 - r - circleSolidWidth
    const rightIs = x < x1 + r + circleSolidWidth
    const topIs = y > y1 - r - circleSolidWidth
    const bottomIs = y < y1 + r + circleSolidWidth
    if (leftIs && rightIs && topIs && bottomIs) return { active: true, circle: list[i] }
  }
  return { active: false, circle: null }
}
const addCircleChoose = (c: CircleType) => {
  const list = circleChooseList.value
  const o = list.find((item) => item.n === c.n)
  if (o) return
  list.push(c)
}

// 线
const drawLine = (x1: number, y1: number, x2: number, y2: number) => {
  const c = ctx.value
  if (!c) return
  c.beginPath()
  c.strokeStyle = '#CFE6FF'
  c.lineWidth = px2px(3)
  c.lineCap = 'round'
  c.moveTo(x1, y1)
  c.lineTo(x2, y2)
  c.stroke()
  c.closePath()
}
const renderChooseLine = () => {
  const list = circleChooseList.value
  if (list.length < 2) return
  for (let i = 1; i < list.length; i++) {
    drawLine(list[i - 1].x, list[i - 1].y, list[i].x, list[i].y)
  }
}

// 渲染
const render = () => {
  renderCircleList()
  renderChooseCircle()
  renderChooseLine()
}
const reset = () => {
  renderCircleList()
  circleChooseList.value = []
  pointList.value = []
}

// 事件
const pointList = ref<{ x: number; y: number }[]>([])
const getPoint = (touch: Touch) => {
  const canvas = canvasRef.value
  // 这种方式tranform时,获取的坐标是错误的
  // const offsetLeft = canvas?.offsetLeft || 0
  // const offsetTop = canvas?.offsetTop || 0
  if(!canvas) return { x: 0, y: 0 }
  const rect = canvas.getBoundingClientRect()
  const offsetLeft = rect.x
  const offsetTop = rect.y
  return { x: touch.clientX - offsetLeft, y: touch.clientY - offsetTop }
}
const touchstart = (e: TouchEvent) => {
  const touch = e.touches[0]
  const p = getPoint(touch)
  pointList.value.push(p)
  const o = getIsChooseCircleByPoint(p.x, p.y)
  if (o.active && o.circle) addCircleChoose(o.circle)
}
const touchmove = (e: TouchEvent) => {
  const touch = e.touches[0]
  const p = getPoint(touch)
  pointList.value.push(p)

  const o = getIsChooseCircleByPoint(p.x, p.y)
  if (o.active && o.circle) addCircleChoose(o.circle)
  

  render()
  const p0 = circleChooseList.value[circleChooseList.value.length - 1]
  if (!p0) return
  drawLine(p0.x, p0.y, p.x, p.y)
}
const touchend = () => {
  reset()
}
</script>

<template>
  <div class="flex flex-center flex-column">
    <BaseHead title="test"></BaseHead>
    <h1>vue手势解锁功能</h1>
    <canvas class="canvas" ref="canvasRef" @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend"></canvas>
  </div>
</template>

<style lang="scss" scoped>
.page{
  width: 100vw;
  height: 100vh;
  box-sizing: border-box;
  overflow: hidden;
}
.canvas {
  position: fixed;
  top: 400px;
  left: 50%;
  transform: translateX(-50%);
  background-color: #ccc;
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/408631.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【AI链接】 大模型语言模型网站链接

目录 GPT类1. chatgpt2. GROP3. Google AI Studio4. Moonshot AI (国内) 解读论文类&#xff1a;1. txyz 编程辅助插件&#xff1a;1. Fitten Code GPT类 1. chatgpt https://chat.openai.com/ 2. GROP https://groq.com/ 3. Google AI Studio https://aistudio.google…

计算机网络:思科实验【3-集线器与交换机的区别、交换机的自学习算法】

&#x1f308;个人主页&#xff1a;godspeed_lucip &#x1f525; 系列专栏&#xff1a;Cisco Packet Tracer实验 本文对应的实验报告源文件请关注微信公众号程序员刘同学&#xff0c;回复思科获取下载链接。 实验目的实验环境实验内容集线器与交换机的区别交换机的自学习算法…

南京观海微电子----Verilog基础(一)——数据类型、运算符

1. 数据类型 1.1 常量 整数&#xff1a;整数可以用二进制b或B&#xff0c;八进制o或O&#xff0c;十进制d或D&#xff0c;十六进制h或H表示&#xff0c;例如&#xff0c;8’b00001111表示8位位宽的二进制整数&#xff0c;4’ha表示4位位宽的十六进制整数。 X和Z&#xff1a;X…

Github 2024-02-21 开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2024-02-21统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目8非开发语言项目1TypeScript项目1 gpt4free 语言模型集合改进计划 创建周期&#xff1a;300 天开…

Excel的中高级用法

单元格格式&#xff0c;根据数值的正负分配不同的颜色和↑ ↓ 根据数值正负分配颜色 2-7 [蓝色]#,##0;[红色]-#,##0 分配颜色的基础上&#xff0c;根据正负加↑和↓ 2↑-7↓ 其实就是在上面颜色的代码基础上加个 向上的符号↑&#xff0c;或向下的符号↓ [蓝色]#,##0↑;[红色…

vivo 基于 StarRocks 构建实时大数据分析平台,为业务搭建数据桥梁

在大数据时代&#xff0c;数据分析和处理能力对于企业的决策和发展至关重要。 vivo 作为一家全球移动互联网智能终端公司&#xff0c;需要基于移动终端的制造、物流、销售等各个方面的数据进行分析以满足业务决策。 而随着公司数字化服务的演进&#xff0c;业务诉求和技术架构有…

动态规划课堂1-----斐波那契数列模型

目录 动态规划的概念&#xff1a; 动态规划的解法流程&#xff1a; 题目: 第 N 个泰波那契数 解法&#xff08;动态规划&#xff09; 代码&#xff1a; 优化&#xff1a; 题目&#xff1a;最小花费爬楼梯 解法&#xff08;动态规划&#xff09; 解法1&#xff1a; 解…

【QT 5 +Linux下软件生成+qt软件生成使用工具+学习他人文章+第一篇:使用linuxdeployqt软件生成】

【QT 5 Linux下软件生成qt软件生成使用工具学习他人文章第一篇&#xff1a;使用linuxdeployqt软件生成】 1、前言2、实验环境3、自我学习总结-本篇总结1、新手的疑问&#xff0c;做这件事的目的2、了解工具&#xff1a;linuxdeployqt工具3、解决相关使用过程中问题 4、参照文章…

5分钟轻松帮你EasyRecovery恢复女朋友照片

相信有不少男性电脑玩家都会将女朋友的照片存放在电脑硬盘之内&#xff0c;作为珍贵的收藏和回忆。但是在某些时候&#xff0c;如果我们错误地删除了这些照片&#xff0c;或者由于系统问题导致其中的照片丢失&#xff0c;那么我们怎么找回女朋友的照片&#xff1f;这个问题就足…

进程的学习

进程基本概念: 1.进程: 程序&#xff1a;存放在外存中的一段数据组成的文件 进程&#xff1a;是一个程序动态执行的过程,包括进程的创建、进程的调度、进程的消亡 2.进程相关命令: 1.top 动态查看当前系统中的所有进程信息&#xff08;根据CPU占用率排序&#xf…

微芒计划-简洁方便的效率待办管理工具【免费】

&#x1f632;微芒计划-简洁方便的效率待办管理工具【免费】 下载地址 &#x1f4dd;我的待办 快速添加待办任务&#xff0c;快速查看任务进度&#xff0c;摘要等。新增标签&#xff0c;分类&#xff0c;更好管理待办任务。 ☀️OKR目标管理 OKR让抽象的企业战略明确为上下对…

✅技术社区项目—Session/Cookie身份验证识别

session实现原理 SpringBoot提供了一套非常简单的session机制&#xff0c;那么它又是怎么工作的呢? 特别是它是怎么识别用户身份的呢? session又是存在什么地方的呢? 核心工作原理 借助cookie中的 JESSIONID 来作为用户身份标识&#xff0c;这个数据相同的&#xff0c;认…

车载电子电器架构 —— OEM基础技术概念开发流程

车载电子电器架构 —— 基础技术概念开发 我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 屏蔽力是信息过载时代一个人的特殊竞争力&#xff0c;任何消耗…

SpringMVC 学习(二)之第一个 SpringMVC 案例

目录 1 通过 Maven 创建一个 JavaWeb 工程 2 配置 web.xml 文件 3 创建 SpringMVC 配置文件 spring-mvc.xml 4 创建控制器 HelloController 5 创建视图 index.jsp 和 success.jsp 6 运行过程 7 参考文档 1 通过 Maven 创建一个 JavaWeb 工程 可以参考以下博文&#x…

吴恩达deeplearning.ai:Tensorflow训练一个神经网络

以下内容有任何不理解可以翻看我之前的博客哦&#xff1a;吴恩达deeplearning.ai 在之前的博客中。我们陆续学习了各个方面的有关深度学习的内容&#xff0c;今天可以从头开始训练一个神经网络了。 Tensorflow训练神经网络模型 我们使用之前用过的例子&#xff1a; 这个神经…

Python中的functools模块详解

大家好&#xff0c;我是海鸽。 函数被定义为一段代码&#xff0c;它接受参数&#xff0c;充当输入&#xff0c;执行涉及这些输入的一些处理&#xff0c;并根据处理返回一个值&#xff08;输出&#xff09;。当一个函数将另一个函数作为输入或返回另一个函数作为输出时&#xf…

JAVA算法和数据结构

一、Arrays类 1.1 Arrays基本使用 我们先认识一下Arrays是干什么用的&#xff0c;Arrays是操作数组的工具类&#xff0c;它可以很方便的对数组中的元素进行遍历、拷贝、排序等操作。 下面我们用代码来演示一下&#xff1a;遍历、拷贝、排序等操作。需要用到的方法如下 public…

26.HarmonyOS App(JAVA)列表对话框

列表对话框的单选模式&#xff1a; //单选模式 // listDialog.setSingleSelectItems(new String[]{"第1个选项","第2个选项"},1);//单选 // listDialog.setOnSingleSelectListener(new IDialog.ClickedListener() { // Override …

互联网加竞赛 机器视觉opencv答题卡识别系统

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 答题卡识别系统 - opencv python 图像识别 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f947;学长这里给一个题目综合评分(每项满分5分…

C++中的左值和右值

目录 一. 左值和右值的概念 1. 左值 1.1 可修改的的左值 1.2 不可修改的左值 右值 二. 左值引用和右值引用 1. 左值引用 2. 右值引用 主要用途 1. 移动语义 2. 完美转发 2.1 引用折叠 2.2 std::forward 一. 左值和右值的概念 什么是左值和右值 1. 左值 左值是一个表示…