canvas 實現vue 手勢解鎖組件


1.手機鎖屏九宮格解鎖組件

2.代碼如下

<template>
<canvas id="gesture" ref="canvas" :style="style" />
</template>

<script>
export default {
name: 'GestureLock',
props: {
chooseType: {
type: Number,
default: 3 // 3: 3*3, 4: 4*4, 5: 5*5
}
},
data() {
return {
touchedFlag: false, // 是否正在繪制
canvas: null,
ctx: null,
radius: 0, // 計算后的半徑大小
style: {
width: 450,
height: 500
},
devicePixelRatio: window.devicePixelRatio || 1,
circleArr: [],
lastPoint: [],
restPoint: []
};
},
mounted() {
this.canvas = this.$refs.canvas;
this.ctx = this.canvas.getContext('2d');

this.canvas.width = this.style.width * this.devicePixelRatio;
this.canvas.height = this.style.height * this.devicePixelRatio;

this.createCircle();
this.bindEvent();
},
beforeDestroy() {
this.canvas.removeEventListener('touchstart', this.onStartHandler);
this.canvas.removeEventListener('touchmove', this.onMoveHandler);
this.canvas.removeEventListener('touchend', this.onEndHandler);
},
methods: {
drawPoint(style) {
// 初始化圓心
for (var i = 0; i < this.lastPoint.length; i++) {
this.ctx.fillStyle = style;
this.ctx.beginPath();
this.ctx.arc(
this.lastPoint[i].x,
this.lastPoint[i].y,
this.radius / 2.5,
0,
Math.PI * 2,
true
);
this.ctx.closePath();
this.ctx.fill();
}
},
drawStatusPoint(type) {
// 初始化狀態線條
for (var i = 0; i < this.lastPoint.length; i++) {
this.ctx.strokeStyle = type;
this.ctx.beginPath();
this.ctx.arc(
this.lastPoint[i].x,
this.lastPoint[i].y,
this.radius,
0,
Math.PI * 2,
true
);
this.ctx.closePath();
this.ctx.stroke();
}
},
drawCircle(x, y) {
// 初始化解鎖密碼面板 小圓圈
this.ctx.strokeStyle = '#648F83'; // 密碼的點點默認的顏色
this.ctx.lineWidth = 2;
this.ctx.beginPath();
this.ctx.arc(x, y, this.radius, 0, Math.PI * 2, true); // 畫圓
this.ctx.closePath();
this.ctx.stroke();
},
drawLine(style, position) {
// style:顏色 解鎖軌跡
this.ctx.beginPath();
this.ctx.strokeStyle = style;
this.ctx.lineWidth = 3;
this.ctx.moveTo(this.lastPoint[0].x, this.lastPoint[0].y);

for (var i = 1; i < this.lastPoint.length; i++) {
this.ctx.lineTo(this.lastPoint[i].x, this.lastPoint[i].y);
}
this.ctx.lineTo(position.x, position.y);
this.ctx.stroke();
this.ctx.closePath();
},
createCircle() {
var n = this.chooseType;
var count = 0;
this.radius = this.ctx.canvas.width / (1 + 4 * n); // 公式計算
this.lastPoint = [];
this.circleArr = [];
this.restPoint = [];
var r = this.radius;

for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
count++;
let point = {
x: j * 4 * r + 3 * r,
y: i * 4 * r + 3 * r,
index: count
};
this.circleArr.push(point);
this.restPoint.push(point);
}
}

this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);

// 畫9個圓
for (let i = 0; i < this.circleArr.length; i++) {
this.drawCircle(this.circleArr[i].x, this.circleArr[i].y);
}
},
onStartHandler(e) {
e.preventDefault(); // 某些android 的 touchmove不宜觸發 所以增加此行代碼

// 重置圖案
this.onReset();
var position = this.getPosition(e);

for (var i = 0; i < this.circleArr.length; i++) {
if (
Math.abs(position.x - this.circleArr[i].x) < this.radius &&
Math.abs(position.y - this.circleArr[i].y) < this.radius
) {
this.touchedFlag = true;
this.drawPoint(this.circleArr[i].x, this.circleArr[i].y);
this.lastPoint.push(this.circleArr[i]);
this.restPoint.splice(i, 1);
break;
}
}
},
onMoveHandler(e) {
if (this.touchedFlag) {
this.onUpdateHandler(this.getPosition(e));
}
},
onEndHandler(e) {
if (this.touchedFlag) {
this.touchedFlag = false;
this.$emit('input', this.lastPoint.map((point) => {
return point.index;
}));
// 重繪最后所有點
this.onUpdateHandler(this.lastPoint[this.lastPoint.length - 1]);
}
},
onUpdateHandler(position) {
// 核心變換方法在touchmove時候調用
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);

for (let i = 0; i < this.circleArr.length; i++) {
// 每幀先把面板畫出來
this.drawCircle(this.circleArr[i].x, this.circleArr[i].y);
}

this.drawPoint('#648F83'); // 每幀花軌跡
this.drawStatusPoint('#648F83'); // 每幀花軌跡

this.drawLine('#648F83', position, this.lastPoint); // 每幀畫圓心

for (let i = 0; i < this.restPoint.length; i++) {
if (
Math.abs(position.x - this.restPoint[i].x) < this.radius &&
Math.abs(position.y - this.restPoint[i].y) < this.radius
) {
this.drawPoint(this.restPoint[i].x, this.restPoint[i].y);
this.lastPoint.push(this.restPoint[i]);
this.restPoint.splice(i, 1);
break;
}
}
},
onReset() {
this.createCircle();
},
bindEvent() {
// 網頁
this.canvas.addEventListener('mousedown', this.onStartHandler);
this.canvas.addEventListener('mousemove', this.onMoveHandler);
this.canvas.addEventListener('mouseup', this.onEndHandler);
// 移動端
this.canvas.addEventListener('touchstart', this.onStartHandler);
this.canvas.addEventListener('touchmove', this.onMoveHandler);
this.canvas.addEventListener('touchend', this.onEndHandler);
},
getPosition(e) {
// 獲取touch點相對於canvas的坐標
var rect = e.currentTarget.getBoundingClientRect();
var position = e.touches ? {
x: (e.touches[0].clientX - rect.left) * this.devicePixelRatio,
y: (e.touches[0].clientY - rect.top) * this.devicePixelRatio
} : {
x: (e.clientX - rect.left) * this.devicePixelRatio,
y: (e.clientY - rect.top) * this.devicePixelRatio
};
return position;
}
}
};
</script>
3.組件引用
<GestureLock v-model="gestureData"></GestureLock>
data () {
return {
gestureData:[]
};
}
 
watch: {
// 按繪制的順序輸出的數組
gestureData(val) {
console.log(val);
}
},
4.結果

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM