java后端实现滑块拼图验证

1.直接上代码,首先创建一个PuzzleHelper 拼图助手方便生成滑块
说明:
1.滑块的背景图在资源库中的图片随机。
2.滑块的边缘凹凸随机。
3.滑块的位置随机。
你可能会出现的问题:
1.图片转BASE64我用的工具类,你需要自己去网上找一个。
2.你可能报错Can’t read input file!,是因为图片的地址不正确,所以找不到图片。

import com.ruoyi.common.utils.sign.Base64;
import com.ruoyi.model.PuzzleInfo;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Random;

public class PuzzleHelper {
    //大图宽度
    private static final Integer bigWidth = 4000;
    //大图高度
    private static final Integer bigHeight = 2000;
    //边距(上、右、下小图距离大图最近距离,左边需要留大点边距方便拖动),同时也是内凹点和外凸点到正方形的边距
    private static final Integer margin = 100;
    //小正方形边长,制作拼图的轮廓
    private static final Integer square = 400;
    //小圆半径,拼图的凹凸轮廓
    private static final Integer circle = 80;
    //阴影宽度
    private static final Integer shadow=10;
    //图片压缩倍数
    private static final Integer scale=10;

    /**
     * @Title: getImageBASE64
     * @Description: 图片转BASE64
     */
    public static String getImageBASE64(BufferedImage image) throws IOException {
        byte[] imagedata = null;
        ByteArrayOutputStream bao=new ByteArrayOutputStream();
        ImageIO.write(image,"png",bao);
        imagedata=bao.toByteArray();
        String BASE64IMAGE = Base64.encode(imagedata).trim();
        BASE64IMAGE = BASE64IMAGE.replaceAll("\r|\n", "");  //删除 \r\n
        return BASE64IMAGE;
    }
    /**
     * 改变图片大小
     *
     * @param image  原图
     * @param width  目标宽度
     * @param height 目标高度
     * @return 目标图
     */
    public static BufferedImage resizeImage(final Image image, int width, int height,boolean type) {
        BufferedImage bufferedImage;
        if (type){
            bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        }
        else {
            bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        }

        final Graphics2D graphics2D = bufferedImage.createGraphics();
        graphics2D.setComposite(AlphaComposite.Src);
        //below three lines are for RenderingHints for better image quality at cost of higher processing time
        graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        graphics2D.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        graphics2D.drawImage(image, 0, 0, width, height, null);
        graphics2D.dispose();
        return bufferedImage;
    }

    /**
     * 随机正方形坐标和边缘凹凸信息
     */
    public static Integer[] getPosAndOutline(){
        //前端将图约束在300*150的框内,所以计算时除以10防止坐标约束时(除以10)出现小数

        //最小高度,只需排除边距加外凸
        Integer minY = (2*margin)/10;
        //最大高度,排除边距,正方形边长,上下两个外凸(极限)情况下,增加的高度。
        Integer maxY = (bigHeight - (square + 3 * margin))/10;
        //最小宽度,左边留一个半身位方便移动,身位宽度仍是极限(正方形边长+左右两个外凸)
        Integer minX = ((square + margin * 2) * 3 / 2)/10;
        //最大宽度,排除边距,正方形边长,左右两个外凸。
        Integer maxX = (bigWidth - (square + 3 * margin))/10;

        //获取正方形的位置,乘以10计算时保持正常大小,传给前端时需约束处理(除以10)
        Integer y = (minY + (int) (Math.random() * (maxY + 1 - minY)))*10;
        Integer x = (minX + (int) (Math.random() * (maxX + 1 - minX)))*10;

        Integer[] p=new Integer[10];
        //正方形x坐标
        p[0]=x;
        //正方形y坐标
        p[1]=y;

        //获取边缘凹凸信息
        //0没有 1内凹圆弧 2外凸圆弧
        Integer top = new Random().nextInt(3);
        Integer left = new Random().nextInt(3);
        Integer down = new Random().nextInt(3);
        Integer right = new Random().nextInt(3);
        //上方凹凸信息
        p[2]=top;
        //左侧凹凸信息
        p[3]=left;
        //下方凹凸信息
        p[4]=down;
        //右侧凹凸信息
        p[5]=right;

        return p;
     }

    /**
     * 获取小图轮廓位置信息,值为1的地方是小图需要从原图取色,大图需要在原图基础上模糊的位置
     */
    public static Integer[][] getSmallImg(Integer[] pos) {
        //正方形x坐标
        Integer x=pos[0];
        //正方形y坐标
        Integer y=pos[1];
        //小图x坐标(小图没有左侧外凸时x坐标与正方形一致)
        Integer posX=null==pos[8]?pos[0]:pos[8];
        //小图y坐标(小图没有上方外凸时y坐标与正方形一致)
        Integer posY=null==pos[9]?pos[1]:pos[9];
        //0没有 1内凹圆弧 2外凸圆弧
        Integer top =  pos[2];
        Integer left =pos[3];
        Integer down = pos[4];
        Integer right = pos[5];

        //取色模板,值为1的地方是小图需要从原图取色,大图需要在原图基础上模糊的位置
        Integer[][] templateImage = new Integer[bigHeight][bigWidth];

        double rPow = Math.pow(circle, 2);
        //模板只需要计算小图范围,其他地方不用管,阴影范围需要从小图边界向外延申shadow长度作为参照
        for (int i = posY-shadow; i <= posY+pos[7]+shadow; i++) {
            for (int j = posX-shadow; j <= posX+pos[6]+shadow; j++) {
                //先判断正方形
                if ((i >= y && i <= (y + square) && j >= x && j <= (x + square))) {
                    templateImage[i][j] = 1;
                } else {
                    templateImage[i][j] = 0;
                }

                //不在上方圆内
                if (top == 1) {
                    Integer tx = x + square / 2;
                    Integer ty = y + (margin - circle);
                    //当前点在上方圆形的位置内外
                    double res = Math.pow(j - tx, 2) + Math.pow(i - ty, 2);
                    //在圆内挖掉
                    if (res <= rPow) {
                        templateImage[i][j] = 0;
                    }
                }
                //在上方圆内
                if (top == 2) {
                    Integer tx = x + square / 2;
                    Integer ty = y - (margin - circle);
                    //当前点在上方圆形的位置内外
                    double res = Math.pow(j - tx, 2) + Math.pow(i - ty, 2);
                    //在圆内补上
                    if (res <= rPow) {
                        templateImage[i][j] = 1;
                    }
                }
                //不在左侧圆内
                if (left == 1) {
                    Integer tx = x + (margin - circle);
                    Integer ty = y + square / 2;
                    //当前点在左侧圆形的位置内外
                    double res = Math.pow(j - tx, 2) + Math.pow(i - ty, 2);
                    //在圆内挖掉
                    if (res <= rPow) {
                        templateImage[i][j] = 0;
                    }
                }
                //在左侧圆内
                if (left == 2) {
                    Integer tx = x - (margin - circle);
                    Integer ty = y + square / 2;
                    //当前点在左侧圆形的位置内外
                    double res = Math.pow(j - tx, 2) + Math.pow(i - ty, 2);
                    //在圆内挖掉
                    if (res <= rPow) {
                        templateImage[i][j] = 1;
                    }
                }
                //不在下方圆内
                if (down == 1) {
                    Integer tx = x + square / 2;
                    Integer ty = y + square - (margin - circle);
                    //当前点在左侧圆形的位置内外
                    double res = Math.pow(j - tx, 2) + Math.pow(i - ty, 2);
                    //在圆内挖掉
                    if (res <= rPow) {
                        templateImage[i][j] = 0;
                    }
                }
                //在下方圆内
                if (down == 2) {
                    Integer tx = x + square / 2;
                    Integer ty = y + square + (margin - circle);
                    //当前点在左侧圆形的位置内外
                    double res = Math.pow(j - tx, 2) + Math.pow(i - ty, 2);
                    //在圆内挖掉
                    if (res <= rPow) {
                        templateImage[i][j] = 1;
                    }
                }
                //不在右侧圆内
                if (right == 1) {
                    Integer tx = x + square - (margin - circle);
                    Integer ty = y + square / 2;
                    //当前点在左侧圆形的位置内外
                    double res = Math.pow(j - tx, 2) + Math.pow(i - ty, 2);
                    //在圆内挖掉
                    if (res <= rPow) {
                        templateImage[i][j] = 0;
                    }
                }
                //在右侧圆内
                if (right == 2) {
                    Integer tx = x + square + (margin - circle);
                    Integer ty = y + square / 2;
                    //当前点在左侧圆形的位置内外
                    double res = Math.pow(j - tx, 2) + Math.pow(i - ty, 2);
                    //在圆内挖掉
                    if (res <= rPow) {
                        templateImage[i][j] = 1;
                    }
                }
            }
        }
        return templateImage;
    }


    /**
     * 从原图中裁剪拼图
     * @param oriImage
     * @param targetImage
     * @param templateImage
     * @param pos
     */
    private static void cutByTemplate(BufferedImage oriImage, BufferedImage targetImage, Integer[][] templateImage,Integer[] pos) {
        int[][] martrix = new int[3][3];
        int[] values = new int[9];
        //小图x坐标(小图没有左侧外凸时x坐标与正方形一致)
        Integer posX=null==pos[8]?pos[0]:pos[8];
        Integer posY=null==pos[9]?pos[1]:pos[9];
        //创建shape区域
        for (int i = posY; i <=posY+pos[7]; i++) {
            for (int j = posX; j <= posX+pos[6]; j++) {
                int rgb = templateImage[i][j];
                // 原图中对应位置变色处理
                int rgb_ori = oriImage.getRGB(j, i);

                int top=1;
                int left=1;
                int down=1;
                int right=1;
                if (i>shadow-1)
                {
                    top= templateImage[i-shadow][j];
                }
                if (j>shadow-1){
                    left = templateImage[i][j-shadow];
                }
                if (i<bigHeight-shadow){
                    down = templateImage[i+shadow][j];
                }
                if (j<bigWidth-shadow){
                    right = templateImage[i][j+shadow];
                }

                if (rgb == 1) {
                    targetImage.setRGB(j-posX, i-posY, rgb_ori);

                    //抠图区域高斯模糊
                    readPixel(oriImage,  j, i, values);
                    fillMatrix(martrix, values);
                    oriImage.setRGB(j, i, avgMatrix(martrix));

                    Color white = new Color(230,230,230);
                    Color gray=new Color(40,40,40);
                    Color black=new Color(20,20,20);
                    //上方是图区外,当前为顶部边界,加重阴暗
                    if (top==0){
                        oriImage.setRGB(j, i,black.getRGB());
                        targetImage.setRGB(j-posX, i-posY,white.getRGB());
                    }
                    //左侧是图区外,当前为左侧边界,加重阴暗
                    if (left==0){
                        oriImage.setRGB(j, i,black.getRGB());
                        targetImage.setRGB(j-posX, i-posY,white.getRGB());
                    }
                    //下方是图区外,当前为下方边界,加重光亮
                    if (down==0){
                        oriImage.setRGB(j, i,white.getRGB());
                        targetImage.setRGB(j-posX, i-posY,white.getRGB());
                    }
                    //右侧是图区外,当前为右侧边界,加重光亮
                    if (right==0){
                        oriImage.setRGB(j, i,white.getRGB());
                        targetImage.setRGB(j-posX, i-posY,white.getRGB());
                    }

                }
//                else {
//                    //这里把背景设为透明
//                    targetImage.setRGB(j, i, rgb_ori & 0x00ffffff);
//                }
            }
        }
    }


    private static void readPixel(BufferedImage img, int x, int y, int[] pixels) {
        int xStart = x - 1;
        int yStart = y - 1;
        int current = 0;
        for (int i = xStart; i < 3 + xStart; i++)
            for (int j = yStart; j < 3 + yStart; j++) {
                int tx = i;
                if (tx < 0) {
                    tx = -tx;

                } else if (tx >= img.getWidth()) {
                    tx = x;
                }
                int ty = j;
                if (ty < 0) {
                    ty = -ty;
                } else if (ty >= img.getHeight()) {
                    ty = y;
                }
                pixels[current++] = img.getRGB(tx, ty);

            }
    }

    private static void fillMatrix(int[][] matrix, int[] values) {
        int filled = 0;
        for (int i = 0; i < matrix.length; i++) {
            int[] x = matrix[i];
            for (int j = 0; j < x.length; j++) {
                x[j] = values[filled++];
            }
        }
    }

    private static int avgMatrix(int[][] matrix) {
        int r = 0;
        int g = 0;
        int b = 0;
        for (int i = 0; i < matrix.length; i++) {
            int[] x = matrix[i];
            for (int j = 0; j < x.length; j++) {
                if (j == 1) {
                    continue;
                }
                Color c = new Color(x[j]);
                r += c.getRed();
                g += c.getGreen();
                b += c.getBlue();
            }
        }
        return new Color(r / 8, g / 8, b / 8).getRGB();
    }

    public static PuzzleInfo getPuzzleInfo() throws IOException {
        //随机背景图
        int i = new Random().nextInt(10);
        String path ="puzzle/source"+i+".jpg";

        //拼图原图
//        ClassPathResource classPathResource = new ClassPathResource(path);
        InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);


//        Image bi = ImageIO.read(classPathResource.getFile());
        Image bi = ImageIO.read(inputStream);

        //规范原图的大小
        BufferedImage oriImage=resizeImage(bi,bigWidth,bigHeight,true);
        //获取正方形的位置和边缘凹凸信息
        Integer[] pos = getPosAndOutline();


        //计算拼图的真实大小和拼图的位置信息
        Integer width=square+1;
        Integer height =square+1;
        if (pos[2]==2){
            //上方突出,真实高度加一个margin,真实坐标,正方形y坐标减去一个margin
            height+=margin;
            pos[9]=pos[1]-margin;
        }
        if (pos[3]==2){
            width+=margin;
            pos[8]=pos[0]-margin;
        }
        if (pos[4]==2){
            height+=margin;
        }
        if (pos[5]==2){
            width+=margin;
        }
        pos[6]=width;
        pos[7]=height;
        //创建拼图
        BufferedImage targetImage= new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
        //获取拼图的裁剪信息(0不要1裁剪)
        Integer[][] templateImage = getSmallImg(pos);
        //裁剪拼图
        cutByTemplate(oriImage,targetImage,templateImage,pos);

        PuzzleInfo puzzleInfo=new PuzzleInfo();
//        puzzleInfo.setBigImg(getImageBASE64(oriImage));
//        puzzleInfo.setSmallImg(getImageBASE64(targetImage));
        puzzleInfo.setBigImg(getImageBASE64(resizeImage(oriImage,bigWidth/scale,bigHeight/scale,true)));
        puzzleInfo.setSmallImg(getImageBASE64(resizeImage(targetImage,width/scale,height/scale,false)));
        puzzleInfo.setPosX(pos[8]==null?pos[0]/10:pos[8]/10);
        puzzleInfo.setPosY(pos[9]==null?pos[1]/10:pos[9]/10);
        puzzleInfo.setSmallWidth(width/10);
        puzzleInfo.setSmallHeight(height/10);
        puzzleInfo.setBigWidth(bigWidth/10);
        puzzleInfo.setBigHeight(bigHeight/10);
        return puzzleInfo;
    }


    public static void main(String[] args) throws IOException {
        File file1=new File("E:/img/small.png");
        File file2=new File("E:/img/big.jpg");
        File file= new File("E:/img/source.jpg");

        Image bi = ImageIO.read(file);
        BufferedImage oriImage=resizeImage(bi,bigWidth,bigHeight,true);

        Integer[] pos = getPosAndOutline();


        Integer width=square+1;
        Integer height =square+1;
        if (pos[2]==2){
            height+=margin;
            pos[9]=pos[1]-margin;
        }
        if (pos[3]==2){
            width+=margin;
            pos[8]=pos[0]-margin;
        }
        if (pos[4]==2){
            height+=margin;
        }
        if (pos[5]==2){
            width+=margin;
        }
        pos[6]=width;
        pos[7]=height;

        BufferedImage targetImage= new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);

        Integer[][] templateImage = getSmallImg(pos);

        cutByTemplate(oriImage,targetImage,templateImage,pos);
        ImageIO.write(oriImage, "jpg", file2);
        ImageIO.write(targetImage, "png", file1);
    }

}

2.接受结果的实体类
需要注意:
1.bigImg是大图的base64,smallImg是小图的base64。
2.posY 是小图相对大图的y坐标,posX是小图相对大图的x坐标,我们拿到PuzzleInfo 对象时,需要把posX保存在缓存中,并赋值0。
前端将移动距离传回后端,只需与缓存中的posX对比,差值的绝对值小于5即可。


import lombok.Data;

@Data
public class PuzzleInfo {
    private String bigImg;
    private String smallImg;
    private Integer posY;
    private Integer posX;
    private Integer smallWidth;
    private Integer smallHeight;
    private Integer bigWidth;
    private Integer bigHeight;
}

准备图片资源

保持格式和图片名如下,除非修改获取资源部分的代码,否则报错找不到图片资源。如果保持这样还报错,说明图片未编译到target目录,把target删除,重新编译。
在这里插入图片描述

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现滑动图片拼图验证的步骤如下: 1.准备两张图片,一张是完整的原图,另一张是被拆分的拼图。 2.使用Java的Graphics类将原图和拼图绘制到JPanel上。 3.使用Java的Random类生成一个随机位置,将拼图绘制到原图的随机位置。 4.使用Java的MouseAdapter类监听鼠标事件,当用户按下鼠标并拖动时,将拼图跟随鼠标移动。 5.使用Java的Rectangle类判断拼图是否在原图的正确位置,如果是,则验证通过,否则提示用户重新拼图。 下面是一个简单的Java代码示例: ```java import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Random; import javax.swing.*; public class SlidePuzzle extends JPanel { private Image img; // 原图 private Image puzzleImg; // 拼图 private Point puzzleLoc; // 拼图位置 private Rectangle puzzleRect; // 拼图矩形 private boolean isDragging; // 是否正在拖动 public SlidePuzzle() { // 加载图片 img = new ImageIcon("original.jpg").getImage(); puzzleImg = new ImageIcon("puzzle.jpg").getImage(); // 随机生成拼图位置 Random rand = new Random(); int x = rand.nextInt(img.getWidth(null) - puzzleImg.getWidth(null)); int y = rand.nextInt(img.getHeight(null) - puzzleImg.getHeight(null)); puzzleLoc = new Point(x, y); // 创建拼图矩形 puzzleRect = new Rectangle(puzzleLoc.x, puzzleLoc.y, puzzleImg.getWidth(null), puzzleImg.getHeight(null)); // 添加鼠标监听器 addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { if (puzzleRect.contains(e.getPoint())) { isDragging = true; } } public void mouseReleased(MouseEvent e) { if (isDragging) { isDragging = false; if (puzzleRect.intersects(new Rectangle(300, 300, 100, 100))) { JOptionPane.showMessageDialog(null, "验证通过!"); } else { JOptionPane.showMessageDialog(null, "拼图不正确,请重新拼图!"); } } } }); addMouseMotionListener(new MouseAdapter() { public void mouseDragged(MouseEvent e) { if (isDragging) { puzzleLoc.translate(e.getX() - puzzleRect.x, e.getY() - puzzleRect.y); puzzleRect.setLocation(puzzleLoc); repaint(); } } }); } public void paintComponent(Graphics g) { super.paintComponent(g); g.drawImage(img, 0, 0, null); g.drawImage(puzzleImg, puzzleLoc.x, puzzleLoc.y, null); } public static void main(String[] args) { JFrame frame = new JFrame("滑动拼图验证"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(600, 600); frame.add(new SlidePuzzle()); frame.setVisible(true); } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值