前言
在工作中遇到一个项目需求要让用户使用手势画星位图来解锁星座运势,一看设计稿,这不就是我们平时的手机屏幕解锁吗?于是上网搜了一些关于手势解锁的文章,没找到可以直接复用的,于是打开canvas教程,边学习边设计实现了这个功能,同时兼容了移动端和PC端,在这里把代码分享出来,感兴趣的可以看看。
Demo
需要实现的功能
-
在canvas画布上展示指定行 * 列星星,并可设置随机显示位置
-
手指滑动可连接画布上任意亮点的星星
-
当画布上已经有连接的星星时,可以从已有星星的首部或者尾部开始继续连接
-
同一颗星星不可重复连接,且需要限制连接星星数量的最大最小值
-
其他:兼容PC端、连接星星过程中禁止滚动等
初始化数据和页面渲染
-
定义好连接星星的行列数目(starXNum 和 starYNum),和canvas画布的宽高
-
根据定义好的行列和canvas画布大小,计算好每颗星星的大小(starX)和横竖间距(spaceX、spaceY),初始化星星, 这里一开始想通过canvas渲染星星到画布上,但是由于呈现出的小圆点呈锯齿状,视觉体验不好,因此改成用常规div+css画出所有的星星然后通过计算距离渲染(如上图)
<div class="starMap" ref="starMap">
<canvas
id="starMap"
ref="canvas"
class="canvasBox"
:width="width"
:height="height"
:style="{ width, height }"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
@mousedown="touchStart"
@mousemove="touchMove"
@mouseup="touchEnd"
@mouseout="touchEnd"
@mouseenter="touchStart"
></canvas>
<div class="starsList">
<div v-for="n in starXNum" :key="n" class="starColBox" :style="{ marginBottom: `${spaceY}px` }">
<div v-for="j in starYNum" :key="j" class="starRow" :style="{ marginRight: `${spaceX}px` }">
<div :class="['starIcon', showStar(n, j) && 'show']" :style="{ width: `${starX}px`, height: `${starX}px` }">
<div :class="['starCenter', isSelectedStar(n, j) && `animate-${getRandom(0, 2, 0)}`]"></div>
</div>
</div>
</div>
</div>
<canvas id="judgeCanvas" :width="width" :height="height" class="judgeCanvas" :style="{ width, height }"></canvas>
</div>
/*
* this.width=画布宽
* this.height=画布高
* this.starX=星星的大小,宽高相等不做区分
*/
spaceX () {
// 星星横向间距
return (this.width - this.starX * this.starXNum) / 4
}
spaceY () {
// 星星纵向间距
return (this.height - this.starX * this.starYNum) / 4
}
- 初始化canvas画布和基础数据
-
通过 canvas.getContext(‘2d’) ➚ 获取绘图区域
-
定义一个数组pointIndexArr来存储最原始画布上所有可能的星星,再定义数组 pointPos 存储初当前展示的所有星星的坐标(以当前canvas画布左上角的坐标为圆点),用于手指滑动过程中判断是否经过某个点
-
定义数组 points 存放画布上已经连接的星星
-
设置canvas绘图的样式,如连接线的宽度 lineWidth,模糊度 lineBlurWidth,设置canvas连接线色值 strokeStyle = ‘#c9b8ff’,连接线结束时为圆形的线帽 lineCap = ‘round’ 。
function setData () {
// 初始化canvas数据
this.initStarPos()
this.lineWidth = 2 // 连接线宽度
this.lineBlurWidth = 6 // 连接线shadow宽
this.canvas = document.getElementById('starMap')
if (!this.canvas) return console.error('starMap: this.canvas is null')
this.ctx = this.canvas.getContext('2d')
this.ctx.strokeStyle = '#c9b8ff'
this.ctx.lineCap = 'round'
this.ctx.lineJoin = 'bevel'
const judgeCanvas = document.getElementById('judgeCanvas')
this.judgeCtx = judgeCanvas.getContext('2d')
}
function initStarPos