php+vue3实现点选验证码

buildadmin 中的点选验证码实现
验证码类

<?phpnamespace ba;use Throwable;
use think\facade\Db;
use think\facade\Lang;
use think\facade\Config;/*** 点选文字验证码类*/
class ClickCaptcha
{/*** 验证码过期时间(s)* @var int*/private int $expire = 600;/*** 可以使用的背景图片路径* @var array*/private array $bgPaths = ['static/images/captcha/click/bgs/1.png','static/images/captcha/click/bgs/2.png','static/images/captcha/click/bgs/3.png',];/*** 可以使用的字体文件路径* @var array*/private array $fontPaths = ['static/fonts/zhttfs/SourceHanSansCN-Normal.ttf',];/*** 验证点 Icon 映射表* @var array*/private array $iconDict = ['aeroplane' => '飞机','apple'     => '苹果','banana'    => '香蕉','bell'      => '铃铛','bicycle'   => '自行车','bird'      => '小鸟','bomb'      => '炸弹','butterfly' => '蝴蝶','candy'     => '糖果','crab'      => '螃蟹','cup'       => '杯子','dolphin'   => '海豚','fire'      => '火','guitar'    => '吉他','hexagon'   => '六角形','pear'      => '梨','rocket'    => '火箭','sailboat'  => '帆船','snowflake' => '雪花','wolf head' => '狼头',];/*** 配置* @var array*/private array $config = [// 透明度'alpha' => 36,// 中文字符集'zhSet' => '们以我到他会作时要动国产的是工就年阶义发成部民可出能方进在和有大这主中为来分生对于学级地用同行面说种过命度革而多子后自社加小机也经力线本电高量长党得实家定深法表着水理化争现所起政好十战无农使前等反体合斗路图把结第里正新开论之物从当两些还天资事队点育重其思与间内去因件利相由压员气业代全组数果期导平各基或月然如应形想制心样都向变关问比展那它最及外没看治提五解系林者米群头意只明四道马认次文通但条较克又公孔领军流接席位情运器并飞原油放立题质指建区验活众很教决特此常石强极已根共直团统式转别造切九你取西持总料连任志观调么山程百报更见必真保热委手改管处己将修支识象先老光专什六型具示复安带每东增则完风回南劳轮科北打积车计给节做务被整联步类集号列温装即毫知轴研单坚据速防史拉世设达尔场织历花求传断况采精金界品判参层止边清至万确究书术状须离再目海权且青才证低越际八试规斯近注办布门铁需走议县兵固除般引齿胜细影济白格效置推空配叶率述今选养德话查差半敌始片施响收华觉备名红续均药标记难存测身紧液派准斤角降维板许破述技消底床田势端感往神便贺村构照容非亚磨族段算适讲按值美态易彪服早班麦削信排台声该击素张密害侯何树肥继右属市严径螺检左页抗苏显苦英快称坏移巴材省黑武培著河帝仅针怎植京助升王眼她抓苗副杂普谈围食源例致酸旧却充足短划剂宣环落首尺波承粉践府鱼随考刻靠够满夫失包住促枝局菌杆周护岩师举曲春元超负砂封换太模贫减阳扬江析亩木言球朝医校古呢稻宋听唯输滑站另卫字鼓刚写刘微略范供阿块某功友限项余倒卷创律雨让骨远帮初皮播优占圈伟季训控激找叫云互跟粮粒母练塞钢顶策双留误础阻故寸盾晚丝女散焊功株亲院冷彻弹错散商视艺版烈零室轻倍缺厘泵察绝富城冲喷壤简否柱李望盘磁雄似困巩益洲脱投送侧润盖挥距触星松送获兴独官混纪依未突架宽冬章偏纹吃执阀矿寨责熟稳夺硬价努翻奇甲预职评读背协损棉侵灰虽矛厚罗泥辟告箱掌氧恩爱停曾溶营终纲孟钱待尽俄缩沙退陈讨奋械载胞哪旋征槽倒握担仍呀鲜吧卡粗介钻逐弱脚怕盐末丰雾冠丙街莱贝辐肠付吉渗瑞惊顿挤秒悬姆森糖圣凹陶词迟蚕亿矩康遵牧遭幅园腔订香肉弟屋敏恢忘编印蜂急拿扩飞露核缘游振操央伍域甚迅辉异序免纸夜乡久隶念兰映沟乙吗儒汽磷艰晶埃燃欢铁补咱芽永瓦倾阵碳演威附牙芽永瓦斜灌欧献顺猪洋腐请透司括脉宜笑若尾束壮暴企菜穗楚汉愈绿拖牛份染既秋遍锻玉夏疗尖井费州访吹荣铜沿替滚客召旱悟刺脑措贯藏敢令隙炉壳硫煤迎铸粘探临薄旬善福纵择礼愿伏残雷延烟句纯渐耕跑泽慢栽鲁赤繁境潮横掉锥希池败船假亮谓托伙哲怀摆贡呈劲财仪沉炼麻祖息车穿货销齐鼠抽画饲龙库守筑房歌寒喜哥洗蚀废纳腹乎录镜脂庄擦险赞钟摇典柄辩竹谷乱虚桥奥伯赶垂途额壁网截野遗静谋弄挂课镇妄盛耐扎虑键归符庆聚绕摩忙舞遇索顾胶羊湖钉仁音迹碎伸灯避泛答勇频皇柳哈揭甘诺概宪浓岛袭谁洪谢炮浇斑讯懂灵蛋闭孩释巨徒私银伊景坦累匀霉杜乐勒隔弯绩招绍胡呼峰零柴簧午跳居尚秦稍追梁折耗碱殊岗挖氏刃剧堆赫荷胸衡勤膜篇登驻案刊秧缓凸役剪川雪链渔啦脸户洛孢勃盟买杨宗焦赛旗滤硅炭股坐蒸凝竟枪黎救冒暗洞犯筒您宋弧爆谬涂味津臂障褐陆啊健尊豆拔莫抵桑坡缝警挑冰柬嘴啥饭塑寄赵喊垫丹渡耳虎笔稀昆浪萨茶滴浅拥覆伦娘吨浸袖珠雌妈紫戏塔锤震岁貌洁剖牢锋疑霸闪埔猛诉刷忽闹乔唐漏闻沈熔氯荒茎男凡抢像浆旁玻亦忠唱蒙予纷捕锁尤乘乌智淡允叛畜俘摸锈扫毕璃宝芯爷鉴秘净蒋钙肩腾枯抛轨堂拌爸循诱祝励肯酒绳塘燥泡袋朗喂铝软渠颗惯贸综墙趋彼届墨碍启逆卸航衣孙龄岭休借',];/*** 构造方法* @param array $config 点击验证码配置* @throws Throwable*/public function __construct(array $config = []){$clickConfig  = Config::get('buildadmin.click_captcha');//这里会得配置文件中的数据合并//$clickConfig 中的配置是这样的//    'click_captcha'         => [// 模式:text=文字,icon=图标(若只有icon则适用于国际化站点)//'mode'           => ['text', 'icon'],// 长度//'length'         => 2,// 混淆点长度//'confuse_length' => 2,],$this->config = array_merge($clickConfig, $this->config, $config);// 清理过期的验证码Db::name('captcha')->where('expire_time', '<', time())->delete();}/*** 创建图形验证码* @param string $id 验证码ID,开发者自定义* @return array 返回验证码图片的base64编码和验证码文字信息*/public function creat(string $id): array{$imagePath  = Filesystem::fsFit(public_path() . $this->bgPaths[mt_rand(0, count($this->bgPaths) - 1)]);  //随机一个背景图片$fontPath   = Filesystem::fsFit(public_path() . $this->fontPaths[mt_rand(0, count($this->fontPaths) - 1)]);  //随机一个字体$randPoints = $this->randPoints($this->config['length'] + $this->config['confuse_length']);  //生成验证码的长度, 加上混肖点的长度相加$lang = Lang::getLangSet();foreach ($randPoints as $v) {$tmp['size'] = rand(15, 30);if (isset($this->iconDict[$v])) {// 图标$tmp['icon']   = true;$tmp['name']   = $v;$tmp['text']   = $lang == 'zh-cn' ? "<{$this->iconDict[$v]}>" : "<$v>";$iconInfo      = getimagesize(Filesystem::fsFit(public_path() . 'static/images/captcha/click/icons/' . $v . '.png'));$tmp['width']  = $iconInfo[0];   //$size = getimagesize($filename);  $size[0]: 图像的宽度 $size[1]: 图像的高度$tmp['height'] = $iconInfo[1];} else {// 字符串文本框宽度和长度$fontArea      = imagettfbbox($tmp['size'], 0, $fontPath, $v);$textWidth     = $fontArea[2] - $fontArea[0];  //得到文字的宽度$textHeight    = $fontArea[1] - $fontArea[7];   //得到文字的高度$tmp['icon']   = false;  //说明这个不是图片$tmp['text']   = $v;$tmp['width']  = $textWidth;        //文字的宽度$tmp['height'] = $textHeight;      //文字的高度}$textArr['text'][] = $tmp;}// 图片宽高和类型$imageInfo         = getimagesize($imagePath);$textArr['width']  = $imageInfo[0];  //$textArr 的宽度 是背景图宽度$textArr['height'] = $imageInfo[1];  //$testArr 的高度 是背景图高度// 随机生成验证点位置foreach ($textArr['text'] as &$v) {list($x, $y) = $this->randPosition($textArr['text'], $textArr['width'], $textArr['height'], $v['width'], $v['height'], $v['icon']);$v['x'] = $x;$v['y'] = $y;$text[] = $v['text'];  //这里的把生成的标记也按顺序 记录了下来}unset($v);;// 创建图片的实例$image = imagecreatefromstring(file_get_contents($imagePath));foreach ($textArr['text'] as $v) {if ($v['icon']) {$this->iconCover($image, $v);} else {//字体颜色$color = imagecolorallocatealpha($image, 239, 239, 234, 127 - intval($this->config['alpha'] * (127 / 100)));// 绘画文字imagettftext($image, $v['size'], 0, $v['x'], $v['y'], $color, $fontPath, $v['text']);}}$nowTime         = time();$textArr['text'] = array_splice($textArr['text'], 0, $this->config['length']);   //取了两个$text            = array_splice($text, 0, $this->config['length']);   //前两个的text ,用来返回给前端用的Db::name('captcha')->replace()->insert(['key'         => md5($id),'code'        => md5(implode(',', $text)),'captcha'     => json_encode($textArr, JSON_UNESCAPED_UNICODE),'create_time' => $nowTime,'expire_time' => $nowTime + $this->expire]);// 输出图片while (ob_get_level()) {ob_end_clean();}if (!ob_get_level()) ob_start();switch ($imageInfo[2]) {case 1:// GIFimagegif($image);$content = ob_get_clean();break;case 2:// JPGimagejpeg($image);$content = ob_get_clean();break;case 3:// PNGimagepng($image);$content = ob_get_clean();break;default:$content = '';break;}imagedestroy($image);return ['id'     => $id,'text'   => $text,'base64' => 'data:' . $imageInfo['mime'] . ';base64,' . base64_encode($content),'width'  => $textArr['width'],'height' => $textArr['height'],];}/*** 检查验证码* @param string $id    开发者自定义的验证码ID* @param string $info  验证信息* @param bool   $unset 验证成功是否删除验证码* @return bool* @throws Throwable*/public function check(string $id, string $info, bool $unset = true): bool{$key     = md5($id);$captcha = Db::name('captcha')->where('key', $key)->find();if ($captcha) {// 验证码过期if (time() > $captcha['expire_time']) {Db::name('captcha')->where('key', $key)->delete();return false;}$textArr = json_decode($captcha['captcha'], true);list($xy, $w, $h) = explode(';', $info);$xyArr = explode('-', $xy);//xyArr[0] 249,112    xyArr[1]47,68$xPro  = $w / $textArr['width'];// 宽度比例$yPro  = $h / $textArr['height'];// 高度比例foreach ($xyArr as $k => $v) {$xy = explode(',', $v);$x  = $xy[0];  //249$y  = $xy[1];   //112if ($x / $xPro < $textArr['text'][$k]['x'] || $x / $xPro > $textArr['text'][$k]['x'] + $textArr['text'][$k]['width']) {return false;}$phStart = $textArr['text'][$k]['icon'] ? $textArr['text'][$k]['y'] : $textArr['text'][$k]['y'] - $textArr['text'][$k]['height'];$phEnd   = $textArr['text'][$k]['icon'] ? $textArr['text'][$k]['y'] + $textArr['text'][$k]['height'] : $textArr['text'][$k]['y'];if ($y / $yPro < $phStart || $y / $yPro > $phEnd) {return false;}}if ($unset) Db::name('captcha')->where('key', $key)->delete();return true;} else {return false;}}/*** 绘制Icon*/protected function iconCover($bgImg, $iconImgData): void{$iconImage      = imagecreatefrompng(Filesystem::fsFit(public_path() . 'static/images/captcha/click/icons/' . $iconImgData['name'] . '.png'));$trueColorImage = imagecreatetruecolor($iconImgData['width'], $iconImgData['height']);imagecopy($trueColorImage, $bgImg, 0, 0, $iconImgData['x'], $iconImgData['y'], $iconImgData['width'], $iconImgData['height']);imagecopy($trueColorImage, $iconImage, 0, 0, 0, 0, $iconImgData['width'], $iconImgData['height']);imagecopymerge($bgImg, $trueColorImage, $iconImgData['x'], $iconImgData['y'], 0, 0, $iconImgData['width'], $iconImgData['height'], $this->config['alpha']);imagedestroy($iconImage);imagedestroy($trueColorImage);}/*** 随机生成验证点元素* @param int $length* @return array*/public function randPoints(int $length = 4): array{$arr = [];// 文字if (in_array('text', $this->config['mode'])) {for ($i = 0; $i < $length; $i++) {$arr[] = mb_substr($this->config['zhSet'], mt_rand(0, mb_strlen($this->config['zhSet'], 'utf-8') - 1), 1, 'utf-8');}}//这里生成了 4 个文字// 图标if (in_array('icon', $this->config['mode'])) {$icon = array_keys($this->iconDict); //得到所有的图片的 keyshuffle($icon);  //打乱key的顺序$icon = array_slice($icon, 0, $length);  //截取4个图片的key$arr  = array_merge($arr, $icon);  //把生成的 文字和图片的 数组合并}shuffle($arr); //打乱顺序return array_slice($arr, 0, $length);  //取出前4个}/*** 随机生成位置布局* @param array $textArr 点位数据* @param int   $imgW    图片宽度* @param int   $imgH    图片高度* @param int   $fontW   文字宽度* @param int   $fontH   文字高度* @param bool  $isIcon  是否是图标* @return array*/private function randPosition(array $textArr, int $imgW, int $imgH, int $fontW, int $fontH, bool $isIcon): array{$x = rand(0, $imgW - $fontW);$y = rand($fontH, $imgH - $fontH);// 碰撞验证if (!$this->checkPosition($textArr, $x, $y, $fontW, $fontH, $isIcon)) {$position = $this->randPosition($textArr, $imgW, $imgH, $fontW, $fontH, $isIcon);} else {$position = [$x, $y];}return $position;}/*** 碰撞验证* @param array $textArr 验证点数据* @param int   $x       x轴位置* @param int   $y       y轴位置* @param int   $w       验证点宽度* @param int   $h       验证点高度* @param bool  $isIcon  是否是图标* @return bool*/public function checkPosition(array $textArr, int $x, int $y, int $w, int $h, bool $isIcon): bool{$flag = true;foreach ($textArr as $v) {if (isset($v['x']) && isset($v['y'])) {$flagX     = false;$flagY     = false;$historyPw = $v['x'] + $v['width'];if (($x + $w) < $v['x'] || $x > $historyPw) {$flagX = true;}$currentPhStart = $isIcon ? $y : $y - $h;$currentPhEnd   = $isIcon ? $y + $v['height'] : $y;$historyPhStart = $v['icon'] ? $v['y'] : ($v['y'] - $v['height']);$historyPhEnd   = $v['icon'] ? ($v['y'] + $v['height']) : $v['y'];if ($currentPhEnd < $historyPhStart || $currentPhStart > $historyPhEnd) {$flagY = true;}if (!$flagX && !$flagY) {$flag = false;}}}return $flag;}
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
这里的知识点,和验证的时候,图片和文字的 x 坐标和 y 坐标的对比不一样是有关系的

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


前端代码
前端通过代码, 请求 后台的 验证码的 creat ,得到图片,并显示到前端页面

<template><div :id="uuid"><div class="ba-click-captcha" :class="props.class"><div v-if="state.loading" class="loading">{{ i18n.global.t('utils.Loading') }}</div><div v-else class="captcha-img-box"><imgclass="captcha-img"@click.prevent="onRecord($event)":src="state.captcha.base64":alt="i18n.global.t('validate.Captcha loading failed, please click refresh button')"/><spanv-for="(item, index) in state.xy":key="index"class="step"@click="onCancelRecord(index)":style="`left:${parseFloat(item.split(',')[0]) - 13}px;top:${parseFloat(item.split(',')[1]) - 13}px`">{{ index + 1 }}</span></div><div class="captcha-prompt" v-if="state.tip">{{ state.tip }}</div><div v-else class="captcha-prompt">{{ i18n.global.t('validate.Please click') }}<span v-for="(text, index) in state.captcha.text" :key="index" :class="state.xy.length > index ? 'clicaptcha-clicked' : ''">{{ text }}</span></div><div class="captcha-refresh-box"><div class="captcha-refresh-line captcha-refresh-line-l"></div><i class="fa fa-refresh captcha-refresh-btn" :title="i18n.global.t('Refresh')" @click="load"></i><div class="captcha-refresh-line captcha-refresh-line-r"></div></div></div><div class="ba-layout-shade" @click="onClose"></div></div>
</template><script setup lang="ts">
import { reactive, computed } from 'vue'
import { getCaptchaData, checkClickCaptcha } from '/@/api/common'
import { i18n } from '/@/lang'interface Props {uuid: stringcallback?: (captchaInfo: string) => voidclass?: stringunset?: booleanerror?: stringsuccess?: string
}const props = withDefaults(defineProps<Props>(), {uuid: '',callback: () => {},class: '',unset: false,error: i18n.global.t('validate.The correct area is not clicked, please try again!'),success: i18n.global.t('validate.Verification is successful!'),
})const state: {loading: booleanxy: string[]tip: stringcaptcha: {id: stringtext: stringbase64: stringwidth: numberheight: number}
} = reactive({loading: true,xy: [],tip: '',captcha: {id: '',text: '',base64: '',width: 350,height: 200,},
})const load = () => {state.loading = truegetCaptchaData(props.uuid).then((res) => {state.xy = []state.tip = ''state.loading = falsestate.captcha = res.data})
}const onRecord = (event: MouseEvent) => {if (state.xy.length < state.captcha.text.length) {state.xy.push(event.offsetX + ',' + event.offsetY)if (state.xy.length == state.captcha.text.length) {const captchaInfo = [state.xy.join('-'), (event.target as HTMLImageElement).width, (event.target as HTMLImageElement).height].join(';')checkClickCaptcha(props.uuid, captchaInfo, props.unset).then(() => {state.tip = props.successsetTimeout(() => {props.callback?.(captchaInfo)onClose()}, 1500)}).catch(() => {state.tip = props.errorsetTimeout(() => {load()}, 1500)})}}
}const onCancelRecord = (index: number) => {state.xy.splice(index, 1)
}const onClose = () => {document.getElementById(props.uuid)?.remove()
}const captchaBoxTop = computed(() => (state.captcha.height + 200) / 2 + 'px')
const captchaBoxLeft = computed(() => (state.captcha.width + 24) / 2 + 'px')load()
</script><style scoped lang="scss">
.ba-click-captcha {padding: 12px;border: 1px solid var(--el-border-color-extra-light);background-color: var(--el-color-white);position: fixed;z-index: 9999991;left: calc(50% - v-bind('captchaBoxLeft'));top: calc(50% - v-bind('captchaBoxTop'));border-radius: 10px;box-shadow: 0 0 0 1px hsla(0, 0%, 100%, 0.3) inset, 0 0.5em 1em rgba(0, 0, 0, 0.6);.loading {color: var(--el-color-info);width: 350px;text-align: center;line-height: 200px;}.captcha-img-box {position: relative;.captcha-img {width: v-bind('state.captcha.width') px;height: v-bind('state.captcha.height') px;border: none;cursor: pointer;}.step {box-sizing: border-box;position: absolute;width: 20px;height: 20px;line-height: 20px;font-size: var(--el-font-size-small);font-weight: bold;text-align: center;color: var(--el-color-white);border: 1px solid var(--el-border-color-extra-light);background-color: var(--el-color-primary);border-radius: 30px;box-shadow: 0 0 10px var(--el-color-white);user-select: none;cursor: pointer;}}.captcha-prompt {height: 40px;line-height: 40px;font-size: var(--el-font-size-base);text-align: center;color: var(--el-color-info);span {margin-left: 10px;font-size: var(--el-font-size-medium);font-weight: bold;color: var(--el-color-error);&.clicaptcha-clicked {color: var(--el-color-primary);}}}.captcha-refresh-box {position: relative;margin-top: 10px;.captcha-refresh-line {position: absolute;top: 16px;width: 140px;height: 1px;background-color: #ccc;}.captcha-refresh-line-l {left: 5px;}.captcha-refresh-line-r {right: 5px;}.captcha-refresh-btn {cursor: pointer;display: block;margin: 0 auto;width: 32px;height: 32px;font-size: 32px;color: var(--el-color-info);}}
}
</style>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
当点击次数达到两次的时候,就提交到后台去验证
前端提交的数据格式是这样的
在这里插入图片描述
id是前后端对应的, info 中 以 “-” 分隔了 两次点击的 坐标点 , 350是图片的完度,200是图片的高度

接下来后端进行验证, 我们来看看后端的验证过程

在这里插入图片描述
在这里插入图片描述

自已写一个试试, 后端的接口还是用的 buildadmin 的接口,前端我自己写了一下简易代码做了一下实验,如果开发的时候 可以使用上面的 代码, 注意上面的代码是 ts 的,稍稍改一下代码就可以了
以下是我用 vue3 js写的简易代码, 也是可以实现验证码的 , 仅供参考

<template><div class="captcha-wrapper"><div class="captcha"><img v-if="data.captchaInfo.base64" width="300" height="200"  @click.prevent="clickcaptcha($event)" class="img-captcha" :src="data.captchaInfo.base64" /><span v-for="(item,index) in data.captchaInfo.clickXY" :style="{left:item.x+'px',top:item.y+'px'}">{{index+1}}</span></div><div>请点击字符或图片:{{data.captchaInfo.text}}</div><button @click="getphoto"> 刷新验证码 </button></div></template><script setup>
import {onMounted,ref,reactive} from "vue"
import { checkClickCaptcha, getCaptchaData } from '../../api/common'let uuid = ref("");
let data = reactive({captchaInfo:{base64:"",text:"",width:"",   //后端返回的图片的宽高, 一般在显示的时候就按这个大小显示, 本例中没有使用它们,而是自定义了一个 宽高,验证时,要把本地自定义的宽高传给后端才可以height:"",id:uuid,number:0,       //当前图片被点击的次数clickposition:"",clickXY:[]}
})//生命周期onMounted(()=>{console.log(123);uuid = Math.floor(Math.random()*(10000-1+1))+1;getphoto();  //生命周期开始时调用后台接口,得到 验证码图片});//图片的点击事件let clickcaptcha = (e)=>{let xy = e.offsetX+","+e.offsetY;   //得到点击的位置,因为是两个验证码,所以要点击两次if(data.captchaInfo.number == 0){data.captchaInfo.clickposition = xy;   //如果是第一次点击 记录一下, 点击位置data.captchaInfo.clickXY = [{x:e.offsetX,y:e.offsetY}]}else {data.captchaInfo.clickposition = data.captchaInfo.clickposition + "-" + xy;  //如果是第二次点击 ,把两次点击的位置都记录下来data.captchaInfo.clickXY.push({x:e.offsetX,y:e.offsetY})}data.captchaInfo.number++;if(data.captchaInfo.number == 2){  //点击了两次checkClickCaptcha(data.captchaInfo.id, data.captchaInfo.clickposition+";"+'300;200', true).then((res) => {console.log(res);if(res.code == 1){//这里验证成功的代码,  验证成功之后, 把captchaInfo 的数据清空//然后提交表单中的数据alert("验证成功")}else if(res.code == 0){alert("验证失败")}}).catch(() => {alert("验证失败")})}}let getphoto = ()=>{getCaptchaData(uuid).then(res=>{data.captchaInfo = Object.assign(data.captchaInfo,res.data,{ number:0,       //当前图片被点击的次数clickposition:"",clickXY:[]});})}</script><style scoped lang="scss">.captcha-wrapper{width:300px;  //这里要和自定义的图片一样宽.captcha{position: relative;.img-captcha{}span{position:absolute;display:block;width:20px;height:20px;background:#f60;text-align: center;line-height: 20px;border-radius: 10px;color:#fff;}}}
</style>

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/143098.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

我记不住的那些命令(不断更新中)

fzf 一种进行模糊查找的命令行工具 主页&#xff1a;https://github.com/junegunn/fzf 我的主机是 Kali&#xff0c;通过apt进行安装fzf&#xff0c;并进行配置。 # apt install fzf # apt show fzf 通过参考/usr/share/doc/fzf/README.Debian来进行快速配置快捷键和自动补…

保护您的Google账号安全:检查和加固措施

简介&#xff1a;随着我们在日常生活中越来越依赖于Google账号&#xff0c;我们的个人信息和敏感数据也变得越来越容易受到威胁。为了确保您的Google账号的安全性&#xff0c;本文将介绍一些简单但有效的方法&#xff0c;帮助您检查和加固您的Google账号。 --- 在数字时代&am…

squid代理服务器

squide是正向代理&#xff0c;缓存加速。基于aci访问控制协议。可以过滤控制。可以实现访问控制 squid代理的工作机制 nginx是转发 1、 squid是代替客户端向网站请求数据&#xff0c;不需要访问代理的ip地址。直接请求目的网站即可。由代理服务器处理请求和响应。squid也可以…

[Mac软件]Adobe Media Encoder 2024 V24.0.2免激活版

软件说明 使用Media Encoder&#xff0c;您将能够处理和管理多媒体。插入、转码、创建代理版本&#xff0c;并几乎以任何可用的格式输出。在应用程序中以单一方式使用多媒体&#xff0c;包括Premiere Pro、After Effects和Audition。 紧密整合 与Adobe Premiere Pro、After …

WebRTC简介及使用

文章目录 前言一、WebRTC 简介1、webrtc 是什么2、webrtc 可以做什么3、数据传输需要些什么4、SDP 协议5、STUN6、TURN7、ICE 二、WebRTC 整体框架三、WebRTC 功能模块1、视频相关①、视频采集---video_capture②、视频编解码---video_coding③、视频加密---video_engine_encry…

保护数字前沿:下一代防火墙如何塑造网络安全的未来

下一代防火墙通过提供先进的威胁检测、精细控制和云安全功能&#xff0c;正在重塑网络安全的未来。随着数字环境的不断发展&#xff0c;组织必须采用这些创新解决方案来保护其数字资产并维护安全的数字前沿。 在当今互联的世界中&#xff0c;网络威胁变得越来越复杂&#xff0c…

阿里云ACK(Serverless)安装APISIX网关及APISIX Ingress Controller

在k8s上安装apisix全家&#xff0c;通过helm安装很简单&#xff0c;但是会遇到一些问题。 安装 首先登录阿里云控制台&#xff0c;在ACK集群详情页&#xff0c;进入CloudShell&#xff0c;执行下面helm命令安装apisix、apisix-ectd、apisix-dashboard和apisix-ingress-contro…

第1关:构造函数与析构函数的实现

题目&#xff1a;根据.h写出.cpp 考点&#xff1a; 1.链表的默认构造&#xff0c; 拷贝构造&#xff0c;传参构造以及析构函数等。 代码&#xff1a; /********** BEGIN **********/ #include <cstdlib> #include <cstring> #include "LinkedList.h&…

ARM PMU

PMU单元概览 ARM PMU概要 PMU作为一个扩展功能&#xff0c;是一种非侵入式的调试组件。 对PMU寄存器的访问可以通过CP15协处理器指令和Memory-Mapped地址。 基于PMUv2架构&#xff0c;A7处理器在运行时可以收集关于处理器和内存的各种统计信息。对于处理器来说这些统计信息中…

AI:83-基于深度学习的手势识别与实时控制

🚀 本文选自专栏:人工智能领域200例教程专栏 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的代码,详细讲解供大家学习,希望可以帮到大家。欢迎订阅支持,正在不断更新中,…

【Linux奇遇记】我和Linux的初次相遇

&#x1f308;个人主页: Aileen_0v0 &#x1f525;系列专栏:Linux奇遇记系列专栏&#x1f4ab;"没有罗马,那就自己创造罗马~" 目录 前端和后端的介绍 1.前端 2.后端 3.前后端区别 Linux在前后端开发中的角色 如何学习Linux 去进行程序开发 Linux的常见根目…

爆款元服务!教你如何设计高使用率卡片

元服务的概念相信大家已经在 HDC 2023 上有了很详细的了解&#xff0c;更轻便的开发方式&#xff0c;让开发者跃跃欲试。目前也已经有很多开发者开发出了一些爆款元服务&#xff0c;那么如何让你的元服务拥有更高的传播范围、更高的用户使用率和更多的用户触点呢&#xff1f;设…

MySQL 索引事务

MySQL 索引&事务 文章目录 MySQL 索引&事务1. 索引1.1 概念1.2 作用1.3 使用场景1.4 使用 2. 事务2.1 为什么使用事务2.2 事务概念2.3 事务的特性2.4 使用 1. 索引 1.1 概念 索引(index)是一种特殊的文件&#xff0c;包含着对数据表里所有记录的引用指针。可以对表中的…

学好Python-新手小白如何做?

新手小白如何学好Python?有哪些参考方法吗?这是一个老生常谈的话题了。今天为大家带来两位前辈的分享&#xff0c;他们给出了非常实用的方法和思路&#xff0c;希望对你有所帮助。 1、多练&#xff0c;两个字&#xff1a;多练 如果真的要说方法可以参考如下&#xff1a; ①…

排查线程阻塞问题

案例代码 package first;import java.util.concurrent.TimeUnit;public class DeadLock {private static volatile Object lock new Object();public static void main(String[] args) {new Thread(() -> {test1();}).start();new Thread(() -> {test2();}).start();}p…

centos下安装mysql8版本

1、如果服务器没有wget&#xff0c;先下载wget工具 sudo yum install wget 2、下载指定mysql版本的tar包 sudo wget https://downloads.mysql.com/archives/get/p/23/file/mysql-8.0.21-1.el7.x86_64.rpm-bundle.tar 3、解压tar包 sudo tar -xvf mysql-8.0.21-1.el7.x86_64.rpm…

《QT从基础到进阶·二十四》按钮组QButtonGroup,单选框QRadioButton和多选框QCheckBox

1、按钮组QButtonGroup 如果有多个单选按钮&#xff0c;可以统一放进一个按钮组。 图中有三个单选按钮放进了一个QGroupBox,并且设置了水平布局&#xff0c;现在要将这三个单选按钮放进一个按钮组&#xff0c;之前的想法是先把三个按钮加入按钮组&#xff0c;再把按钮组放进QG…

MATLAB中uiresume函数用法

目录 语法 说明 示例 按下按钮后恢复执行 使用函数调用恢复执行 uiresume函数的功能是恢复暂停程序的执行。 语法 uiresume uiresume(f) 说明 uiresume 恢复与当前图窗 (gcf) 关联的对应 uiwait 调用暂停的程序执行。 uiresume(f) 恢复与图窗 f 关联的对应 uiwait 调用…

开发企业微信群机器人,实现定时提醒

大家好&#xff0c;我是鱼皮&#xff0c;今天分享一个用程序解决生活工作问题的真实案例。 说来惭愧&#xff0c;事情是这样的&#xff0c;在我们公司&#xff0c;每天都要轮流安排一名员工&#xff08;当然也包括我&#xff09;去楼层中间一个很牛的饮水机那里接水。但由于大…

【杂谈】体验AI帮助编写代码,有提升效率,AI本身提升空间也很大

体验AI帮助编写代码 ​专栏内容&#xff1a; 手写数据库toadb 本专栏主要介绍如何从零开发&#xff0c;开发的步骤&#xff0c;以及开发过程中的涉及的原理&#xff0c;遇到的问题等&#xff0c;让大家能跟上并且可以一起开发&#xff0c;让每个需要的人成为参与者。 本专栏会定…