随机背景在随机位置添加随机颜色的文字

功能:根据给的中文字符串,取随机的图片为背景,将字符串中的每个字都随机显示在图片上,并偏斜一定的角度。

后端图片处理类,会返回图片对应的宽、高、旋转角度、旋转前起始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>

 

posted @ 2018-07-31 16:04  fengzmh  阅读(421)  评论(0编辑  收藏  举报