React-slider-vertify demo address: react-slider-vertify note: ⚠️

Before, I have been sharing articles about low code and visualization, which involves many interesting knowledge points and design ideas. Today, I will continue to share a very interesting and practical front-end practice project — implement a sliding verification code based on React + Canvas from zero, and publish it to NPM for others to use. Of course, if you prefer the vUE development method, don’t worry, the design ideas and ideas in this article are universal, if you want to learn how to package VUE components and publish them to NPM, you can also refer to my previous article: From Zero to one to develop a component library based on VUE.

Here’s what we can learn from this project:

  • The basic ideas and skills of front-end component design
  • Canvas basic knowledge and use
  • React hooks basic knowledge and use
  • Basic design principle of sliding verification code
  • How to package an extensible sliding captcha component
  • How to build component documentation using Dumi
  • How do I release my first NPM component package

If you are interested in any of the above, this article will inspire you.

Results demonstrate

Sliding verification component basic usage and technical implementation

The figure above is a demonstration of the implementation of the sliding validation component, but of course there are many configuration options to choose from to support more customized scenarios. Next, I will first introduce how to install and use the verification code plug-in, so that you have an intuitive experience, and then I will introduce the implementation of sliding verification code in detail, if you have a certain technical basis, you can also jump to the technical implementation part.

The basic use

Because the react-Slider-Vertify component has been published on NPM, you can install and use it as follows:

  1. The installation
# 或者 yarn add @alex_xu/react-slider-vertify
npm i @alex_xu/react-slider-vertify -S
Copy the code
  1. use
import React from 'react';
import { Vertify } from '@alex_xu/react-slider-vertify';

export default() = > {return <Vertify 
            width={320}
            height={160}
            onSuccess={()= > alert('success')} 
            onFail={() => alert('fail')} 
            onRefresh={() => alert('refresh')} 
        />
};
Copy the code

After the above two steps we can easily use the sliding captcha component, isn’t it easy?

Of course, I’ve also exposed a lot of configurable properties to give you more control over the component. For reference:

The technical implementation

Before doing this project, I also studied some knowledge of sliding verification code and existing technical solutions, and gained a lot. Next, I will introduce how to implement and encapsulate sliding captcha components with React. If you have better ideas and suggestions, please feel free to let me know in the comments section.

1. Ideas and skills of component design

Everyone has their own way and style of designing components, but the ultimate goal is to design components more elegantly. Here I outline the design metrics for elegant components:

  • Readability (uniform and clear code format, complete annotation, well-structured code structure, proper use of programming paradigm)

  • Availability (code functional integrity, compatibility in different scenarios, business logic coverage)

  • Reusability (code can be reused well by other business modules)

  • Maintainability (code is easy to maintain and extend, with some downward/upward compatibility)

  • A high performance

The above is my own design components to consider the indicators, you can refer to.

We also need to clear before another design components needs, take the sliding authentication code component, for example, we need to know its usage scenarios (used to login to the registration, activities, BBS, SMS riskier business scenarios, such as man-machine authentication service) and demand (interactive logic, in what way to verify, exposed to which attributes).

The above is a general component development requirement that I sorted out. Before developing specific components, if we encounter complex business logic, we can also list each implementation step and then implement it one by one, which helps to organize our thinking and make development more efficient.

2. Basic implementation principles of sliding verification codes

After introducing component design ideas and requirements analysis, let’s look at the implementation principle of sliding verification code.

We all know that the main purpose of captcha design is to prevent machines from illegally and violently invading our applications. The core problem is to determine who is operating the application (human or machine), so the usual solution is random identification.

As you can see in the figure above, the verification is successful only if the user manually drags the slider to the corresponding hollowed-out area, which is randomly located (randomness test is implemented in the front end for now, it is safer to return the position and image through the back end).

Based on the above analysis, we can draw a basic sliding verification code design schematic:

Let’s encapsulate the extensible sliding captcha component together.

3. Package an extensible sliding captcha component

In my usual style of component development, I first write the basic framework of the component based on the requirements:

import React, { useRef, useState, useEffect, ReactNode } from 'react';

interface IVertifyProp {
    / * * *@description   Canvas width *@default       320 * /
    width:number./ * * *@description   Canvas highly *@default       160 * /
    height:number./ * * *@description   Slider side length *@default       42 * /
     l:number./ * * *@description   Slider radius *@default       9 * /
      r:number./ * * *@description   Visible or not@default       true* /
      visible:boolean./ * * *@description   Slider text *@default       Slide right to fill the puzzle */
      text:string | ReactNode,
      / * * *@description   Refresh button icon, is the URL address of icon *@default       - * /
       refreshIcon:string./ * * *@description   The url used to get a random image *@default       https://picsum.photos/${id}/${width}/${height}, see https://picsum.photos/, only need to implement similar interface */
       imgUrl:string./ * * *@description   Verify successful callback *@default       ():void => {}
     */
    onSuccess:VoidFunction, 
    / * * *@description   Validation failed callback *@default       ():void => {}
     */
    onFail:VoidFunction, 
    / * * *@description   Refresh callback *@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 <div className="vertifyWrap">
        <div className="canvasArea">
            <canvas width={width} height={height}></canvas>
            <canvas className="block" width={width} height={height}></canvas>
        </div>
        <div className={sliderClass}>
            <div className="sliderMask">
                <div className="slider">
                    <div className="sliderIcon">&rarr;</div>
                </div>
            </div>
            <div className="sliderText">{ textTip }</div>
        </div>
        <div className="refreshIcon" onClick={handleRefresh}></div>
        <div className="loadingContainer">
            <div className="loadingIcon"></div>
            <span>Loading in...</span>
        </div>
    </div>
 }
Copy the code

This is the basic structure of our component. Component properties can be seen in the code at a glance, which is a benefit of having the requirements sorted out in advance so that we can think more clearly when writing components. After writing the basic CSS styles, the interface looks like this:

Next we need to implement the following core functions:

  • Hollow out effectcanvasImage to realize
  • Hollow out designcanvasimplementation
  • Slider movement and validation logic implementation

The above description may seem abstract, but let me draw a picture to illustrate it:

Because the component implementation uses the React hooks entirely, please refer to my previous article if you are not familiar with them:

  • 8 frequently used custom hooks by hand in 10 minutes

1. Hollow out effectcanvasThe picture

Before starting coding, we need to have a basic understanding of canvas. It is suggested that those who are not familiar with it can refer to the efficient Canvas learning document: Canvas of MDN.

It can be seen from the above figure that the first problem to be solved is how to draw irregular graphics with canvas. Here I will simply draw a sketch:

We just need to use the path API provided by canvas to draw the path shown above and fill the path with any translucent color. We suggest that you are not familiar with the following API:

  • BeginPath () starts path drawing
  • MoveTo () moves the pen to the specified point
  • Arc () Draws an arc
  • Draw a line lineTo ()
  • Stroke (stroke)
  • The fill ()
  • Clip () Cuts the path

The implementation method is as follows:

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 is a Boolean. True, counterclockwise, otherwise clockwise
    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'
    // Determine whether to fill or cut. Cut is mainly used to generate pattern sliders
    operation === 'fill'? ctx.fill() : ctx.clip()
}
Copy the code

This implementation is also a reference to yield’s native JS implementation, but the addition is the Canvas’s globalCompositeOperation property, whose main purpose is to set how to draw a source (new) image onto the target (existing) image.

  • Source image = the drawing we intend to place on the canvas

  • Target image = the drawing we have placed on the canvas

Here’s a graphic example from the W3C:

This property is set so that the hollow shape is not affected by the background image and overlays it. As follows:

Next we just need to draw the image onto the canvas:

const canvasCtx = canvasRef.current.getContext('2d')
// Draw hollow shapes
drawPath(canvasCtx, 50.50.'fill')

// Draw the image
canvasCtx.drawImage(img, 0.0, width, height)
Copy the code

As for how to generate random images and random positions, the implementation is also very simple, and the front-end implementation is math.random.

2. Realize hollow patternscanvas

The hollow shape is realized above, so the hollow pattern is similar, we just need to clip() the image into the shape mask, and place the hollow pattern on the left side of the canvas. The code is as follows:

const blockCtx = blockRef.current.getContext('2d')
drawPath(blockCtx, 50.50.'clip')
blockCtx.drawImage(img, 0.0, width, height)

// Extract the pattern slider and place it on the far left
const y1 = 50 - r * 2 - 1
const ImageData = blockCtx.getImageData(xRef.current - 3, y1, L, L)
// Adjust the slider canvas width
blockRef.current.width = L
blockCtx.putImageData(ImageData, 0, y1)
Copy the code

Above code we use getImageData and putImageData, these two apis are mainly used to get canvas scene pixel data and write the scene pixel data. The results are as follows:

3. Implement slider movement and verification logic

The solution to realize slider movement is also relatively simple, we just need to use the event event of the mouse:

  • onMouseDown
  • onMouseMove
  • onMouseUp

The above is a simple schematic diagram, the specific implementation code is as follows:

const handleDragMove = (e) = > {
    if(! isMouseDownRef.current)return false
    e.preventDefault()
    // To support mobile, use 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'
}
Copy the code

Of course, we also need to listen for events after the drag stops to determine whether the validation was successful and to bury successful and failed callbacks. The code is as follows:

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')
        // Custom callback function after success
        typeof onSuccess === 'function' && onSuccess()
      } else {
        // Verification failed, refresh and reset
        setSliderClass('sliderContainer sliderContainer_fail')
        setTextTip('Please try again') 
        reset()
      }
    } else {
      setSliderClass('sliderContainer sliderContainer_fail')
      // Custom callback function after failure
      typeof onFail === 'function' && onFail()
      setTimeout(reset.bind(this), 1000)}}Copy the code

The results are as follows:

Of course, there are still some details that need to be optimized. Here is a complete code on Github, you can refer to it. If you want to contribute to this component, you can also make an issue at any time.

4. How to use Dumi to build component documents

To make components better understood and used by others, we can build component documentation. As a front-end coder who loves open source, writing component documentation is also a good development habit. Let’s also write component documentation for react-Slider-Vertify. Here I’m using Dumi to build component documentation, but you can also use other options (storybook, for example). Let’s take a look at the effect after construction:

The Dumi build component documentation is very simple, so here’s how to install and use it.

  1. The installation
# or $yarn create @umijs/dumi-lib $NPX @umijs/create-dumi-lib --site # or $YARN create @umijs/dumi-lib --siteCopy the code
  1. Run locally
npm run dev
# or
yarn dev
Copy the code
  1. Written document

Dumi contract defines the location and way of document compilation, and there is a specific introduction on its official website. Here is a simple component directory structure diagram built by Dumi:

We can write the home and boot pages of the component library documentation under Docs, and use index.md to write the usage documentation of the component itself under a single component folder. Of course, the whole process is very simple. Here is an example of the document:

In this way dumi can help us automatically render a component usage document. If you want to learn more about component documentation, you can also learn about it on Dumi’s website.

5. Release your first NPM package

The final issue is component publishing. Before many friends ask me how to publish my own component to NPM for more people to use, there are many materials to learn this knowledge online, so today I will use the sliding verification code @alex_xu/ react-Slider-vertify example, and make a simple introduction.

  1. To have anpmAccount and login

If you don’t have an NPM account before, you can register one on the NPM website and log in once using the familiar IDE terminal:

npm login
Copy the code

After entering the user name and password, we can publish the package from the command line:

npm publish --access public
Copy the code

The reason why the directive is followed by the public parameter is to avoid permission problems that lead to component package publishing failure. To save trouble, we can also configure the publishing command into package.json, which will be automatically published after the component is packaged:

{
    "scripts": {
        "start": "dumi dev"."release": "npm run build && npm publish --access public",}}Copy the code

This way we can easily publish components to NPM for others to use! I also open source many component libraries before, if you have any questions about the details of component packaging and construction process, you can also refer to my previous open source project solution. Release to NPM:

The last

If you are interested in visual scaffolding or low code/zero code, please refer to my previous articles or share your thoughts in the comments section, and explore the real technology of the front end.

Github: React-Slider-Vertify launches: Github open Source project public account: Interesting front-end

More recommended

  • How to design a visual platform component store?
  • Build engine from zero design visualization large screen
  • Build desktop visual editor Dooring from zero using Electron
  • (low code) Visual construction platform data source design analysis
  • Build a PC page editor pc-dooring from scratch
  • How to build building blocks to quickly develop H5 pages?