• 使用 rollup 打包一个原生 js + canvas 实现的移动端手势解锁功能组件


    说明

    2017 前端星计划选拔作业学习笔记

    源码笔记:https://github.com/kaimo313/h5-handlelock

    原型和操作流程

    用户用手指按顺序依次划过 9 个原点中的若干个(必须不少于 4 个点),如果划过的点的数量和顺序与之前用户设置的相同,那么当用户的手指离开屏幕时,判定为密码输入正确,否则密码错误。

    要求:实现一个移动网页,允许用户设置手势密码和验证手势密码。已设置的密码记录在本地 localStorage 中。

    stat 1:设置密码。

    用户选择设置密码时,要提示用户输入手势密码。

    在这里插入图片描述

    stat 2:密码长度太短

    如果用户输入的密码不足 5 个点,提示用户密码太短。

    在这里插入图片描述

    stat 3:再次输入密码

    设置成功一次密码后,提示用户再次输入密码。

    在这里插入图片描述

    stat 4:两次密码输入不一致

    如果用户输入的两次密码不一致,提示并重置,重新开始设置密码

    在这里插入图片描述

    stat 5:密码设置成功

    如果两次输入一致,密码设置成功,更新 localStorage。

    在这里插入图片描述

    stat 6:验证密码 - 不正确

    切换单选框进入验证密码模式,将用户输入的密码与保存的密码相比较,如果不一致,则提示输入密码不正确,重置为等待用户输入。

    在这里插入图片描述

    stat 7:验证密码 - 正确

    如果用户输入的密码与 localStorage 中保存的密码一致,则提示密码正确。

    在这里插入图片描述

    组件设计步骤

    组件设计一般来说包括 7 个步骤,分别是:理解需求、技术选型、结构(UI)设计、数据和 API 设计、流程设计、兼容性和细节优化,以及工具和工程化。

    理解需求

    需要由使用者决定设置密码的过程里执行什么操作、验证密码的过程和密码验证成功后执行什么操作,应当将过程节点开放出来,让使用者来决定。

    技术选型

    UI 展现的核心是九宫格和选中的小圆点,这里我们使用 canvas 去实现效果

    • SVG 原生操作的 API 不是很方便,可以使用Snap.svg,但移动端兼容性不如 DOM 和 Canvas 好
    • DOM 的优点是容易实现响应式,事件处理简单,布局也不复杂,但是要计算斜线的长度和斜率。

    第一个细节:用 DOM 构造一个正方形的容器,使用 padding-top:100% 撑开容器高度使它等于容器宽度。

    #container {
      position: relative;
      overflow: hidden;
      width: 100%;
      padding-top: 100%;
      height: 0px;
      background-color: white;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    第二个细节:为了在 retina 屏上获得清晰的显示效果,可以将 Canvas 的宽高增加一倍,然后通过 transform: scale(0.5) 来缩小到匹配容器宽高。

    #container canvas{
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%) scale(0.5);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Retina:一种新型高分辨率的显示标准,是把更多的像素点压缩至一块屏幕里,从而达到更高的分辨率并提高屏幕显示的细腻程度。由摩托罗拉公司研发。最初该技术是用于Moto Aura上。这种分辨率在正常观看距离下足以使人肉眼无法分辨其中的单独像素。也被称为视网膜显示屏

    原因:canvas 绘制的图形是位图,即栅格图像或点阵图像,当将它渲染到高清屏时,会被放大,每个像素点会用 devicePixelRatio 的平方个物理像素点来渲染,因此图片会变得模糊。

    另外设置一下canvas的宽高相等

    let width = 2 * container.getBoundingClientRect().width;
    canvas.width = canvas.height = width;
    
    • 1
    • 2

    这样就得到了一个正方形

    在这里插入图片描述

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>kaimo canvas demotitle>
        <style>
            #container {
                position: relative;
                overflow: hidden;
                width: 100%;
                padding-top: 100%;
                height: 0px;
                background-color: white;
            }
            #container canvas{
                position: absolute;
                left: 50%;
                top: 50%;
                transform: translate(-50%, -50%) scale(0.5);
            }
        style>
    head>
    <body>
        <div id="container">
            <canvas id="canvas">canvas>
        div>
        <script>
            let width = 2 * container.getBoundingClientRect().width;
            canvas.width = canvas.height = width;
        script>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    结构设计

    因为 Canvas 的渲染机制里,要更新画布的内容,需要刷新要更新的区域重新绘制。因此我们有必要把频繁变化的内容和基本不变的内容分层管理,这样能显著提升性能。

    这里我们可以分成三层

    • 底层:已经画好的线
    • 中层:排列的九个点
    • 上层:随着手指头移动的那个线段

    下面确定圆点的位置:我们使用 4 x 4 的这种,需要遍历绘制实心圆,另外 Touch 相对于屏幕的坐标也需要转换为 Canvas 相对于画布的坐标。

    在这里插入图片描述

    API 设计

    Recorder 只负责记录用户手势行为

    • render:进行渲染
    • record:负责记录
    • clearPath:负责在画布上清除上一次记录的结果
    • cancel:负责终止记录过程

    对于输出结果,可以使用选中圆点的行列坐标拼接起来得到一个唯一的序列。

    例如:11121323 就是如下选择图形:

    在这里插入图片描述

    让 Locker 继承 Recorder,管理实际的设置和验证密码的过程

    • check:负责校验
    • update:负责更新密码

    另外我们可以将外观配置抽取出来:比如颜色,个数等等。

    流程设计

    验证密码的流程:

    在这里插入图片描述

    设置密码的流程:

    在这里插入图片描述

    打包组件

    这里我们采用 rollup 进行组件的打包,rollup 它是一个类似于 webpack 的打包工具,区别于 webpack,它更适合一个库的打包。

    核心概念

    • input:入口文件,类比于 webpack 的 entry,它指明了我们库文件入口位置。
    • output:输出位置,它指明了打包后的输出信息,包括:输出目录,打包文件名等。
    • plugins:插件,rollup 在构建过程中,插件可提供一些辅助功能,例如:alias别名解析、转义ES6等。
    • external:当我们的库依赖于其它第三方库时,我们不需要把这些第三方库一起打包,而是应该把依赖写在external里面。

    构建说明

    • umd:此选项构建出来的库文件是一个通用模式,可以通过不同的方式去使用:script 标签引入,ES Module 规范引入和 CommonJs 规范引入等。
    • cjs:此选项构建出来的库文件主要为 CommonJs 规范,可在 Node 环境中使用。
    • es:此版本构建出来的库文件主要为 ES Module 规范,可在支持 ES Module 也就是 import/export 的环境中使用。

    package.json 中配置打包命令:

    {
      "scripts": {
        "dev": "rollup -wc"
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • -c:为 --config 的缩写,表示设置 rollup 打包的配置。
    • -w:为 --watch 的缩写,在本地开发环境添加 -w 参数可以监控源文件的变化,自动重新打包。

    常用插件

    在这里插入图片描述

    代码实现

    1、新建初始化项目

    npm init -y
    
    • 1

    在这里插入图片描述

    2、安装依赖

    npm i rollup -D
    
    • 1

    在这里插入图片描述
    为了让库文件具有更好的兼容性,需要把ES6代码在打包的时候转义成ES5。

    # 安装rollup插件包
    npm install @rollup/plugin-babel -D
    
    # 安装babel相关包
    npm install @babel/core @babel/preset-env -D
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述
    在根目录下新建 .babelrc 文件,并撰写如下内容:

    {
        "presets": [
            "@babel/preset-env"
        ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    需要对生产环境进行压缩

    # 安装代码压缩插件
    npm install rollup-plugin-terser -D
    
    • 1
    • 2

    在这里插入图片描述

    3、配置 rollup.config.js 文件

    根路径添加 rollup.config.js 文件,配置如下:

    // 用于es6转es5
    import { babel } from '@rollup/plugin-babel';
    // 用于代码压缩
    import { terser } from 'rollup-plugin-terser';
    
    const config = {
        input: "./src/index.js",
        output: [
            {
                file: './lib/kaimo-handlock-umd.js',
                format: 'umd',
                name: 'KaimoHandlock'
                // 当入口文件有export时,'umd'格式必须指定name
                // 这样,在通过