vue 手势解锁功能

news2024/11/19 1:18:43

效果

实现

<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>

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java面试问题集锦

1.JDK、JRE、JVM 三者有什么关系&#xff1f; JDK&#xff08;全称 Java Development Kit&#xff09;&#xff0c;Java开发工具包&#xff0c;能独立创建、编译、运行程序。 JDK JRE java开发工具&#xff08;javac.exe/java.exe/jar.exe) JRE&#xff08;全称 Java Runtim…

MyBatis之Mapper.xml文件中parameterType,resultType,resultMap的用法

MyBatis之自定义数据类型转换器 前言1.parameterType2.resultType3.resultMap实例代码总结 前言 今天我们来学习Mapper.xml&#xff08;编写SQL的&#xff09;文件中&#xff0c;增删改查标签中&#xff0c;使用parameterType属性指定传递参数类型&#xff0c;resultType属性指…

C# OpenCvSharp 利用白平衡技术进行图像修复

目录 效果 灰度世界(GrayworldWB)-白平衡算法 完美反射(SimpleWB)-白平衡算法 基于学习的(LearningBasedWB)-白平衡算法 代码 下载 C# OpenCvSharp 利用白平衡技术进行图像修复 OpenCV xphoto模块中提供了三种不同的白平衡算法&#xff0c;分别是&#xff1a;灰度世界(G…

Linux进一步研究权限-----------ACL使用

一、使用情况 1.1、场景: 某个大公司&#xff0c;在一个部门&#xff0c;有一个经理和手下有两个员工&#xff0c;在操控一个Linux项目,项目又分为三期做&#xff0c;然而一期比较重要&#xff0c;经理带着员工做完了&#xff0c;公司就觉得技术难点已经做完攻克了&#xff0…

npm install报错解决记录

npm install报错解决记录 常见错误类型 权限错误: EACCES: permission denied EPERM: operation not permitted网络错误: ECONNREFUSED: Connection refused ETIMEDOUT: connect ETIMEDOUT包解析错误: Cannot find module ‘xxx’ Error: No compatible version found编译错误…

飞行机器人专栏(十三)-- 智能优化算法之粒子群优化算法与多目标优化

一、理论基础 1.1 引言 粒子群优化算法&#xff08;Particle Swarm Optimization, PSO&#xff09;自1995年由Eberhart和Kennedy提出以来&#xff0c;已经成为解决优化问题的一种有效且广泛应用的方法。作为一种进化计算技术&#xff0c;PSO受到社会行为模式&#xff0c;特别是…

互联设备-中继器-路由器等

网卡的主要作用 1 在发送方 把从计算机系统要发送的数据转换成能在网线上传输的bit 流 。 2 在接收方 把从网线上接收来的 bit 流重组成计算机系统可以 处理的数据 。 3 判断数据是否是发给自己的 4 发送和控制计算机系统和网线数据流 计算机的分类 1、台式机 2、小型机和服…

【DDD】学习笔记-薪资管理系统的测试驱动开发

回顾薪资管理系统的设计建模 在 3-15 课&#xff0c;我们通过场景驱动设计完成了薪资管理系统的领域设计建模。既然场景驱动设计可以很好地与测试驱动开发融合在一起&#xff0c;因此根据场景驱动设计的成果来开展测试驱动开发&#xff0c;就是一个水到渠成的过程。让我们先来…

rem适配方案

目录 一&#xff0c;rem实际开发适配方案 二&#xff0c;rem适配方案技术使用&#xff08;市场主流&#xff09; 方案一&#xff1a; 方案二&#xff1a;​编辑 一&#xff0c;rem实际开发适配方案 ① 按照设计稿与设备宽度的比例&#xff0c;动态计算并设置html根标签的fo…

【自然语言处理-二-attention注意力 是什么】

自然语言处理二-attention 注意力机制 自然语言处理二-attention 注意力记忆能力回顾下RNN&#xff08;也包括LSTM GRU&#xff09;解决memory问题改进后基于attention注意力的modelmatch操作softmax操作softmax值与hidder layer的值做weight sum 计算和将计算出来的和作为memo…

Jetpack Compose 架构层

点击查看&#xff1a;Jetpack Compose 架构层 官网 本页面简要介绍了组成 Jetpack Compose 的架构层&#xff0c;以及这种设计所依据的核心原则。 Jetpack Compose 不是一个单体式项目&#xff1b;它由一些模块构建而成&#xff0c;这些模块组合在一起&#xff0c;构成了一个完…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的人脸表情识别系统(附完整资源+PySide6界面+训练代码)

摘要&#xff1a;本篇博客呈现了一种基于深度学习的人脸表情识别系统&#xff0c;并详细展示了其实现代码。系统采纳了领先的YOLOv8算法&#xff0c;并与YOLOv7、YOLOv6、YOLOv5等早期版本进行了比较&#xff0c;展示了其在图像、视频、实时视频流及批量文件中识别人脸表情的高…

【elementUi-table表格】 滚动条 新增监听事件; 滚动条滑动到指定位置;

1、给滚动条增加监听 this.dom this.$refs.tableRef.bodyWrapperthis.dom.scrollTop 0let _that thisthis.dom.addEventListener(scroll, () > {//获取元素的滚动距离let scrollTop _that.dom.scrollTop//获取元素可视区域的高度let clientHeight this.dom.clientHeigh…

springboot+vue项目基础开发(17)路由使用

路由 在前端中,路由指的是根据不同的访问路径,展示不同的内容 vue Router的vue.js的官方路由 安装vue Router 再启动 在src下面新建router文件,创建index.js 代码 import {createRouter,createWebHashHistory} from vue-router //导入组件 import Login from @/views/Log…

SparkSQL学习03-数据读取与存储

文章目录 1 数据的加载1.1 方式一&#xff1a;spark.read.format1.1.1读取json数据1.1.2 读取jdbc数据 1.2 方式二&#xff1a;spark.read.xxx1.2.1 读取json数据1.2.2 读取csv数据1.2.3 读取txt数据1.2.4 读取parquet数据1.2.5 读取orc数据1.2.6 读取jdbc数据 2 数据的保存2.1…

RT-Thread-快速入门-2-时钟与定时器

时钟与定时器 阅读须知 定义与作用 定义 系统时钟 系统时钟在RT-Thread中用于管理时间&#xff0c;为系统运行提供时间基准。系统时钟由硬件计时器&#xff08;通常是CPU的内部定时器或外部定时器&#xff09;提供时钟节拍&#xff0c;这些时钟节拍通常以固定频率中断CPU&#…

opengl 学习纹理

一.纹理是什么&#xff1f; 纹理是一个2D图片&#xff08;甚至也有1D和3D的纹理&#xff09;&#xff0c;它可以用来添加物体的细节&#xff1b;类似于图像一样&#xff0c;纹理也可以被用来储存大量的数据&#xff0c;这些数据可以发送到着色器上。 采样是指用纹理坐标来获取纹…

npm install 失败,需要node 切换到 对应版本号

npm install 失败 原本node 的版本号是16.9&#xff0c;就会报以上错误 node版本问题了&#xff0c;我切到这个版本&#xff0c;报同样的错。降一下node&#xff08;14.18&#xff09;版本就好了 具体的方法&#xff1a;&#xff08;需要在项目根目录下切换&#xff09; 1. …

微服务学习

一、服务注册发现 服务注册就是维护一个登记簿&#xff0c;它管理系统内所有的服务地址。当新的服务启动后&#xff0c;它会向登记簿交待自己的地址信息。服务的依赖方直接向登记簿要Service Provider地址就行了。当下用于服务注册的工具非常多ZooKeeper&#xff0c;Consul&am…

JavaScript从零写网站《一瞬》开发日志20240223

产品介绍 一个无需注册能随时发布图片并配一段文字介绍的app&#xff0c;有时间线&#xff0c;用户在主页面向下滑动&#xff0c;可以看到被发布的若干图片&#xff0c;并且能够在每一个发布处做基本互动——评论&#xff0c;点赞 编程语言 本产品使用htmlcssJavaScript开发…