之前一直在分享
低程式碼
和
視覺化
的文章,其中涉及到很多有意思的知識點和設計思想,今天繼續和大家分享一款非常有趣且實用的前端實戰專案——從零基於
react + canvas
實現一個滑動驗證碼,並將其釋出到 npm 上供他人使用。當然如果大家更喜歡 vue 的開發方式,也不用擔心,文中的設計思想和思路都是通用的,如果大家想學習如何封裝 vue 元件併發布到 npm 上,也可以參考我之前的文章: 從零到一教你基於vue開發一個元件庫。
文章轉載:樂位元組
從這個實戰專案中我們可以學到如下知識點:
前端元件設計的基本思路和技巧
canvas 基本知識和使用
react hooks 基本知識和使用
滑動驗證碼基本設計原理
如何封裝一款可擴充套件的滑動驗證碼元件
如何使用 dumi 搭建元件文件
如何釋出自己第一個npm元件包
如果你對以上任意知識點感興趣,相信這篇文章都會給你帶來啟發。
效果演示
滑動驗證元件基本使用和技術實現
上圖是實現的滑動驗證元件的一個效果演示,當然還有很多配置項可以選擇,以便支援更多
定製化
的場景。接下來我先介紹一下如何安裝和使用這款驗證碼外掛,讓大家有一個直觀的體驗,然後我會詳細介紹一下滑動驗證碼的實現思路,如果大家有一定的技術基礎,也可以直接跳到技術實現部分。
基本使用
因為 react-slider-vertify 這款元件我已經發布到 npm 上了,所以大家可以按照如下方式安裝和使用:
安裝
# 或者 yarn add @alex_xu/react-slider-vertifynpm i @alex_xu/react-slider-vertify -S複製程式碼
使用
import React from ‘react’;import { Vertify } from ‘@alex_xu/react-slider-vertify’;export default () => { return
透過以上兩步我們就可以輕鬆使用這款滑動驗證碼元件了,是不是很簡單?
當然我也暴露了很多可配置的屬性,讓大家對元件有更好的控制。參考如下:
技術實現
在做這個專案之前我也研究了一些滑動驗證碼的知識以及已有的技術方案,收穫很多。接下來我會以我的元件設計思路來和大家介紹如何用 react 來實現和封裝滑動驗證碼元件,如果大家有更好的想法和建議, 也可以在評論區隨時和我反饋。
1。元件設計的思路和技巧
每個人都有自己設計元件的方式和風格,但最終目的都是更
優雅
的設計元件。這裡我大致列舉一下
優雅
元件的設計指標:
可讀性
(程式碼格式統一清晰,註釋完整,程式碼結構層次分明,程式設計正規化使用得當)
可用性
(程式碼功能完整,在不同場景都能很好相容,業務邏輯覆蓋率)
複用性
(程式碼可以很好的被其他業務模組複用)
可維護性
(程式碼易於維護和擴充套件,並有一定的向下/向上相容性)
高效能
以上是我自己設計元件的考量指標,大家可以參考一下。
另外設計元件之前我們還需要明確需求,就拿滑動驗證碼元件舉例,我們需要先知道它的使用場景(
用於登入註冊、活動、論壇、簡訊等高風險業務場景的人機驗證服務
)和需求(
互動邏輯,以什麼樣的方式驗證,需要暴露哪些屬性
)。
以上就是我梳理的一個大致的元件開發需求,在開發具體元件之前,如果遇到複雜的業務邏輯,我們還可以將每一個實現步驟列舉出來,然後一一實現,這樣有助於整理我們的思路和更高效的開發。
2。滑動驗證碼基本實現原理
在介紹完元件設計思路和需求分析之後,我們來看看滑動驗證碼的實現原理。
我們都知道設計驗證碼的主要目的是為了防止機器非法暴力地入侵我們的應用,其中核心要解決的問題就是
判斷應用是誰在操作
(
人
or
機器
),所以通常的解決方案就是
隨機識別
。
上圖我們可以看到只有使用者手動將滑塊拖拽到對應的鏤空區域,才算驗證成功,鏤空區域的位置是隨機的(隨機性測試這裡暫時以前端的方式來實現,更安全的做法是通過後端來返回位置和圖片)。
基於以上分析我們就可以得出一個基本的滑動驗證碼設計原理圖:
接下來我們就一起封裝這款可擴充套件的滑動驗證碼元件。
3。封裝一款可擴充套件的滑動驗證碼元件
按照我開發元件一貫的風格,我會先基於需求來編寫元件的基本框架:
import React, { useRef, useState, useEffect, ReactNode } from ‘react’;interface IVertifyProp { /** * @description canvas寬度 * @default 320 */ width:number, /** * @description canvas高度 * @default 160 */ height:number, /** * @description 滑塊邊長 * @default 42 */ l:number, /** * @description 滑塊半徑 * @default 9 */ r:number, /** * @description 是否可見 * @default true */ visible:boolean, /** * @description 滑塊文字 * @default 向右滑動填充拼圖 */ text:string | ReactNode, /** * @description 重新整理按鈕icon, 為icon的url地址 * @default - */ refreshIcon:string, /** * @description 用於獲取隨機圖片的url地址 * @default https://picsum。photos/${id}/${width}/${height}, 具體參考https://picsum。photos/, 只需要實現類似介面即可 */ imgUrl:string, /** * @description 驗證成功回撥 * @default ():void => {} */ onSuccess:VoidFunction, /** * @description 驗證失敗回撥 * @default ():void => {} */ onFail:VoidFunction, /** * @description 重新整理時回撥 * @default ():void => {} */ onRefresh:VoidFunction}export default ({ width = 320, height = 160, l = 42, r = 9, imgUrl, text, refreshIcon = ‘http://yourimgsite/icon。png’, visible = true, onSuccess, onFail, onRefresh }: IVertifyProp) => { return
以上就是我們元件的基本框架結構。從程式碼中可以發現元件屬性一目瞭然,這都是提前做好需求整理帶來的好處,它可以讓我們在編寫元件時思路更清晰。在編寫好基本的 css 樣式之後我們看到的介面是這樣的:
接下來我們需要實現以下幾個核心功能:
鏤空效果的 canvas 圖片實現
鏤空圖案 canvas 實現
滑塊移動和驗證邏輯實現
上面的描述可能比較抽象,我畫張圖示意一下:
因為元件實現完全採用的 react hooks ,如果大家對 hooks 不熟悉也可以參考我之前的文章:
10分鐘教你手寫8個常用的自定義hooks
1.實現鏤空效果的 canvas 圖片
在開始
coding
之前我們需要對 canvas 有個基本的瞭解,建議不熟悉的朋友可以參考高效
canvas
學習文件: Canvas of MDN。
由上圖可知首先要解決的問題就是如何用
canvas
畫不規則的圖形,這裡我簡單的畫個草圖:
我們只需要使用
canvas
提供的
路徑api
畫出上圖的路徑,並將路徑填充為任意半透明的顏色即可。建議大家不熟悉的可以先了解如下
api
:
beginPath() 開始路徑繪製
moveTo() 移動筆觸到指定點
arc() 繪製弧形
lineTo() 畫線
stroke() 描邊
fill() 填充
clip() 裁切路徑
實現方法如下:
const drawPath = (ctx:any, x:number, y:number, operation: ‘fill’ | ‘clip’) => { ctx。beginPath() ctx。moveTo(x, y) ctx。arc(x + l / 2, y - r + 2, r, 0。72 * PI, 2。26 * PI) ctx。lineTo(x + l, y) ctx。arc(x + l + r - 2, y + l / 2, r, 1。21 * PI, 2。78 * PI) ctx。lineTo(x + l, y + l) ctx。lineTo(x, y + l) // anticlockwise為一個布林值。為true時,是逆時針方向,否則順時針方向 ctx。arc(x + r - 2, y + l / 2, r + 0。4, 2。76 * PI, 1。24 * PI, true) ctx。lineTo(x, y) ctx。lineWidth = 2 ctx。fillStyle = ‘rgba(255, 255, 255, 0。8)’ ctx。strokeStyle = ‘rgba(255, 255, 255, 0。8)’ ctx。stroke() ctx。globalCompositeOperation = ‘destination-over’ // 判斷是填充還是裁切, 裁切主要用於生成圖案滑塊 operation === ‘fill’? ctx。fill() : ctx。clip()}複製程式碼
這塊實現方案也是參考了 yield 大佬的原生 js 實現,這裡需要補充的一點是 canvas 的 globalCompositeOperation 屬性,它的主要目的是設定如何將一個源(新的)影象繪製到目標(已有)的影象上。
源影象 = 我們打算放置到畫布上的繪圖
目標影象 = 我們已經放置在畫布上的繪圖
w3c上有個形象的例子:
這裡之所以設定該屬性是為了讓鏤空的形狀不受背景底圖的影響並覆蓋在背景底圖的上方。如下:
接下來我們只需要將圖片繪製到畫布上即可:
const canvasCtx = canvasRef。current。getContext(‘2d’)// 繪製鏤空形狀drawPath(canvasCtx, 50, 50, ‘fill’)// 畫入圖片canvasCtx。drawImage(img, 0, 0, width, height)複製程式碼
當然至於如何生成隨機圖片和隨機位置,實現方式也很簡單,前端實現的話採用 Math。random 即可。
2.實現鏤空圖案 canvas
上面實現了鏤空形狀,那麼鏤空圖案也類似,我們只需要使用 clip() 方法將圖片裁切到形狀遮罩裡,並將鏤空圖案置於畫布左邊即可。程式碼如下:
const blockCtx = blockRef。current。getContext(‘2d’)drawPath(blockCtx, 50, 50, ‘clip’)blockCtx。drawImage(img, 0, 0, width, height)// 提取圖案滑塊並放到最左邊const y1 = 50 - r * 2 - 1const ImageData = blockCtx。getImageData(xRef。current - 3, y1, L, L)// 調整滑塊畫布寬度blockRef。current。width = LblockCtx。putImageData(ImageData, 0, y1)複製程式碼
上面的程式碼我們用到了 getImageData 和 putImageData,這兩個
api
主要用來獲取 canvas 畫布場景畫素資料和對場景進行畫素資料的寫入。實現後 的效果如下:
3.實現滑塊移動和驗證邏輯
實現滑塊移動的方案也比較簡單,我們只需要利用滑鼠的 event 事件即可:
onMouseDown
onMouseMove
onMouseUp
以上是一個簡單的示意圖,具體實現程式碼如下:
const handleDragMove = (e) => { if (!isMouseDownRef。current) return false e。preventDefault() // 為了支援移動端, 可以使用e。touches[0] const eventX = e。clientX || e。touches[0]。clientX const eventY = e。clientY || e。touches[0]。clientY const moveX = eventX - originXRef。current const moveY = eventY - originYRef。current if (moveX < 0 || moveX + 36 >= width) return false setSliderLeft(moveX) const blockLeft = (width - l - 2r) / (width - l) * moveX blockRef。current。style。left = blockLeft + ‘px’}
當然我們還需要對拖拽停止後的事件做監聽,來判斷是否驗證成功,並埋入成功和失敗的回撥。程式碼如下:
const handleDragEnd = (e) => { if (!isMouseDownRef。current) return false isMouseDownRef。current = false const eventX = e。clientX || e。changedTouches[0]。clientX if (eventX === originXRef。current) return false setSliderClass(‘sliderContainer’) const { flag, result } = verify() if (flag) { if (result) { setSliderClass(‘sliderContainer sliderContainer_success’) // 成功後的自定義回撥函式 typeof onSuccess === ‘function’ && onSuccess() } else { // 驗證失敗, 重新整理重置 setSliderClass(‘sliderContainer sliderContainer_fail’) setTextTip(‘請再試一次’) reset() } } else { setSliderClass(‘sliderContainer sliderContainer_fail’) // 失敗後的自定義回撥函式 typeof onFail === ‘function’ && onFail() setTimeout(reset。bind(this), 1000) }}
實現後的效果如下:
當然還有一些細節需要最佳化處理,這裡在 github 上有完整的程式碼,大家可以參考學習一下,如果大家想對該元件參與貢獻,也可以隨時提 issue。
4。如何使用 dumi 搭建元件文件
為了讓元件能被其他人更好的理解和使用,我們可以搭建元件文件。作為一名熱愛開源的前端
coder
,編寫元件文件也是個很好的開發習慣。接下來我們也為
react-slider-vertify
編寫一下元件文件,這裡我使用 dumi 來搭建元件文件,當然大家也可以用其他方案(比如storybook)。我們先看一下搭建後的效果:
dumi 搭建元件文件非常簡單,接下來和大家介紹一下安裝使用方式。
安裝
$ npx @umijs/create-dumi-lib # 初始化一個文件模式的元件庫開發腳手架# or$ yarn create @umijs/dumi-lib$ npx @umijs/create-dumi-lib ——site # 初始化一個站點模式的元件庫開發腳手架# or$ yarn create @umijs/dumi-lib ——site複製程式碼
本地執行
npm run dev# oryarn dev
編寫文件
dumi 約定式的定義了文件編寫的位置和方式,其官網上也有具體的飯介紹,這裡簡單給大家上一個 dumi 搭建的元件目錄結構圖:
我們可以在 docs 下編寫元件庫文件首頁和引導頁的說明,在單個元件的資料夾下使用 index。md 來編寫元件自身的使用文件,當然整個過程非常簡單,我這裡舉一個文件的例子:
透過這種方式 dumi 就可以幫我們自動渲染一個元件使用文件。如果大家想學習更多元件文件搭建的內容,也可以在 dumi 官網學習。
5。釋出自己第一個npm元件包
最後一個問題就是元件釋出。之前很多朋友問我如何將自己的元件釋出到 npm 上讓更多人使用,這塊的知識網上有很多資料可以學習,那今天就以滑動驗證碼
@alex_xu/react-slider-vertify
的例子,來和大家做一個簡單的介紹。
擁有一個 npm 賬號並登入
如果大家之前沒有 npm 賬號,可以在 npm 官網 註冊一個,然後用我們熟悉的
IDE
終端登入一次:
npm login
跟著提示輸入完使用者名稱密碼之後我們就能透過命令列釋出元件包了:
npm publish ——access public
之所以指令後面會加 public 引數,是為了避免許可權問題導致元件包無法釋出成功。我們為了省事也可以把釋出命令配置到 package。json 中,在元件打包完成後自動釋出:
{ “scripts”: { “start”: “dumi dev”, “release”: “npm run build && npm publish ——access public”, }}
這樣我們就能將元件輕鬆釋出到 npm 上供他人使用啦! 我之前也開源了很多元件庫,如果大家對元件打包細節和構建流程有疑問,也可以參考我之前開源專案的方案。 釋出到 npm 後的效果:
最後
如果大家對視覺化搭建或者低程式碼/零程式碼感興趣,也可以參考我往期的文章或者在評論區交流你的想法和心得,歡迎一起探索前端真正的技術
文章轉載:樂位元組
在學程式設計,Java的小夥伴們,一個人摸黑學會很難,up也是過來人, 歡迎加入扣裙:933-873-310 備註75 這裡給大家準備了配套影片、書籍等學習資源,還有接單技巧