vue封装一个图案手势锁组件

目录
  • 说在前面
  • 效果展示
  • 预览地址
  • 实现步骤
    • 1.组件设计
    • 2.组件分析
    • 3.组件实现
    • 4.组件使用
  • 组件库引用
  • 源码地址
  • 组件文档

说在前面

现在很多人都喜欢使用图案手势锁,这里我使用vue来封装了一个可以直接使用的组件,在这里记录一下这个组件的开发步骤。

效果展示

组件实现效果如下图:

预览地址

http://jyeontu.xyz/jvuewheel/#/JAppsLock

实现步骤

完成一个组件需要几步?

1.组件设计

首先我们应该要知道我们要做怎样的组件,具备怎样的功能,这样才可以开始动手去实现。
功能上其实是已经很明确了,就是仿照手机上现有的图案锁来进行网页版组件开发。这里我们对入参和回调先进行一个大致的设计。

size

图案的尺寸,默认为3,即图案的大小为 3 * 3,4的话即为4 * 4;

showArrow

是否显示划线轨迹箭头,有的时候我们并不希望图案划到的轨迹箭头显示,这样的保密性会更高,所以这里需要一个开关来控制箭头的显示与否;

commit

commit为划动结束时的回调函数,我们可以在父组件接收到划动轨迹列表。

2.组件分析

接下来就需要对组件实现过程中使用到的关键技术点做一个分析了:

(1)触屏事件&鼠标移动事件

我们需要在页面上画出图案,那么我们肯定需要利用到网页的触屏事件和鼠标移动事件,鼠标移动事件主要是用于pc端,而在移动端使用时,我们则需要利用到网页的触屏事件。

(2)点之间的连线和箭头方向

我们需要在划到的相邻的两个点之间进行连线并用箭头标出其划线方向,这里我们需要借助一点数学三角函数的知识来计算,这里就不展开了,后面会对其进行分析解释。

(3)连线完回调获得滑动轨迹

这个时候我们需要监听鼠标抬起事件和触屏结束事件,在鼠标抬起或触屏结束的时候执行回调,将滑动的图案轨迹以数组的形式返回。

3.组件实现

(1)鼠标事件和触屏事件监听

首先我们应该先对鼠标事件和触屏事件进行监听,这样才可以捕捉到我们划动图案的轨迹。\

vue中的鼠标事件和触屏事件
Vue中已经为我们定义了鼠标事件和触屏事件,我们可以直接这样使用:

@mousedown.prevent="mousedown()"
@touchstart.prevent="mousedown()"
@mouseover="mouseover(cInd)"
@touchmove="mouseover(cInd)"

JavaScript监听鼠标事件和触屏事件
在JavaScript中我们需要对鼠标事件和触屏事件进行监听:

const content = document.getElementById(this.JAppsLockId);
content.addEventListener("mousedown", this.mousedown);
content.addEventListener("mouseup", this.mouseup);

window.addEventListener("mouseup", this.mouseup);

content.addEventListener("touchstart", this.mousedown);
content.addEventListener("touchend", this.mouseup);

window.addEventListener("touchend", this.mouseup);

content.addEventListener("dragstart", () => {});
content.addEventListener("touchmove", this.touchmove);

(2)鼠标事件和触屏事件定义 鼠标按下 & 手指触屏开始
在组件内鼠标按下或者手指触屏开始的时候,我们应该做一个标记,标记当前状态为鼠标按下状态。

mousedown() {
    this.isDown = true;
    this.choosePoints = [];
    this.removeLines();
},

鼠标移动 & 触屏划动
当当前为鼠标按下状态且鼠标在移动时,我们需要判断鼠标是否移动经过某一个点,这里的鼠标移动事件和触屏划动事件有点区别,需要分别定义。

mouseover(ind) {
    if (!this.isDown) return;
    if (this.choosePoints.includes(ind)) return;
    this.choosePoints.push(ind);
},
touchmove(event) {
    if (!this.isDown) return;
    if (this.pointsArea.length === 0) {
        this.initPointsArea();
    }
    const content = document.getElementById(this.JAppsLockId + "lock");
    let nx = event.targetTouches[0].pageX - content.offsetLeft;
    let ny = event.targetTouches[0].pageY - content.offsetTop;
    for (let i = 0; i < this.pointsArea.length; i++) {
        const item = this.pointsArea[i];
        const { x, y, r } = item;
        if (Math.pow(x - nx, 2) + Math.pow(y - ny, 2) <= r * r) {
            if (this.choosePoints.includes(i)) return;
            this.choosePoints.push(i);
            break;
        }
    }
},

(3)鼠标抬起和触屏划动结束回调

在鼠标抬起和触屏划动结束的时候需要进行回调,将当前划动过程中经过的图案轨迹输出。

mouseup() {
    if (!this.isDown) return;
    this.isDown = false;
    this.drawLine();
    this.$emit("commit", this.choosePoints);
},

(4)组件数据初始化

我们需要先确定当前组件的id,当父组件定义了子组件的id时则使用定义的id,否则则自动生成id

initData() {
    let id = this.id;
    if (id == "") {
        id = getUId();
    }
    this.JAppsLockId = id;
},

(5)图案数据初始化

我们需要根据传来的size参数来渲染不同尺寸的图案点阵。

initCell() {
    const id = this.JAppsLockId;
    const size = this.size;
    const content = document.getElementById(id);
    const cH = content.offsetHeight;
    const cW = content.offsetWidth;
    const cellH = (cH - 20 - size * 6 * 2) / size + "px";
    const cellW = (cW - 20 - size * 6 * 2) / size + "px";
    this.cellH = cellH;
    this.cellW = cellW;
}

(6)获取图案点阵的位置数据

我们可以先获取图案点阵的圆心坐标及半径,为后续进行判断计算作准备。

initPointsArea() {
    this.pointsArea === [];
    const cell = document.getElementsByClassName("j-apps-lock-cell")[0];
    for (let i = 0; i < this.size * this.size; i++) {
        const point = document.getElementById("point-" + i);
        const x =
            (point.offsetLeft + point.offsetWidth + point.offsetLeft) /
            2;
        const y =
            (point.offsetTop + point.offsetHeight + point.offsetTop) /
            2;
        const r = cell.offsetHeight / 2;
        this.pointsArea.push({ x, y, r });
    }
},

(7)图案连线 首先我们需要先计算好需要连线的两个图案的坐标。

drawLine() {
    const domPoints = this.getPoints();
    for (let i = 1; i < domPoints.length; i++) {
        const x1 =
            domPoints[i - 1].offsetWidth + domPoints[i - 1].offsetLeft;
        const x2 = domPoints[i].offsetWidth + domPoints[i].offsetLeft;
        const y1 =
            domPoints[i - 1].offsetHeight + domPoints[i - 1].offsetTop;
        const y2 = domPoints[i].offsetHeight + domPoints[i].offsetTop;
        this.createLine(
            x1,
            x2,
            y1,
            y2,
            domPoints[i - 1],
            domPoints[i]
        );
    }
}

通过计算好的坐标数据,生成对应的线段

createLine(x1, x2, y1, y2, p1, p2) {
    let line = document.createElement("span");
    line.classList.add("j-apps-lock-line");
    line.style.position = "absolute";
    line.style.display = "flex";
    line.style.left = "50%";
    line.style.top = "50%";
    line.style.margin = "center";
    line.style.width = Math.max(Math.abs(x2 - x1), 2) + "px";
    line.style.height = Math.max(Math.abs(y2 - y1), 2) + "px";
    line.style.backgroundColor = "gray";
    if (this.showArrow)
        line.appendChild(this.createArrow(x1, x2, y1, y2));
    if (x1 != x2 && y1 != y2) {
        const x = Math.abs(x1 - x2);
        const y = Math.abs(y1 - y2);
        line.style.height = Math.sqrt(x * x + y * y) + "px";
        line.style.width = "2px";
        let angle = (Math.atan(x / y) * 180) / Math.PI;
        if ((x2 > x1 && y2 > y1) || (x2 < x1 && y2 < y1))
            angle = "-" + angle;
        line.style.transform = `rotate(${angle}deg)`;
        line.style.transformOrigin = "left top";
        if (y2 > y1) p1.appendChild(line);
        else p2.appendChild(line);
    } else if (x2 > x1 || y2 > y1) {
        p1.appendChild(line);
    } else {
        p2.appendChild(line);
    }
    return line;
},

由上面代码我们可以看到,在连线的绘制中,我们使用到了css中的旋转属性,其旋转角度是使用Math.atan计算出来的,所以我们需要先对三角函数进行一定了解。

javascript中计算三角函数

三角函数的定义

正弦(sin)      sinA = a / c       sinθ = y / r
余弦(cos)     cosA = b / c      cosθ = y / r
正切(tan)      tanA = a / b      tanθ = y / x
余切(cot)      cotA = b / a      cotθ = x / y
js中计算三角函数用Math.sin()等静态方法,参数为弧度

角度与弧度都是角的度量单位

角度:两条射线从圆心向圆周射出,形成一个夹角和夹角正对的一段弧。当这段弧长正好等于圆周长的360分之一时,两条射线的夹角的大小为1度。
弧度:两条射线从圆心向圆周射出,形成一个夹角和夹角正对的一段弧。当这段弧长正好等于圆的半径时,两条射线的夹角大小为1弧度。

1弧度时,弧长等于半径,那弧长是半径的倍数就是弧度了
弧度 = 弧长 / 半径
弧长 = 弧度 * 半径
弧长 = (角度 / 360) * 周长

角度与弧度换算

角度 = 弧长 / 周长 = 弧长/(2πr) = 弧度*r/(2πr) = 弧度/(2π)
弧度 = 弧长 / 半径 = [(角度 / 360) * 周长] / 半径 =[ (角度 / 360) * 2πr] / r = 角度 * π / 180

js计算三角函数

var sin30 = Math.sin(30 * Math.PI / 180)
console.log(sin30);  //0.49999999999999994

var cos60 = Math.cos(60 * Math.PI / 180)
console.log(cos60);  //0.5000000000000001

var tan45 = Math.tan(45 * Math.PI / 180)
console.log(tan45);  //0.9999999999999999

var asin30 = Math.round(Math.asin(sin30) * 180 / Math.PI)
console.log(asin30); //30

var acos60 = Math.round(Math.acos(cos60) * 180 / Math.PI)
console.log(acos60); //60

var atan45 = Math.round(Math.atan(tan45) * 180 / Math.PI)
console.log(atan45); //45

(8)图案连线轨迹箭头

我们只需要将箭头元素添加到线段元素中,作为线段元素的子元素,我们便不用单独对箭头元素的旋转角度进行处理。

createArrow(x1, x2, y1, y2) {
    let arrow = document.createElement("span");
    arrow.classList.add("j-apps-lock-arrow");
    arrow.style.position = "relative";
    arrow.style.margin = "auto";
    arrow.style.fontSize = "1.5rem";
    arrow.style.zIndex = "10";
    arrow.style.display = "block";
    arrow.style.minWidth = "1.4rem";
    arrow.style.textAlign = "center";
    if (y1 === y2) {
        arrow.innerText = x1 > x2 ? "<" : ">";
        arrow.style.top = "-0.8rem";
    } else {
        arrow.innerText = y1 > y2 ? "∧" : "∨";
        arrow.style.left = "-0.65rem";
    }
    return arrow;
},

4.组件使用

<template>
    <div class="content">
        <j-apps-lock @commit="commit" size="4"></j-apps-lock>
    </div>
</template>
<script>
    export default {
        data() {
            return {
            }
        },
        methods:{
            commit(password) {
                this.$JToast(password);
            }
        }
    }
</script>

组件库引用

这里我将这个组件打包进了自己的一个组件库,并将其发布到了npm上,有需要的同学也可以直接引入该组件进行使用。
引入教程可以看这里:http://jyeontu.xyz/jvuewheel/#/installView
引入后即可直接使用。

源码地址

组件库已开源,想要查看完整源码的可以到 gitee 查看,自己也整理了相关的文档对其进行了简单介绍,具体如下:

组件文档

jvuewheel: http://jyeontu.xyz/jvuewheel/#/JBarrageView

Gitee源码

Gitee源码:https://gitee.com/zheng_yongtao/jyeontu-component-warehouse

到此这篇关于vue封装一个图案手势锁组件的文章就介绍到这了,更多相关vue 图案手势锁内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • vue-drawer-layout实现手势滑出菜单栏

    本文实例为大家分享了vue-drawer-layout实现手势滑出菜单栏的具体代码,供大家参考,具体内容如下 文档链接地址 安装 npm install vue-drawer-layout --save main.js导入 import DrawerLayout from 'vue-drawer-layout' Vue.use(DrawerLayout) 完整代码 <template> <div class="box"> <vue-drawer-layou

  • vue-router 手势滑动触发返回功能

    vue-router的路由变换只存在"变换前"和"变换后",不存在"切换中"的状态,所以做不到大多数app(微信那样的)在滑动过程中让界面跟随手指移动.但滑动事件还是可以监听的,我们可以在滑动之后再触发路由回退事件. 微博的滑动返回基本上就是这样的原理:先滑动.再触发返回事件,但用起来很是怪异,有严重的滞后感.夸克浏览器做的就比较好:一是滑动时界面虽然不动,但是界面上有小图标提示,能让用户接受到反馈:二是返回过程很快,没有多余的过渡动画. app

  • vue2实现手势密码功能

    本文实例为大家分享了vue2实现手势密码功能的具体代码,供大家参考,具体内容如下 组件: <template>   <div class="masks" v-show="currentValue">     <div class="gesturePwd">       <div class="box">         <h4 ref="gestureTitle&

  • vue封装一个图案手势锁组件

    目录 说在前面 效果展示 预览地址 实现步骤 1.组件设计 2.组件分析 3.组件实现 4.组件使用 组件库引用 源码地址 组件文档 说在前面 现在很多人都喜欢使用图案手势锁,这里我使用vue来封装了一个可以直接使用的组件,在这里记录一下这个组件的开发步骤. 效果展示 组件实现效果如下图: 预览地址 http://jyeontu.xyz/jvuewheel/#/JAppsLock 实现步骤 完成一个组件需要几步? 1.组件设计 首先我们应该要知道我们要做怎样的组件,具备怎样的功能,这样才可以开始

  • vue封装一个弹幕组件详解

    目录 前言 功能实现 1.获取随机颜色 随机数生成 随机颜色编码生成 2.随机生成弹幕出现的高度坐标 3.格式化弹幕对象 颜色 定位 4.创建弹幕对象 滚动动画定义 创建弹幕dom对象实例 弹幕销毁 弹幕循环 5.实时弹幕发送 html JavaScript 源码地址 前言 现在很多地方都有使用到弹幕,最近在捣鼓自己的个人博客网站,也想着在里面加入一个弹幕模块,所以在这里封装了一个可复用的弹幕组件,目前已经实现了基本的功能,可能还会有存在缺陷,后续会继续优化.这里给大家介绍分享一下实现的过程.

  • Vue实现封装一个切片上传组件

    目录 组件效果 使用文档 封装过程 1. 文件切片 2. 构造切片请求参数 3. 控制分片请求的并发 完整代码 待完善 组件效果 单文件切片上传 多文件切片上传 组件使用案例 <template> <div id="app"> <div class="upload-wrap"> <UploadSlice :action="uploadInfoSlice.actionChunk" :headers=&quo

  • Vue实现一个返回顶部backToTop组件

    最近在学习VUE.自己就在研究怎么用VUE实现一个组件的封装,今日就算留个笔记 前言 返回顶部这个功能用jq实现,好容易实现,一个animate配合scrollTo就搞定了 今天我们来试试vue封装一个原生js实现的返回顶部; 写起来够呛,借助github,看了别人的gist,稍微封装了下; 当然不是用scrollTo直接调位那种,没有过渡效果怎么说得过去!!还是捣鼓出来了. 废话不多说,看效果图- 效果图 实现思路 过渡用的是requestAnimationFrame,这货只支持IE10+,所

  • Vue封装远程下拉框组件的实现示例

    之前封装了一个远程搜索的输入框,静态在Vue官网看到一个类似的远程搜索下拉框,今天也封装一个远程搜索下拉框,面对不同的需求 我们修改了官方提供的代码来封装了 父组件 RemoteSearch.vue <template> <el-row> <el-select v-if="chooseFlag ==0" v-model="selectKey" :multiple="false" :filterable="t

  • vue设计一个倒计时秒杀的组件详解

    简介: 倒计时秒杀组件在电商网站中层出不穷  不过思路万变不离其踪,我自己根据其他资料设计了一个vue版的 核心思路: 1.时间不能是本地客户端的时间  必须是服务器的时间这里用一个settimeout代替 以为时间必须统一 2.开始时间,结束时间通过父组件传入,当服务器时间在这个开始时间和结束时间的范围内  参加活动按钮可以点击,并且参加过活动以后不能再参加, 3.在组件创建的时候 同步得到现在时间服务时间差,并且在这里边设置定时器,每秒都做判断看秒杀是否开始和结束, 4.在更新时间的函数中是

  • Vue 封装防刷新考试倒计时组件的实现

    本文详细的介绍了防刷新考试倒计时组件 ,分享给大家,也给自己留个笔记,感兴趣的可以了解下 <!-- 考试倒计时组件 --> <template> <div class="time"> <p>00:{{timerCount2}}:{{timerCount1}}</p> <button @click="reset">重新计时</button> </div> </temp

  • Vue封装一个TodoList的案例与浏览器本地缓存的应用实现

    本文主要介绍了Vue封装一个TodoList的案例与浏览器本地缓存的应用实现,分享给大家,具体如下: 使用Vue封装一个简易的Todolist的小案例. 同时加入了浏览器本地缓存的技术手段. 浏览器本地缓冲: 前提: 一般我们定义的变量,或者用Vuex保存的数据, 当浏览器进行了一个刷新 那么这个数据就会丢失, 这样就做不出历史记录的效果了, 但是, 使用浏览器缓存就可以帮助我们解决这个问题- 浏览器缓存分为二种 sessionStorage 和 localStorage, 二种原型链分别如下:

  • Vue封装一个简单轻量的上传文件组件的示例

    一.之前遇到的一些问题 项目中多出有上传文件的需求,使用现有的UI框架实现的过程中,不知道什么原因,总会有一些莫名其妙的bug.比如用某上传组件,明明注明(:multiple="false"),可实际上还是能多选,上传的时候依然发送了多个文件:又比如只要加上了(:file-list="fileList")属性,希望能手动控制上传列表的时候,上传事件this.refs.[upload(组件ref)].submit()就不起作用了,传不了.总之,懒得再看它怎么实现了,我

  • Vue封装一个Tabbar组件 带组件路由跳转方式

    目录 Vue封装Tabbar组件 在App.vue 封装 路由跳转 利用router-view的特性 子组件Tabbar 然后就是配置的路由 从零开始封装一个Tabbar 首先底部菜单栏最外层是一个div, 代码如下 封装完每一个tabbaritem后 使用者在使用我们封装的控件时 Vue封装Tabbar组件 话不多说直接上代码 在App.vue 封装 路由跳转 利用router-view的特性 <template>   <div id="app">     &

随机推荐