1.创建canvas画布
function calcCirclePos(){
let size = 300;
let padding = 300 * 0.08;
// 去除画布padding之外的内容宽高
const contentSize = size - padding * 2;
// 除去圆与圆之间的距离
// 规定每个小圆的直径是总宽度的24%
const circleWidth = contentSize * 0.24;
// 每两个圆圈的圆心之间的距离,横竖都一样
const distance = (contentSize - circleWidth) / 2;
// 左上角第一个圆的圆心坐标,x和y都一样
const firstPoint = mathFf(circleWidth / 2);
// 综上,第一行三个圆的x轴坐标如下
const xy = [
firstPoint,
mathFf(firstPoint + distance),
mathFf(firstPoint + distance * 2)
];
// 由于横竖每个圆之间的间隔都是一样的,
// 所以很容易想到,通过以上三个值遍历就可以得出9个圆的圆心
const points = [];
let i = 0;
while (i < 3) {
for (let index = 0; index < xy.length; index++) {
const element = xy[index];
points.push({ x: element, y: xy[i] });
}
i++;
}
return {
points: points.map((item) => {
return {
x: mathFf(item.x + padding),
y: mathFf(item.y + padding)
};
}),
circleWidth
};
}
function initCanvas(){
let canvas = document.getElementById("lockCanvas");
canvas.width = 300;
canvas.height = 300;
let ctx = canvas.getContext("2d");
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, 300, 300);
let data = calcCirclePos();
points.value = data.points.map((item,index)=>{
return{
x:item.x,
y:item.y,
k:index + 1
}
});
circleWidth.value = data.circleWidth;
//画圆
drawCircle(ctx)
//时间绑定
bindEvent(canvas, ctx);
}
2.画九宫格的九个小圆
//绘制小圆
function drawCircle(ctx ){
// 循环绘制9个圆
points.value.forEach((item)=>{
// 每一次都要重新开始新路径
ctx.beginPath();
ctx.arc(item.x, item.y, circleWidth.value / 2, 0, Math.PI * 2);
ctx.closePath();
// 将线条颜色设置为蓝色
ctx.strokeStyle = "#217bfb";
// stroke() 方法默认颜色是黑色(如果没有上面一行,则会是黑色)
ctx.stroke();
})
}
3.事件绑定
//获取触摸坐标
function getTouchPosition(canvas, event){
const rect = canvas.getBoundingClientRect();
const x = event.pageX - rect.left;
const y = event.pageY - rect.top;
return { x, y };
}
//监听手势
function bindEvent(canvas, ctx){
// 判断设备
const isMobile = /Mobile|Android/i.test(navigator.userAgent);
if(isMobile){
// 监听触摸开始事件
canvas.addEventListener("touchstart", function (e) {
// 这里要判断一下是几指触摸,只允许单指触摸
if (e.touches.length !== 1) return;
// 获取触摸的坐标位置
const { x, y } = getTouchPosition(canvas, e.touches[0]);
// 判断是否滑动到了圆圈内,是就返回圆的坐标
const point = trigger(x, y);
if (!point) {
// 没有返回坐标,就说明没有滑到任何一个小圆内,就不用管
return
}
// 把被触发的小圆坐标存起来
hitPoints.value.push(point);
// 绘制触发后的样式和连线
drawHitCircle(canvas,ctx);
}, false);
// 监听触摸移动事件
canvas.addEventListener("touchmove", function (e) {
// 防止页面跟着移动
e.preventDefault();
if (e.touches.length !== 1) return;
const { x, y } = getTouchPosition(canvas, e.touches[0]);
const point = trigger(x, y);
if (!point) {
// 没有返回坐标,就说明没有滑到任何一个小圆内,就不用管
return
}
if (hitPoints.value.includes(point)) {
// 如果那个位置已被命中过了,就不管
return
}
// 把被触发的小圆坐标存起来
hitPoints.value.push(point);
// 绘制触发后的样式和连线
drawHitCircle(canvas,ctx);
}, false);
// 监听触摸结束事件
canvas.addEventListener("touchend", function (e) {
if (hitPoints.value.length < 4) {
hitPoints.value = [];
// 重新绘制
drawHitCircle(canvas,ctx);
return showFailToast('密码无效,至少需要四个点')
} else {
let temp = hitPoints.value.map((item)=>{
return item.k
})
let count = '';
temp.forEach((el)=>{
count += el;
})
state.gesturePassword = count;
// 密码有效将密码传给后端或存起来
GestureLogin(state.gesturePassword)
// 然后清空临时存储的点
hitPoints.value = [];
// 重新绘制
drawHitCircle(canvas,ctx);
}
}, false);
}
}