说在前面
🎈现在大部分的人都喜欢使用手势图案锁来作为手机的锁屏,之前我也写过一篇使用vue封装的手势图案锁组件,想了解的可以戳这里->《vue封装一个图案手势锁组件》。那么怎么在微信小程序中实现这种手势锁屏的效果的呢?接下来让我们一起来实现一个微信小程序中可以使用的手势锁屏组件吧!
组件设计
我们主要从页面展示效果和交互效果来进行具体分析:
1、页面效果
1.1 九宫格圆点排列
使用flex布局和flex-wrap效果,使9个圆点可以均匀排列。
- wxml
<view id="{{JAppsLockId + 'lock'}}" class="j-apps-lock" > <view class="j-apps-lock-cells"> <view class="j-apps-lock-cell" wx:for="{{size * size}}" wx:key="index" data-id="{{index}}" style="border:{{showBorder[index] ? '1px solid gray' : '1px solid transparent'}}" > <view id="{{'point-' + index}}" class="j-apps-lock-point" data-id="{{index}}" ></view> </view> </view> </view>
- wxss
.j-apps-lock { position: relative; margin: auto; } .j-apps-lock-cells { display: flex; flex-wrap: wrap; justify-content: space-around; } .j-apps-lock-cell { display: flex; border-radius: 50%; justify-content: center; width: 26vw; height: 26vw; margin-top: 2vw; } .j-apps-lock-point { width: 40%; height: 40%; justify-content: center; background-color: rgb(187, 183, 183); display: flex; margin: auto; border-radius: 50%; position: relative; }
1.2 划动选中圈圈效果
只需要动态判断当前圈圈是否被选中,动态修改style属性即可: border:{{showBorder[index] ? '1px solid gray' : '1px solid transparent'}}
<view class="j-apps-lock-cell" wx:for="{{size * size}}" wx:key="index" data-id="{{index}}" style="border:{{showBorder[index] ? '1px solid gray' : '1px solid transparent'}}" > ... </view>
2、交互效果
2.1 监听触屏划过圆点
我们可以对整个组件界面进行一个触屏监听,判断坐标是否经过某一个圆点的坐标,如果是,可以将当前圆点标记为选中状态。
- wxml
<view id="j-apps-lock-body" class="j-apps-lock-body" bindtouchstart="mousedown" bindtouchmove="mouseover" bindtouchend="mouseup" > ... </view>
- js
mousedown(e){ this.clearPoint(); if(this.data.points.length == 0) this.getPoints(); }, mouseover(e){ let points = this.data.points; let x = e.touches[0].pageX; let y = e.touches[0].pageY; let id = ''; let showBorder = this.data.showBorder; for(let i = 0; i < points.length; i++){ const p = points[i]; if(x >= p.left && x <= p.right && y <= p.bottom && y >= p.top){ id = p.dataset.id; showBorder[i] = true; break; } } let chooseList = this.data.chooseList; if(isNaN(parseInt(id)) || chooseList.includes(id)) return; chooseList.push(id); this.setData({ chooseList:chooseList, showBorder:showBorder }) },
2.2 动作结束返回动作轨迹
在手指离开手机屏幕,及触屏事件结束触发mouseup事件的时候,将选中的圈圈坐标顺序传递给父组件,这里我对圈圈的坐标定义如下,大家也可以根据实际情况自己定义:
mouseup(){ this.triggerEvent("getPassword", {chooseList:this.data.chooseList}); },
所以上图的图案滑动轨迹返回的点集数组应该为:
[1,2,3,6,9,8,7,4,5]
核心代码详解
1、微信小程序获取dom实例对象
在进行点坐标判断的时候,我们需要获取到每一个点的坐标范围,但是在微信小程序中我们不能直接同document.getElementbyId去获取到我们需要的实例对象,那么我们该怎么获取到需要的对象,并得到其相应的坐标范围呢?
1.1 wx.createSelectorQuery()
我们需要先了解一下wx.createSelectorQuery()这个api
- 功能
返回一个 SelectorQuery 对象实例。在自定义组件或包含自定义组件的页面中,应使用
this.createSelectorQuery()
来代替。
通过查阅官方文档,我们发现可以通过wx.createSelectorQuery()
来获取到页面上的实例对象,这里我们是在小程序组件中使用,所以我们应该使用this.createSelectorQuery()
来获取。
getPoints(){ const query = this.createSelectorQuery(); let id = '.j-apps-lock-point' query.selectAll(id).boundingClientRect((res)=>{ this.setData({ points:res }) }).exec() },
2、判断是否是否划过点位
前面我们获取的点位的dom实例在这里就可以派上用场了,微信小程序返回的实例对象中会包含这几个属性:
- left:实例对象最左端坐标
- right:实例对象最右端坐标
- top:实例对象最顶端坐标
- bottom:实例对象最底端坐标
这里我们有两个方法来判断:
2.1 正方形区域判断
我们可以直接根据实例对象的四个端点坐标来进行一个正方形的大致位置匹配,这样得出来的结果会有少许误差,但也不会有很大的影响:x >= p.left && x <= p.right && y <= p.bottom && y >= p.top
mouseover(e){ //前面获取的圈圈实例对象集合 let points = this.data.points; //当前触屏坐标 let x = e.touches[0].pageX; let y = e.touches[0].pageY; let id = ''; let showBorder = this.data.showBorder; //判断是否划过某一个圈圈 for(let i = 0; i < points.length; i++){ const p = points[i]; if(x >= p.left && x <= p.right && y <= p.bottom && y >= p.top){ id = p.dataset.id; showBorder[i] = true; break; } } //记录圈圈选中状态 let chooseList = this.data.chooseList; if(isNaN(parseInt(id)) || chooseList.includes(id)) return; chooseList.push(id); this.setData({ chooseList:chooseList, showBorder:showBorder }) },
2.2 圆形区域判断
我们也可以根据点实例对象的端点得出其圆的方程,我们只需要将坐标代入方程判断是否位于圆内即可:
(x1-x)^2 + (y1-y)^2 <= r^2
所以我们需要计算出圈圈的圆心坐标和半径即可:
const r = (p.right - p.left) / 2; const x1 = (p.left + p.right) / 2; const y1 = (p.top + p.bottom) / 2;
mouseover(e){ let points = this.data.points; let x = e.touches[0].pageX; let y = e.touches[0].pageY; let id = ''; let showBorder = this.data.showBorder; for(let i = 0; i < points.length; i++){ const p = points[i]; const r = (p.right - p.left) / 2; const x1 = (p.left + p.right) / 2; const y1 = (p.top + p.bottom) / 2; if(Math.pow(x1 - x,2) + Math.pow(y1 - y,2) <= Math.pow(r,2)){ id = p.dataset.id; showBorder[i] = true; break; } } let chooseList = this.data.chooseList; if(isNaN(parseInt(id)) || chooseList.includes(id)) return; chooseList.push(id); this.setData({ chooseList:chooseList, showBorder:showBorder }) },
源码地址
该组件是我在自己的一个开源项目中使用到的,源码可以到该项目查看具体代码,地址如下:
具体位置如下
往期精彩
说在后面
🎉这里是JYeontu,喜欢算法,GDCPC打过卡;热爱羽毛球,大运会打过酱油。毕业一年,两年前端开发经验,目前担任H5前端开发,算法业余爱好者,有空会刷刷算法题,平时喜欢打打羽毛球🏸 ,也喜欢写些东西,既为自己记录📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解🙇,写错的地方望指出,定会认真改进😊,在此谢谢大家的支持,我们下文再见🙌。