随机背景在随机位置添加随机颜色的文字
功能:根据给的中文字符串,取随机的图片为背景,将字符串中的每个字都随机显示在图片上,并偏斜一定的角度。
后端图片处理类,会返回图片对应的宽、高、旋转角度、旋转前起始x、y及文字四个角的x、y坐标:
class Image { /** * 图片上增加文字 * @param type $strImgPath * @param type $arrText */ public function imgAddText($strText, $intWidth, $intHeight) { //随机取背景图片 $strImgPath = $this->RandomFile(__DIR__ . '/verifycodeimages/', 'jpg|png'); if (empty($strImgPath) || !file_exists($strImgPath)) { return null; } //获取图片信息 $imgInfo = getimagesize($strImgPath); $imgRes = null; switch ($imgInfo[2]) { case 1: $imgRes = @imagecreatefromgif($strImgPath); break; case 2: $imgRes = @imagecreatefromjpeg($strImgPath); break; case 3: $imgRes = @imagecreatefrompng($strImgPath); break; } //重新设置图片大小 $intPicWidth = imagesx($imgRes); $intPicHeight = imagesy($imgRes); if (function_exists("imagecopyresampled")) { $newImgRes = imagecreatetruecolor($intWidth, $intHeight); imagecopyresampled($newImgRes, $imgRes, 0, 0, 0, 0, $intWidth, $intHeight, $intPicWidth, $intPicHeight); } else { $newImgRes = imagecreate($intWidth, $intHeight); imagecopyresized($newImgRes, $imgRes, 0, 0, 0, 0, $intWidth, $intHeight, $intPicWidth, $intPicHeight); } //释放原图资源 imagedestroy($imgRes); //设置字体信息 $strFont = __DIR__ . '/Font/shoushu.ttf'; $arrTextRange = array(); $intFontSize = 30; $arrXY = array(); for ($intNum = 0; $intNum < mb_strlen($strText, 'utf-8'); $intNum++) { //随机获取旋转角度 $intAngle = rand(-45, 45); //随机获取文字颜色 $color = imagecolorallocate($newImgRes, rand(0, 255), rand(0, 255), rand(0, 255)); //获取文字所占像素位置 $arrBox = imagettfbbox($intFontSize, $intAngle, $strFont, mb_substr($strText, $intNum, 1)); $arrTureBox = imagettfbbox($intFontSize, 0, $strFont, mb_substr($strText, $intNum, 1)); if ($arrBox) { $intMinX = min(array($arrBox[0], $arrBox[2], $arrBox[4], $arrBox[6])); $intMaxX = max(array($arrBox[0], $arrBox[2], $arrBox[4], $arrBox[6])); $intMinY = min(array($arrBox[1], $arrBox[3], $arrBox[5], $arrBox[7])); $intMaxY = max(array($arrBox[1], $arrBox[3], $arrBox[5], $arrBox[7])); $intFontWidth = $intMaxX - $intMinX; $intFontHeight = $intMaxY - $intMinY; $intX = $intY = 0; $intRangeMaxX = $intWidth - $intFontWidth; $intRangeMaxY = $intHeight - $intFontHeight; $arrRange = array(); if (empty($arrXY)) { //如果没有画过文字,那整张图片都可以画 $arrRange[] = array(0, 0, $intRangeMaxX, $intRangeMaxY); } else { //按x轴排序,判断排除前后几个字后,其他几个字y轴方向是否还有空间 array_multisort(array_column($arrXY, 0), SORT_ASC, $arrXY); for ($intXNum = 0; $intXNum <= count($arrXY); $intXNum++) { //取前一个字的右侧x轴为最小x $intTempMinX = ($intXNum == 0 ? 0 : $arrXY[$intXNum - 1][2]); //获取右侧的边界值个数 for ($intLen = count($arrXY) - $intXNum + 1; $intLen >= 1; $intLen--) { //取最后一个字的左侧x轴为最大x $intTempMaxX = ($intLen == count($arrXY) - $intXNum + 1 ? $intWidth : $arrXY[$intXNum + $intLen - 1][0]); $arrTempXY = array(); if ($intLen > 1) { //文字区域按照y轴排序 $arrTempXY = array_slice($arrXY, $intXNum, $intLen - 1); array_multisort(array_column($arrTempXY, 1), SORT_ASC, $arrTempXY); } //循环y轴区间 for ($intYNum = 0; $intYNum <= count($arrTempXY); $intYNum++) { $intTempMinY = ($intYNum == 0 ? 0 : $arrTempXY[$intYNum - 1][3]); $intTempMaxY = ($intYNum == count($arrTempXY) ? $intHeight : $arrTempXY[$intYNum][1]); if ($intTempMaxY - $intTempMinY >= $intFontHeight && $intTempMaxX - $intTempMinX >= $intFontWidth) { $arrRange[] = array($intTempMinX, $intTempMinY, $intTempMaxX - $intFontWidth, $intTempMaxY - $intFontHeight); } } } } //按y轴排序,判断排除前后几个字后,其他几个字x轴方向是否还有空间 array_multisort(array_column($arrXY, 1), SORT_ASC, $arrXY); for ($intYNum = 0; $intYNum <= count($arrXY); $intYNum++) { //取前一个字的下侧y轴为最小y $intTempMinY = ($intYNum == 0 ? 0 : $arrXY[$intYNum - 1][3]); //获取下侧的边界值个数 for ($intLen = count($arrXY) - $intYNum + 1; $intLen >= 1; $intLen--) { //取最后一个字的上侧y轴为最大y $intTempMaxY = ($intLen == count($arrXY) - $intYNum + 1 ? $intHeight : $arrXY[$intYNum + $intLen - 1][1]); $arrTempXY = array(); if ($intLen > 1) { //文字区域按照x轴排序 $arrTempXY = array_slice($arrXY, $intYNum, $intLen - 1); array_multisort(array_column($arrTempXY, 0), SORT_ASC, $arrTempXY); } //循环x轴区间 for ($intXNum = 0; $intXNum <= count($arrTempXY); $intXNum++) { $intTempMinX = ($intXNum == 0 ? 0 : $arrTempXY[$intXNum - 1][2]); $intTempMaxX = ($intXNum == count($arrTempXY) ? $intWidth : $arrTempXY[$intXNum][0]); if ($intTempMaxY - $intTempMinY >= $intFontHeight && $intTempMaxX - $intTempMinX >= $intFontWidth) { $arrRange[] = array($intTempMinX, $intTempMinY, $intTempMaxX - $intFontWidth, $intTempMaxY - $intFontHeight); } } } } } //随机取一块区域,在这一区域范围内获取位置 $objRange = $arrRange[rand(0, count($arrRange) - 1)]; $intX = rand($objRange[0], $objRange[2]); $intY = rand($objRange[1], $objRange[3]); $arrXY[] = array($intX, $intY, $intX + $intFontWidth, $intY + $intFontHeight); imagettftext($newImgRes, $intFontSize, $intAngle, $intX - $intMinX, $intY - $intMinY, $color, $strFont, mb_substr($strText, $intNum, 1)); $intTrueWidth = max(array($arrTureBox[0], $arrTureBox[2], $arrTureBox[4], $arrTureBox[6])) - min(array($arrTureBox[0], $arrTureBox[2], $arrTureBox[4], $arrTureBox[6])); $intTrueHeight = max(array($arrTureBox[1], $arrTureBox[3], $arrTureBox[5], $arrTureBox[7])) - min(array($arrTureBox[1], $arrTureBox[3], $arrTureBox[5], $arrTureBox[7])); $arrTextRange[] = array( 'x' => $intX + ($intFontWidth - $intTrueWidth) / 2, 'y' => $intY + ($intFontHeight - $intTrueHeight) / 2, 'angle' => -$intAngle, 'width' => $intTrueWidth, 'height' => $intTrueHeight, 'coordinate' => array( ['x' => $arrBox[0] + $intX - $intMinX, "y" => $arrBox[1] + $intY - $intMinY], ['x' => $arrBox[2] + $intX - $intMinX, "y" => $arrBox[3] + $intY - $intMinY], ['x' => $arrBox[4] + $intX - $intMinX, "y" => $arrBox[5] + $intY - $intMinY], ['x' => $arrBox[6] + $intX - $intMinX, "y" => $arrBox[7] + $intY - $intMinY] ) ); } } ob_start(); //$im是你自己创建的图片资源 imagepng($newImgRes); imagedestroy($newImgRes); $image_data = ob_get_contents(); ob_end_clean(); //得到这个结果,可以直接用于前端的img标签显示 $image_data_base64 = "data:image/png;base64," . base64_encode($image_data); return array('img' => $image_data_base64, 'range' => $arrTextRange); } /** * 随机获取文件名 * @param type $strFileFolder * @param type $strExtensions * @return type */ function RandomFile($strFileFolder = '', $strExtensions = '.*') { // fix path: $strFileFolder = trim($strFileFolder); $strFileFolder = ($strFileFolder == '') ? './' : $strFileFolder; // check folder: if (!is_dir($strFileFolder)) { return ""; } // create files array $arrFiles = array(); // open directory if ($strDir = @opendir($strFileFolder)) { // go trough all files: while ($strFile = readdir($strDir)) { if (!preg_match('/^\.+$/', $strFile) and preg_match('/\.(' . $strExtensions . ')$/', $strFile)) { // feed the array: $arrFiles[] = $strFile; } } // close directory closedir($strDir); } else { return ""; } if (count($arrFiles) == 0) { return ""; } // seed random function: mt_srand((double) microtime() * 1000000); // get an random index: $intRand = mt_rand(0, count($arrFiles) - 1); // check again: if (!isset($arrFiles[$intRand])) { return ""; } // return the random file: return $strFileFolder . $arrFiles[$intRand]; } }
如需在前端验证返回的位置是否正确,可以按以下方式:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>verifycode</title> <style type="text/css"> body{margin: 0px;} </style> <script src="http://hroexternal4.51job.com/js/plugin/jquery-1.12.2.min.js"></script> <script type="text/javascript"> $('#imgverifycode').on('click', function () { console.log(event.offsetX + '|' + event.offsetY); }); //描点,参数有点的大小,颜色,点的坐标和标签; 很明显opts参数是一个对象 function drawPoint(opts) { document.write("<span id='" + opts.point[0] + "" + opts.point[1] + "' style='display: inline-block; width: " + opts.pw + "px; height: " + opts.ph + "px; background-color: " + opts.color + "; position: absolute;left: " + opts.point[0] + "px; top: " + opts.point[1] + "px;'>" + (opts.point[2] ? ("<div style='position: relative;'><span style='position: absolute; left: 5px; bottom: 1px; text-align: left; width: 100px;'>" + opts.point[2] + "</span></div>") : "") + "</span>"); } //画线 //pstart 起点 //pend 终点 //opts 参数 function drawLine(pstart, pend, opts) { var ph = 1; var pw = 1; var color = "DarkRed"; if (opts) { color = opts.color ? opts.color : color; } var slope; //斜率 var noSlope = false; //是否有斜率 var hdist = pend[0] - pstart[0]; var vdist = pend[1] - pstart[1]; if (hdist != 0) { slope = Math.abs(vdist / hdist); //计算斜率 } else { noSlope = true; //当hdist=0时,直线没有斜率 } var gapp = pw > ph ? ph : pw; //默认相邻点(左上角的像素点)间的距离 var diagonal = Math.sqrt(Math.pow(hdist, 2) + Math.pow(vdist, 2)); //斜边长度 var pn = parseInt(diagonal / gapp); //计算两点之间的点的数量 if (pn < 3) { pn = pn * 3 + 1 } ; //如果点的数量小于3,则加大点数;为什么+1呢,是保证pn=0时至少有一个点 var vgap = Math.abs(vdist) / pn; //相邻两点间的垂直距离 var hgap = Math.abs(hdist) / pn; //相邻两点间的水平距离 for (var i = 0; i < pn; i++) { //描点 //hgap 相邻两点间的水平距离 //vgap 相邻两点间的垂直距离 //hgap*i*(pend[0]<pstart[0]?-1:1)*(noSlope?0:1) 相对于起点的水平偏移 //vgap*i*(pend[1]<pstart[1]?-1:1) 相对于起点的垂直偏移 //(pend[0]<pstart[0]?-1:1) 水平偏移方向 //(pend[1]<pstart[1]?-1:1) 垂直偏移方向 //(noSlope?0:1) 直线没有斜率时,水平偏移为0 drawPoint({ pw: pw, ph: ph, color: color, point: [(hgap * i * (pend[0] < pstart[0] ? -1 : 1) * (noSlope ? 0 : 1) + pstart[0]), (vgap * i * (pend[1] < pstart[1] ? -1 : 1) + pstart[1])] }); } } //多边形 //ps 点的一维数组 function drawPolygon(ps) { if (ps) { //画线 for (var i = 0; i < ps.length - 1; i++) { drawLine(ps[i], ps[i + 1]); } //使闭合 if (ps.length > 2) { drawLine(ps[ps.length - 1], ps[0]) } //描拐点 for (var i = 0; i < ps.length; i++) { drawPoint({ pw: 3, ph: 3, color: 'RED', point: ps[i] }); } } } </script> </head> <body> <div id="verifycodeDiv" style="width: 250px;height: 250px; position:relative;margin:200px 300px;"> <?php include_once dirname(__DIR__) . '/ComModule/Image.Com.php'; //获取图片 $img = new Image(); $img6VerifyCode = $img->imgAddText('我是中国人来自江苏', 250, 250); //显示图片 echo '<img id="imgverifycode" src="' . $img6VerifyCode['img'] . '"/>'; foreach ($img6VerifyCode['range'] as $arrRange) { //在图片上每个字上加一个div,看是否和文字重合,div偏斜角度同文字偏斜角度 echo '<div pos="[' . $arrRange['coordinate'][0]['x'] . "," . $arrRange['coordinate'][0]['y'] . ']' . '[' . $arrRange['coordinate'][1]['x'] . "," . $arrRange['coordinate'][1]['y'] . ']' . '[' . $arrRange['coordinate'][2]['x'] . "," . $arrRange['coordinate'][2]['y'] . ']' . '[' . $arrRange['coordinate'][3]['x'] . "," . $arrRange['coordinate'][3]['y'] . ']" ' . 'style="position:absolute;' . 'top:' . $arrRange['y'] . 'px;left:' . $arrRange['x'] . 'px;' . 'width:' . $arrRange['width'] . 'px;height:' . $arrRange['height'] . 'px;' . 'transform:rotate(' . $arrRange['angle'] . 'deg);">' . '</div>'; //在图片上画矩形,看看矩形是否包含文字 echo '<script type="text/javascript">drawPolygon([' . '[' . $arrRange['coordinate'][0]['x'] . ', ' . $arrRange['coordinate'][0]['y'] . '], ' . '[' . $arrRange['coordinate'][1]['x'] . ', ' . $arrRange['coordinate'][1]['y'] . '],' . '[' . $arrRange['coordinate'][2]['x'] . ', ' . $arrRange['coordinate'][2]['y'] . '],' . '[' . $arrRange['coordinate'][3]['x'] . ', ' . $arrRange['coordinate'][3]['y'] . ']' . ']);</script>'; } ?> </div> </body> </html>