滑动解锁功能大家都不陌生,类似于如下这种:
公司近期也要求实现类似的功能,百度各种文章,总算实现了一个比较糙的版本,保密等原因就不贴完成图了。
我觉得难点在于滑块图和背景图的处理。
图片处理原理
我粗略的理解,图片就是一个二维的直角坐标系的一部分,(x,y)就是对应位置的一个像素点,可以操作某一个像素点,比如改变颜色,设置透明度等。
Java中使用BufferedImage完成图片处理等操作,先来段代码热热身:
/**
* 读取图片获取高宽
*/
String oldFileName = "E:\\images\\black_300_150.png";
BufferedImage image = ImageIO.read(new File(oldFileName));
int height = image.getHeight();
int width = image.getWidth();
在内存中生成图片,并保存到磁盘中:
BufferedImage dest = new BufferedImage(140,120,BufferedImage.TYPE_4BYTE_ABGR);
try {
FileOutputStream fos = new FileOutputStream(new File("E:\\images\\piece" + "_" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ".png"));
ImageIO.write(dest, "png", fos);
fos.flush();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
大概就是这样吧,抛个砖引一下各位的玉。
热完身上重点,就一句话:将背景图指定位置的像素值拷贝并设置到滑块图中,并将背景图抠图位置进行处理。
具体步骤:
1.创建内存中创建一个指定大小的滑块图。
2.根据背景图宽高和滑块图宽高随机设置一个抠图点。
3.复制背景图的抠图区域的像素值到滑块图上。
4.处理背景图抠图位置。
划块图形状
有多种方式实现,如果几何方面编码足够六,可以使用代码生成形状,也可以直接用美工生成一个透明背景的图片,下面的生成滑块图代码,是我抄来的:
static int targetLength=55;//小图长
static int targetWidth=45;//小图宽
static int circleR=6;//半径
static int r1=3;//距离点
private static int[][] getBlockData() {
int[][] data = new int[targetLength][targetWidth];
double x2 = targetLength-circleR;
//随机生成圆的位置
double h1 = circleR + Math.random() * (targetWidth-3*circleR-r1);
double po = circleR*circleR;
double xbegin = targetLength-circleR-r1;
double ybegin = targetWidth-circleR-r1;
for (int i = 0; i < targetLength; i++) {
for (int j = 0; j < targetWidth; j++) {
double d3 = Math.pow(i - x2,2) + Math.pow(j - h1,2);
double d2 = Math.pow(j-2,2) + Math.pow(i - h1,2);
if ((j <= ybegin && d2 <= po)||(i >= xbegin && d3 >= po)) {
data[i][j] = 0;
} else {
data[i][j] = 1;
}
}
}
return data;
}
如你所见,这是个二维数组,刚才说过一个图片就是一个平面直角坐标系,而二维数组就相当于一个图片。
当然也可以根据美工提供的透明背景图片来反向分析出此二维数组,这里利用的原理是图片每个RGB的高8位为0代表透明:
/**
* 根据切图图片转化为切图数组
* @param image 切图图片
*/
public static int[][] convertAryByImage(BufferedImage image) {
int[][] blockData = null;
if (image != null) {
int width = image.getWidth();
int height = image.getHeight();
blockData = new int[width][height];
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
int rgb = image.getRGB(i, j);
int x = rgb & 0xff000000;
blockData[i][j] = x == 0 ? 0 : 1;
}
}
}
return blockData;
}
背景图处理
有了上一步的二维数据,那么我们就可以开始抠图了:
/**
* oriImage 原图
targetImage 滑块图
templateImage 数组
x,y 在原图中抠图位置
*/
private static void cutByTemplate(BufferedImage oriImage,BufferedImage targetImage, int[][] templateImage, int x,int y){
for (int i = 0; i < targetImage.getWidth(); i++) {
for (int j = 0; j < targetImage.getHeight(); j++) {
int rgb = templateImage[i][j];
// 原图中对应位置变色处理
int rgb_ori = oriImage.getRGB(x + i, y + j);
if (rgb == 1) {
//抠图上复制对应颜色值
targetImage.setRGB(i, j, rgb_ori);
//原图对应位置颜色变化
oriImage.setRGB(x + i, y + j, rgb_ori & 0x363636 );
}else{
//这里把背景设为透明
targetImage.setRGB(i, j, rgb_ori & 0x00ffffff);
}
}
}
}
处理完之后滑块图大概是这个样子的:
原图是这样的: