#yew #checkcode #ui-controller #canvas2d

wasm-yew-canvas-checkcode

纯前端的,运行于 WASM 中的,基于 Canvas 2D 绘图引擎的,Yew.rs 图形验证码控件

1 unstable release

0.1.0 Dec 5, 2023

#432 in WebAssembly

MIT license

28KB
375 lines

wasm-yew-canvas-checkcode

纯前端yew.rs图形验证码控件。不同于从后端拉取图形验证码图片,该控件以更“轻量级”的技术手段抑制UI用户重复地连续提交表单。

工作原理

  1. 借助rand crate,本地生成随机字符串。

  2. 经由web_sys crate,操作Canvas 2D平面渲染引擎,生成图形验证码图片。在图片中的

    1. 背景色
    2. 背景星型图案
    3. 每个星型图案的角数(3 ~ 8)、位置、旋转、颜色、密度
    4. 每位验证字符的位置、旋转、颜色,间距

    都是被即时演算出来的。

  3. 通过被传入控件的【回调函数】on_check_code_change(CheckCode),将被生成的随机字符串验证码返回给父控件。

crate导出项清单

  1. ::wasm_yew_canvas_checkcode::CanvasCheckCode图形验证码控件自身。在渲染函数中,其被记为<CanvasCheckCode>
  2. ::wasm_yew_canvas_checkcode::Message控件的枚举类内部状态集
  3. ::wasm_yew_canvas_checkcode::Props控件的输入参数属性集
  4. ::wasm_yew_canvas_checkcode::CheckCode包装了图形验证码字符串的枚举类
    • CheckCode::Initialize(String)代表控件初始化过程生成的图形验证码

    • CheckCode::Update(String)代表由

      • UI鼠标点击事件或
      • 父控件程序触发

      生成的图形验证码

控件输入参数列表

  1. width: f64
    • 可选参数
    • 单位:像素
    • 图形验证码canvas画布的宽度
    • 默认值优先次序
      1. css样式表设置的宽度
      2. 缺省值150
  2. height: f64
    • 可选参数
    • 单位:像素
    • 图形验证码canvas画布的高度
    • 默认值优先次序
      1. css样式表设置的宽度
      2. 缺省值50
  3. star_size: f64
    • 可选参数
    • 单位:像素
    • 背景随机星型图案的大小尺寸。因为星型图案的BBox是正方形,所以这里仅只需要设置一个值。
    • 默认值7
  4. star_count: u8
    1. 可选参数
    2. 单位:个
    3. 背景随机星型图案的最多个数。
    4. 默认值25。在图形渲染过程中,虽然做了星型图形的BBox碰撞测试,但若星型图案太多,还是会出现星型图案的重叠现象。
  5. font_size: f64
    • 可选参数
    • 单位:像素
    • 验证码单个字符的最大尺寸
    • 默认值22
  6. check_code_len: u8
    1. 可选参数
    2. 单位:个
    3. 验证码的字符个数
    4. 默认值5。字符太多也会出现重叠现象,虽然程序也对单个验证码字符的BBox做过碰撞测试了。
  7. on_check_code_change: Callback<CheckCode>
    1. 必填参数
    2. 类型:事件回调函数。
      1. 形参CheckCode是枚举值
        1. CheckCode::Initialize(String)代表控件初始化过程生成的图形验证码
        2. CheckCode::Update(String)代表由UI点击事件或程序触发生成的图形验证码
      2. 没有返回值
    3. 功能:向父控制反馈最新生成的验证码字符串。
  8. reversed_hook: Callback<Scope<CanvasCheckCode>>
    1. 可选参数

    2. 类型:事件回调函数。

      1. 形参Scope<CanvasCheckCode>代表<CanvasCheckCode>控件的【作用域】对象。
      2. 没有返回值
    3. 功能:向父控件传递<CanvasCheckCode>【作用域】对象(复本),以允许从控件外部程序地触发新图形验证码的生成操作。比如,

      1. 首先,由Rc<RefCell<Option<Scope<CanvasCheckCode>>>>字段值,缓存被上传的<CanvasCheckCode>【作用域】对象复本。
      2. 再,在fn view(..) -> bool生命周期函数内,程序地触发<CanvasCheckCode>子控件重新生成图形验证码。
      self.check_code_scope.borrow().as_ref().map(|scope| {
         scope.send_message(CanvasCheckCodeMessage::UpdateCheckCode);
      });
      
    4. 缺省值代表什么都不做

总结,最后两个控件输入参数都是回调函数。其功能都是自下而向,从<CanvasCheckCode>向父控件传递返回值的

  1. on_check_code_change返回最新的图形验证码字符串。
  2. reversed_hook返回程序触发生成新图形验证码的“操作句柄”。

控件输出回调函数钩子

<CanvasCheckCode>控件以回调函数的方式向父控件回传

  1. 最新被生成的图形验证码字符串 —— 用以校对UI用户敲入的图形验证码字符串是否正确。
  2. 自身作用域对象Scope<CanvasCheckCode> —— 用以程序地从父控件触发新图形验证码的生成。

从父控件获取最新图形验证码字符串

回调函数签名on_check_code_change: |check_code: CheckCode| -> () { .. }

  • 回调函数的返回值是unit type
  • 形参是枚举类CheckCode
    • CheckCode::Initialize(String)代表控件初始化过程生成的图形验证码

    • CheckCode::Update(String)代表由

      • UI鼠标点击事件或
      • 父控件程序触发

      生成的图形验证码

例程

use ::wasm_yew_canvas_checkcode::CheckCode;
//
yew::props![CanvasCheckCodeProps {
   on_check_code_change: |check_code| {
      let check_code = match check_code {
            CheckCode::Initialize(value) => value,
            CheckCode::Update(value) => value,
      };
      console::info!("从父组件收到的校验码", check_code);
   }
}]

从父控件程序地刷新图形验证码

回调函数签名reversed_hook: |my_scope: Scope<CanvasCheckCode>| -> () { .. }

  • 回调函数的返回值是unit type
  • 形参是Scope<CanvasCheckCode>

首先,获取<CanvasCheckCode>子控件的作用域对象Scope<CanvasCheckCode>

然后,在父控件的结构体字段child1_scope: Rc<RefCell<Option<Scope<CanvasCheckCode>>>>内缓存该作用域对象。

use ::wasm_yew_canvas_checkcode::CheckCode;
//
yew::props![CanvasCheckCodeProps {
   reversed_hook: |my_scope| {
      // 在父控件中,缓存子控件的`scope`对象
      child1_scope.borrow_mut().replace(my_scope);
   }
}]

最后,在父控件的fn update( .. ) -> bool生命周期函数内,修改子控件<CanvasCheckCode>的状态集以触发新的图型验证码被生成。

fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
   let props = ctx.props();
   match msg {
      Message::SubmitForm => {
         self.child1_scope.borrow().as_ref().map(|child1_scope| { // 从父控件,触发子控件刷新图形验证码
            child1_scope.send_message(CanvasCheckCodeMessage::UpdateCheckCode);
         });
         return false;
      }
      _ => ()
   }
   true
}

附赠两个例程

crate以【**(有脸)**集成测试】的方式,呈送两个例程

仅图形验证码控制简单例程

例程文件:tests\simple.rs

启动命令行指令:wasm-pack test --chrome --test=simple

演示内容:

  1. <CanvasCheckCode>控件被做为整个wasm-webapp的根组件直接加到DOM流中。
  2. 点击<CanvasCheckCode>控件会刷新图形验证码字符串

例程1

登录表单半成品(实战)例程

例程文件:tests\form.rs

启动命令行指令:wasm-pack test --chrome --test=form

演示内容:

  1. wasm-webapp的根组件是一个【用户名/密码/图形验证码】的常规网页登录表单。
  2. 集成测试执行流被阻塞。并且,仅当UI用户录入了正常的图形验证码才能开始表单验证。
  3. 点击<CanvasCheckCode>控件会刷新图形验证码字符串,同时置空图形验证码的文本输入框。
  4. 点击【登录】按钮,会比较从<CanvasCheckCode>控件回传给父控件的图形验证码“本尊”与用户录入的图形验证码字符串是否一致。
  5. 若两个图形验证码字符串一致,则集成测试成功通过。
  6. 否则,集成测试失败

image

Dependencies

~11–15MB
~265K SLoC