最先看到 「貼吧神獸」 驗證碼是在百度貼吧,吧主防止挖墳貼,放出了究極神獸驗證碼javascript
例如:php
地址:http://tieba.baidu.com/p/3320323440css
能夠用 PHP + JavaScript 實現該種類型的驗證碼。html
使用 jQuery 版本:jQuery 1.9.1java
框架使用 ThinkPHP 3.2.3,自定義的驗證碼類基於 TP 的驗證碼類jquery
最終效果圖:web
自定義驗證碼類路徑:/Application/Home/Common/VerivyPostBar.class.phpajax
控制器:/Application/Home/Controller/PostBarController.class.php數組
視圖:/Applicable/Home/View/PostBarVerify/index.htmlsession
自定義驗證碼類 /Application/Home/Common/VerivyPostBar.class.php
<?php namespace Home\Common; use Think\Verify; class VerifyPostBar extends Verify { private $_image = NULL; // 驗證碼圖片實例 private $_color = NULL; // 驗證碼字體顏色 public function entryProcess($id = '') { // 圖片寬(px) $this->imageW || $this->imageW = $this->length*$this->fontSize*1.5 + $this->length*$this->fontSize/2; // 圖片高(px) $this->imageH || $this->imageH = $this->fontSize * 2.5; // 創建一幅 $this->imageW x $this->imageH 的透明圖像 $this->_image = imagecreatetruecolor($this->imageW, $this->imageH); imagesavealpha($this->_image, true); $trans_colour = imagecolorallocatealpha($this->_image, 0, 0, 0, 127); imagefill($this->_image, 0, 0, $trans_colour); // 驗證碼字體隨機顏色 $this->_color = imagecolorallocate($this->_image, mt_rand(1,150), mt_rand(1,150), mt_rand(1,150)); // 驗證碼使用隨機字體 $ttfPath = $_SERVER['DOCUMENT_ROOT'].'/ThinkPHP/Library/Think/Verify/' . ($this->useZh ? 'zhttfs' : 'ttfs') . '/'; if(empty($this->fontttf)){ $dir = dir($ttfPath); $ttfs = array(); while (false !== ($file = $dir->read())) { if($file[0] != '.' && substr($file, -4) == '.ttf') { $ttfs[] = $file; } } $dir->close(); $this->fontttf = $ttfs[array_rand($ttfs)]; } $this->fontttf = $ttfPath . $this->fontttf; if($this->useImgBg) { $this->_background(); } if ($this->useNoise) { // 繪雜點 // $this->_writeNoise(); } if ($this->useCurve) { // 繪干擾線 $this->_writeCurve(); } // 繪驗證碼 $code = array(); // 驗證碼 $codeNX = 0; // 驗證碼第N個字符的左邊距 if($this->useZh){ // 中文驗證碼 for ($i = 0; $i<$this->length; $i++) { $code[$i] = iconv_substr($this->zhSet, $i, 1, 'utf-8'); imagettftext($this->_image, $this->fontSize, mt_rand(-40, 40), $this->fontSize*($i+1)*1.5, $this->fontSize + mt_rand(10, 20), $this->_color, $this->fontttf, $code[$i]); } // 備選驗證碼區域(9個漢字) $len_pre_row = $this->area_length / $this->rows; // 每行的字數 for($r = 0; $r < $this->rows; $r++) { $flag = 1; $start = $r * $len_pre_row; $end = $r * $len_pre_row + $len_pre_row - 1; $code_ = array(); for ($i = $start; $i<$end + 1; $i++) { $code_[$i] = iconv_substr($this->code_area, $i, 1, 'utf-8'); // @param image // @param size // @param angle // @param x // @param y // @param color // @param fontfile imagettftext($this->_image, $this->fontSize, mt_rand(-20, 20), $this->fontSize*2 * $flag, $this->fontSize + 50 * $r + 120, $this->_color, $this->fontttf, $code_[$i]); $flag += 2; // 控制驗證碼備選字符的x座標 } } } // 保存驗證碼 $key = $this->authcode($this->seKey); $code = $this->authcode(strtoupper(implode('', $code))); $secode = array(); $secode['verify_code'] = $code; // 把校驗碼保存到session $secode['verify_time'] = NOW_TIME; // 驗證碼建立時間 session($key.$id, $secode); header('Cache-Control: private, max-age=0, no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); header('Pragma: no-cache'); header("content-type: image/png"); // 保存圖像至硬盤 imagepng($this->_image, 'Public/Home/Images/verifyimage.png'); // 輸出圖像 // imagepng($this->_image); readfile('Public/Home/Images/verifyimage.png'); imagedestroy($this->_image); } /** * 畫雜點 * 往圖片上寫不一樣顏色的字母或數字 */ private function _writeNoise() { $codeSet = '2345678abcdefhijkmnpqrstuvwxyz'; for($i = 0; $i < 10; $i++){ //雜點顏色 $noiseColor = imagecolorallocate($this->_image, mt_rand(150,225), mt_rand(150,225), mt_rand(150,225)); for($j = 0; $j < 5; $j++) { // 繪雜點 imagestring($this->_image, 5, mt_rand(-10, $this->imageW), mt_rand(-10, $this->imageH), $codeSet[mt_rand(0, 29)], $noiseColor); } } } /** * 畫一條由兩條連在一塊兒構成的隨機正弦函數曲線做干擾線(你能夠改爲更帥的曲線函數) * * 高中的數學公式咋都忘了涅,寫出來 * 正弦型函數解析式:y=Asin(ωx+φ)+b * 各常數值對函數圖像的影響: * A:決定峯值(即縱向拉伸壓縮的倍數) * b:表示波形在Y軸的位置關係或縱向移動距離(上加下減) * φ:決定波形與X軸位置關係或橫向移動距離(左加右減) * ω:決定週期(最小正週期T=2π/∣ω∣) * */ private function _writeCurve() { $px = $py = 0; // 曲線前部分 $A = mt_rand(1, $this->imageVerifyH/2); // 振幅 $b = mt_rand(-$this->imageVerifyH/4, $this->imageVerifyH/4); // Y軸方向偏移量 $f = mt_rand(-$this->imagimageVerifyHeH/4, $this->imageVerifyH/4); // X軸方向偏移量 $T = mt_rand($this->imageVerifyH, $this->imageW*2); // 週期 $w = (2* M_PI)/$T; $px1 = 0; // 曲線橫座標起始位置 $px2 = mt_rand($this->imageW/2, $this->imageW * 0.8); // 曲線橫座標結束位置 for ($px=$px1; $px<=$px2; $px = $px + 1) { if ($w!=0) { $py = $A * sin($w*$px + $f)+ $b + $this->imageVerifyH/2; // y = Asin(ωx+φ) + b $i = (int) ($this->fontSize/5); while ($i > 0) { imagesetpixel($this->_image, $px + $i , $py + $i, $this->_color); // 這裏(while)循環畫像素點比imagettftext和imagestring用字體大小一次畫出(不用這while循環)性能要好不少 $i--; } } } } /* 加密驗證碼 */ private function authcode($str){ $key = substr(md5($this->seKey), 5, 8); $str = substr(md5($str), 8, 10); return md5($key . $str); } /** * 繪製背景圖片 * 注:若是驗證碼輸出圖片比較大,將佔用比較多的系統資源 */ private function _background() { $path = dirname(__FILE__).'/Verify/bgs/'; $dir = dir($path); $bgs = array(); while (false !== ($file = $dir->read())) { if($file[0] != '.' && substr($file, -4) == '.jpg') { $bgs[] = $path . $file; } } $dir->close(); $gb = $bgs[array_rand($bgs)]; list($width, $height) = @getimagesize($gb); // Resample $bgImage = @imagecreatefromjpeg($gb); @imagecopyresampled($this->_image, $bgImage, 0, 0, 0, 0, $this->imageW, $this->imageH, $width, $height); @imagedestroy($bgImage); } }
控制器 /Application/Home/Controller/PostBarController.class.php
<?php namespace Home\Controller; use Think\Controller; use Home\Common\VerifyPostBar; class PostBarVerifyController extends Controller { // 界面 public function index() { header('Content-type:text/html;charset=utf-8'); $this->display(); } // 驗證 public function check_verify($code) { $verify = new VerifyPostBar(); if(!$verify->check($code)) { return 400; } else { return 200; } } // 準備驗證碼字符 public function prepare_code() { // 驗證碼的長度 $length = 4; // 驗證碼選區長度 $selects = 12; // 相近的漢字爲一組,從6組36個漢字中抽出4組12個漢字組成驗證碼圖片組 $zhSet = array( array( '已','己','乙','巳','九','走' ), array( '田','由','甲','申','白','日' ), array( '魚','漁','俞','喻','瑜','愈' ), array( '請','清','情','青','晴','蜻' ), array( '寶','玉','穴','必','空','控' ), array( '子','仔','籽','孜','吱','資' ) ); $tmp = array(); $count = count($zhSet); $tmp = $this->rand(0, $count - 1, 4); // 隨機生成4個不重複的數(0-5組裏面選出4組)做爲下標 $chars = array(); foreach($tmp as $key => $val) { $chars[] = $this->choose($zhSet, $val, 3);// 每組3個數 } // 從每組一維數組中選出一個組成長度爲4的驗證碼 foreach($chars as $key => $val) { $k = mt_rand(0, count($val) - 1); $code[] = $val[$k]; // 驗證碼 unset($chars[$key][$k]); } // dump($code); // dump($chars);die; // 把數組合併成一維數組 $characters = array(); foreach($chars as $key => $val) { foreach($val as $k => $v) { $characters[] = $v; } } // 備選驗證碼區數組 $code_area_array = array_merge($code, $characters); shuffle($code_area_array); // 備選驗證碼區字符串 $code_area = implode('', $code_area_array); $code = implode('', $code); $codes['code_area'] = $code_area; $codes['code_area_array'] = $code_area_array; $codes['code'] = $code; $codes['characters'] = $characters; $codes['length'] = $length; $_SESSION['code_area_array'] = $code_area_array; return $codes; } // 顯示驗證碼 public function verify() { $codes = $this->prepare_code(); $conf = array( 'useZh' => true, 'zhSet' => $codes['code'], 'code_area' => $codes['code_area'], 'length' => $codes['length'], // 驗證碼長度(漢字個數) 'rows' => 3, //備選區域3行 'area_length'=> mb_strlen($codes['code_area'], 'utf-8'), // 備選區域漢字個數 'fontSize' => 20, 'imageW' => 320, 'imageH' => 600, 'imageVerifyH' => 45, // 4字驗證碼區域高度 ); $verify = new VerifyPostBar($conf); $verify->entryProcess(); } // 從一組連續的數字中選出不重複的個數 // @param $start 數字的開始值 // @param $end 數字的結束值 // @param $count 選出的個數 public function rand($start, $end, $count) { $tmp = range($start, $end); $tmp = array_rand($tmp, $count); return $tmp; } // 從每組漢字(一組6個)中選出n(4)個 // @param $array 二維數組 // @param $key 數組 $array 的下標 // @param $n 選出幾個 public function choose($array, $key, $n) { $arr = $array[$key]; $count = count($arr); $tmp_key = $this->rand(0, $count - 1, $n); $chars = array(); foreach($tmp_key as $val) { $chars[] = $arr[$val]; } return $chars; } // 記錄點擊次數,若是次數達到4次就作出判斷,驗證碼輸入是否正確 public function count_ckick() { session_start(); // 座標數組 $codes = $_SESSION['code_area_array']; $xy = array( 'line1_y'=>array( 'x1'=>0, 'x2'=>1, 'x3'=>2, 'x4'=>3, ), 'line2_y'=>array( 'x1'=>4, 'x2'=>5, 'x3'=>6, 'x4'=>7, ), 'line3_y'=>array( 'x1'=>8, 'x2'=>9, 'x3'=>10, 'x4'=>11, ) ); if(! isset($_POST['clear']) || $_POST['clear'] != 1) { $_SESSION['count'] = $count = $_POST['count']; if($count > 4) { $_SESSION['count'] = 4; } else { // 記錄選擇的驗證碼文字 $x = $_POST['x']; $y = $_POST['y']; foreach($xy as $key => $val) { foreach($val as $k => $v) { if($y == $key) { if($x == $k) { $code_key = $codes[$v]; } } } } } if(! isset($_SESSION['input_code'])) { $_SESSION['input_code'] = $code_key; } else { $_SESSION['input_code'] .= $code_key; } $return = '點擊了 '.$_SESSION['count'].' 次, 選中的漢字是: '.$code_key.' 輸入的驗證碼是: '.$_SESSION['input_code']; if($count == 4) { $code = $this->check_verify($_SESSION['input_code']); if($code == 200) { $return .= ' 輸入正確'; } else { $return .= ' 輸入錯誤'; } } echo $return; } else { // 清除點擊次數 $_SESSION['count'] = 0; unset($_SESSION['input_code']); echo '成功清除了點擊次數,點擊次數爲',$_SESSION['count'],'次'; } } // 獲取session中記錄的點擊次數 public function record_click() { session_start(); if(! isset($_SESSION['count'])) { $_SESSION['count'] = 0; } echo $_SESSION['count']; } // 修改點擊記錄數 public function update_click() { session_start(); if(! isset($_SESSION['count'])) { $_SESSION['count'] = 0; } else { $newcount = $_SESSION['count'] + $_POST['times']; if($newcount < 0) { $_SESSION['count'] = 0; unset($_SEIION['input_code']); } else { $_SESSION['count'] = $newcount; $_SESSION['input_code'] = mb_substr($_SESSION['input_code'], 0, -1, 'utf-8'); } } echo '點擊數是:'.$_SESSION['count'].' 驗證碼是:'.$_SESSION['input_code']; } }
視圖 /Applicable/Home/View/PostBarVerify/index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> #verify_area { width:600px; height: 400px; position: relative; padding:10px; } h1 { font-size:16px; font-family: "微軟雅黑"; color: #999; text-indent: 30px; } #notice { position: relative; top: 95px; left: 30px; color: #666; display: block; z-index: 2; } #buttons { width: 350px; height: 150px; position: relative; top: 110px; left: 20px; z-index: 2; padding: 0; } #verify_pic { position: relative; top: -150px; } .button { display: inline-block; cursor: pointer; margin: -15px 12px 15px 5px; width: 60px; height: 45px; border: 1px solid #E0E0E0; border-bottom-color: #BFBFBF; outline: 0; background: -ms-linear-gradient(top,#fff,#f5f5f5); background: -webkit-gradient(linear,left top,left bottom,from(#fff),to(#f5f5f5)); background: -moz-linear-gradient(top,#fff,#fafafa); filter: progid:DXImageTransform.Microsoft.Gradient(gradientType=0, startColorStr=#FFFFFF, endColorStr=#F5F5F5); -webkit-opacity: 0.3; -moz-opacity: 0.3; -khtml-opacity: 0.3; opacity: .3; filter:alpha(opacity=30); -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=30); zoom: 1; } #verify_title { text-indent: 30px; position: relative; top: 25px; font-family: "微軟雅黑"; color: #999; } #verify { width: 200px; height: 34px; position: relative; top: 0px; left: 80px; border: 1px solid #ccc; } .verify { width: 40px; height: 34px; border-right: 1px solid #ccc; float: left; background-repeat: no-repeat; } .verify_last { border-right: 0 } .cls { clear: both } .hid { display: none; } #delete { position: relative; left: 162px; width: 39px; height: 34px; background: url('__PUBLIC__/Images/delete.png') 0 0 no-repeat; cursor: pointer; } .addbg { background-image: url("__PUBLIC__/Images/verifyImage.png") } </style> <script src="__PUBLIC__/Js/jquery-1.9.1.min.js"></script> </head> <body> <div id="verify_area"> <form action="{:U('Home/PostBarVerify/check_verify','','')}" method="post" id="form"> <h1>點擊驗證碼圖片換一張</h1> <div> <div id="verify_title">驗證碼</div> <div id="verify"> <div id="verify1" class="verify"></div> <div id="verify2" class="verify"></div> <div id="verify3" class="verify"></div> <div id="verify4" class="verify verify_last"></div> <div id="delete"></div> <div class="cls"></div> </div> </div> <p id="notice">點擊框內文字輸入上圖中漢字</p> <div id="buttons"> <for start="0" end="3" name="i"> <br /> <for start="0" end="4" name="j"> <div class="button" x="x{$j+1}" y="line{$i+1}_y"></div> </for> </for> </div> <img id="verify_pic" src='' style="cursor: pointer;" alt=""> <span class="hid" id="verify_url">{:U('Home/PostBarVerify/verify','','')}</span> </form> </div> </body> <script> $(function(){ // 加載或刷新時清空session計數 $count = 0; clear_count(); $xy = { "line1_y" : -112, // 備選驗證碼第一行y座標 "line2_y" : -165, // 備選驗證碼第二行y座標 "line3_y" : -212, // 備選驗證碼第三行y座標 "x1" : -35, // 備選驗證碼每行第一個字x座標 "x2" : -114, // 備選驗證碼每行第二個字x座標 "x3" : -195, // 備選驗證碼每行第三個字x座標 "x4" : -276 // 備選驗證碼每行第四個字x座標 }; $verify_url = $("#verify_url").html(); // 驗證碼請求地址 $src = $verify_url + '?' + Math.random(); change_verify($src); // 載入頁面時加載驗證碼 $("#verify_pic").click(function(){ // 點擊驗證碼時更換驗證碼 change_verify($src); // 清除驗證碼 $(".verify").css('background-image', ''); clear_count($count); }); function clear_count() { // 清空session計數 $.ajax({ url: '{:U("/Home/PostBarVerify/count_ckick")}', type: 'post', data: { "clear": 1 }, success: function(data) { console.log(data); } }); } // 更換驗證碼 function change_verify($src) { var flag = 0; // 請求驗證碼地址生成驗證碼圖片 $.ajax({ url: $src, async: false, success: function() { flag = 1; } }); if(flag == 1) { $('#verify_pic').attr('src', $src); // 驗證碼圖片圖片 } } // 點擊備選區選擇驗證碼 $('.button').click(function() { // 查詢session中保存的count $.ajax({ url: '{:U("/Home/PostBarVerify/record_click")}', async: false, success: function(data) { $count = data; } }); $count++; if($count > 4) { // return false; } $.ajax({ // 記錄點擊次數以及點擊的文字 url: '{:U("/Home/PostBarVerify/count_ckick")}', type: 'post', data: { "count": $count, "x": $(this).attr("x"), "y": $(this).attr("y") }, success: function(data) { if(data != '') { console.log(data); } } }); $x = $(this).attr('x'); $y = $(this).attr('y'); choose_verify($xy, $x, $y, $count); }); // 生成每次點擊驗證碼的座標,填充到驗證碼 function choose_verify($xy, $x, $y) { $x = $xy[$x] + 'px'; $y = $xy[$y] + 'px'; // 填充驗證碼 $('#verify'+$count).css({ 'background-position-x': $x, 'background-position-y': $y }).addClass('addbg'); } // 刪除驗證碼 $('#delete').click(function(){ $.ajax({ url: '{:U("Home/PostBarVerify/record_click")}', type: 'post', async: false, success: function(data) { if(data == 0) {data = 1;} $i = data; } }); $('#verify'+$i).removeClass('addbg');console.log('#verify'+$i); $i--; // 修改計數,清除session $.ajax({ url: '{:U("Home/PostBarVerify/update_click")}', type: 'post', data: { 'times': -1 }, success: function(data) { console.log(data); } }); $count--; }); }); </script> </html>
演示
初始狀態:
輸入驗證碼:
輸入完成,返回結果,錯誤時:
輸入完成,返回結果,正確時:
刪除驗證碼:
刷新驗證碼:
參考: