在Android和ios两头已经使用的滑块验证码框架还未适配鸿蒙版,于是需要自己去实现类似如下的滑块验证码:
那么实现这样的验证码重要涉及到几个内容:
1、自界说弹窗
2、base64图片转换
3、滑动组件与滑块的联动,以及横移间隔转换等
自界说弹窗:
自界说一个可导出的弹窗组件CustomDialog,最重要是使用 @CustomDialog 修饰符。
- @CustomDialog
- export struct BlockPuzzleDialog {
- phoneNum: number | string = ''
- controller: CustomDialogController = new CustomDialogController({
- builder: BlockPuzzleDialog({}),
- })
- build() {
- Column(){
-
- }
- }
-
-
- // 验证码校验回调给使用页面
- blockCheckCallback: (token: string) => void = (token: string) => {
- }
- }
复制代码 在使用页面创建构造器与弹窗绑定
- @Entry
- @Component
- struct LoginPage {
- dialogController: CustomDialogController = new CustomDialogController({
- builder: BlockPuzzleDialog({
- phoneNum: this.phoneNum, blockCheckCallback: (token: string) => {
- this.blockPuzzleSuccessCallback(token)
- }
- }),
- autoCancel: false,//弹窗是否自动取消
- alignment: DialogAlignment.Center,// 弹窗位置
- cornerRadius: 8,
- width: '90%'// 弹窗宽度
- })
-
- build(){
- ...
- }
- }
复制代码 弹窗UI组件的实现:核心组件就一个预先挖孔的底图上面叠加滑块图片再加上一个slider组件
- build(){
- ......
- Stack() {
- Image(this.coverUri).width('100%').margin({ top: 10 }).objectFit(ImageFit.Auto).onComplete((event) => {
- this.scaleRatio = event!!.componentWidth / event?.width!!
- })
- Image(this.blockUri)
- .width(this.blockW + "px")
- .height(this.blockH + "px")
- .margin({ top: 10 })
- .objectFit(ImageFit.Auto)
- .onComplete((event) => {
- this.blockW = event?.width!! * this.scaleRatio
- this.blockH = event?.height!! * this.scaleRatio
- this.slideMax = Const.mWidth * 0.9 - 24 - px2vp(this.blockW)
- })
- .translate({ x: this.bolckTranslateX + "px" })
- this.loading()
- }.width('100%').alignContent(Alignment.Start)
- RelativeContainer() {
- Text('向右拖动滑动填充拼图')
- .fontSize(18)
- .fontColor($r('app.color.C_BEBEC6'))
- .id('blockTip')
- .alignRules({
- "top": {
- "anchor": "slider",
- "align": VerticalAlign.Top
- },
- "bottom": {
- "anchor": "slider",
- "align": VerticalAlign.Bottom
- },
- "left": {
- "anchor": "slider",
- "align": HorizontalAlign.Start
- },
- "right": {
- "anchor": "slider",
- "align": HorizontalAlign.End
- },
- })
- .textAlign(TextAlign.Center)
- Slider({
- style: SliderStyle.InSet,
- value: $$this.sliderValue,
- step: 1,
- max: vp2px(this.slideMax)
- })
- .trackColor(this.sliderConfig.trackColor)
- .selectedColor(this.sliderConfig.selectedColor)
- .blockSize({ height: 40, width: 44 })
- .blockStyle({
- type: SliderBlockType.IMAGE,
- image: this.sliderConfig.blockImg
- })// .sliderInteractionMode(SliderInteraction.SLIDE_ONLY)
- .trackBorderRadius(Const.BORDER_RADIUS_4)
- .trackThickness(40)
- .width('100%')
- .onChange((value: number, mode: SliderChangeMode) => {
- // this.bolckTranslateX = this.slideMax * (value / this.slideMax)
- this.bolckTranslateX = value
- console.info('滑块滑动:滑块滑动数值==' + value + " 图片位移==" + this.bolckTranslateX)
- if (mode == SliderChangeMode.End) {
- // this.sliderValue = value
- let point = new Point()
- point.x = parseFloat((this.bolckTranslateX / this.scaleRatio).toFixed(0))
- console.info('滑动结束:滑动数值 this.sliderValue==' + this.sliderValue + " this.bolckTranslateX==" +
- this.bolckTranslateX + " 转像素==" + point.x)
- this.checkCaptcha(point)
- }
- })
- .id('slider')
- }.width('100%').height(40).margin({ top: 10 })
- ......
- }
复制代码 滑块图片translate的值就是Slider组件的滑动值。使用
- this.dialogController.open() 弹窗
复制代码 Base64图片的下载与转换
- aboutToAppear(): void {
- this.getSlideImage()
- }
- ......
- // 获取底图和滑块图片的base64数据并保存到本地,同时获取到滑块校验相关信息。
- getSlideImage() {
- this.sliderConfig.showLoading = true
- HttpUtil.getData<BlockResult>(Const.URL_BLOCK_IMG).then((result) => {
- if (result !== undefined && result !== null) {
- this.blockResult = result
- this.coverBase64 = this.blockResult.repData?.originalImageBase64!!
- this.blockBase64 = this.blockResult.repData?.jigsawImageBase64!!
- console.info("滑块:获取到base64 ==" + this.coverBase64)
- let coverName = "coverBase64_" + Date.now().toString() + ".png"
- let blockName = "blockBase64_" + Date.now().toString() + ".png"
- this.coverPath = this.context.filesDir + "/temp/" + coverName;
- this.blockPath = this.context.filesDir + "/temp/" + blockName;
- this.coverUri =
- Utils.saveBase64Image(this.coverBase64, this.context, coverName)
- this.blockUri =
- Utils.saveBase64Image(this.blockBase64, this.context, blockName)
- this.sliderConfig.showLoading = false
- this.reset()
- }
- })
- }
复制代码 可以参考官网示例 通过buffer.from的方法,将base64编码格式的字符串创建为新的Buffer对象,接着用fileIo.writeSync方法将转换好的Buffer对象写入文件。
- let context = getContext(this) as common.UIAbilityContext;
- let filesDir = context.filesDir;
-
- // data为需要转换的base64字符串,返回沙箱路径uri
- export async function writeFile(data: string): Promise<string> {
- let uri = ''
- try {
- let filePath = filesDir + "/1.png";
- uri = fileUri.getUriFromPath(filePath);
- let file = fileIo.openSync(filePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
- console.info("file fd: " + file.fd);
- const reg = new RegExp("data:image/\\w+;base64,")
- const base64 = data.replace(reg, "");
- console.log("base64flag", base64)
- const dataBuffer = buffer.from(base64, 'base64')
- let writeLen = fileIo.writeSync(file.fd, dataBuffer.buffer);
- hilog.info(0xA0c0d0,'uri',uri)
- fileIo.closeSync(file);
- }
- catch (Error) {
- hilog.error(0xA0c0d0,'Error',Error.code)
- }
- return uri;
- }
复制代码 当然你还可以直接将Base64转换成PiexlMap.先将base64字符串剖析成arraybuffer,然后使用这个arraybuffer构建新PixelMap,需要注意的是,使用decodeSync对base64字符串解码时,传入的base64字符串不能有'data:image/jpeg;base64,'这样的前缀。
- import CommonConstants from '../common/constants/CommonContants';
- import { util } from '@kit.ArkTS';
- import { image } from '@kit.ImageKit';
- @Entry
- @Component
- struct Index {
- @State message: string = 'Base64ToPixelMap';
- private base64: string = CommonConstants.Image_Base64_String; // 该变量为图片的base64格式字符串
- @State private pixelMap: PixelMap | null = null;
- build() {
- Row() {
- Column() {
- Text(this.message)
- .fontSize(50)
- .fontWeight(FontWeight.Bold)
- .onClick(async () => {
- let helper = new util.Base64Helper();
- let buffer: ArrayBuffer = helper.decodeSync(this.base64, util.Type.MIME).buffer as ArrayBuffer;
- let imageSource = image.createImageSource(buffer);
- let opts: image.DecodingOptions = { editable: true };
- this.pixelMap = await imageSource.createPixelMap(opts);
- })
- Image(this.pixelMap)
- .width(200).height(200).margin(15)
- }
- .width('100%')
- }
- .height('100%')
- }
- }
复制代码 将得到的图片当地保存地点uri大概转换成的piexlMap设置给底图和滑动图片。
滑动值校验
上面已经说过,滑块的移动值就是Slider滑动值。其中slider 步长设置为1,滑动的最大值slideMax=底图的宽度-滑块图片的宽度。这样滑动值转换更方便,联动结果也更好。这里注意下 底图在填满控件的时间有肯定的缩放,滑动图片组件也需要按照这个缩放比例设置宽高。
- step: 1,
- max: vp2px(this.slideMax)
复制代码 最后在slider的onchange回调中校验滑动值是不是正确,注意滑动值要除以上面的底图缩放比例。
将滑动值加上校验token传给校验接口获取校验结果。
- .onChange((value: number, mode: SliderChangeMode) => {
-
- this.bolckTranslateX = value
- console.info('滑块滑动:滑块滑动数值==' + value + " 图片位移==" + this.bolckTranslateX)
- if (mode == SliderChangeMode.End) {
- // this.sliderValue = value
- let point = new Point()
- point.x = parseFloat((this.bolckTranslateX / this.scaleRatio).toFixed(0))
- console.info('滑动结束:滑动数值 this.sliderValue==' + this.sliderValue + " this.bolckTranslateX==" +
- this.bolckTranslateX + " 转像素==" + point.x)
- this.checkCaptcha(point)
- }
- })
复制代码- checkFail() {
- this.sliderConfig.showLoading = false
- this.sliderConfig.trackColor = $r('app.color.C_0DF32222')
- this.sliderConfig.selectedColor = $r('app.color.C_F32222')
- this.sliderConfig.blockImg = $r('app.media.drag_btn_error')
- this.sliderValue = 0
- this.bolckTranslateX = 0
- setTimeout(() => {
- // 删掉滑块图片
- FileUtil.delFile(this.coverPath)
- FileUtil.delFile(this.blockPath)
- this.getSlideImage()
- }, 300)
- }
- checkSuccess() {
- this.sliderConfig.showLoading = false
- this.sliderConfig.trackColor = $r('app.color.C_0D1264E0')
- this.sliderConfig.selectedColor = $r('app.color.C_1264E0')
- this.sliderConfig.blockImg = $r('app.media.drag_btn_success')
- setTimeout(() => {
- this.controller.close()
- // 删掉滑块图片
- FileUtil.delFile(this.coverPath)
- FileUtil.delFile(this.blockPath)
- if (this.blockCheckCallback !== undefined) {
- this.blockCheckCallback(this.blockResult?.token!!)
- }
- }, 300)
- }
复制代码 调用刚刚界说的回调方法将校验结果回调给登录页面this.blockCheckCallback(this.blockResult?.token!!)
至此导致流程已结束,当然还有一些细节需要自己根据业务实现。最后完成结果如下:
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |