JavaFX实现水波效果.docx
《JavaFX实现水波效果.docx》由会员分享,可在线阅读,更多相关《JavaFX实现水波效果.docx(11页珍藏版)》请在冰豆网上搜索。
![JavaFX实现水波效果.docx](https://file1.bdocx.com/fileroot1/2023-2/3/fbf2c227-9456-45eb-85cb-946bec7d7a4d/fbf2c227-9456-45eb-85cb-946bec7d7a4d1.gif)
JavaFX实现水波效果
JavaFX实现水波效果
publicclassJfxRipperextendsApplication{
privatefinaldoubleMAX_WIDTH=400.0;
privatefinaldoubleMAX_HEIGHT=400.0;
privatestaticAnimationTimertimer;
privatestaticAnimationTimerrainTimer;
privateFileChooserfc;
privatedoublescaleRate;
privateintmaxRainSize,maxRainPower,stoneSize,stonePower;
privateintimgWidth,imgHeight;
int[]arrWaveCurrent;//当前帧各点波动能量数据
int[]arrWaveNext;//下一帧各点波动能量数据
privateImageimg;
privateWritableImageimgRipper;
privateImageViewimgView;
@Override
publicvoidinit()throwsException{
fc=newFileChooser();
//添加文件类型过滤
fc.getExtensionFilters().add(
newFileChooser.ExtensionFilter("图片文件","*.jpg;*.png;*.jpeg;*.bmp"));
img=newImage(getClass().getClassLoader().getResourceAsStream("images/
红花.jpg"));
imgWidth=(int)img.getWidth();
imgHeight=(int)img.getHeight();
//波纹效果计算需要遍历图片每个像素,图片尺寸过大会导致运行速度极慢,故按比
例缩小
if(imgWidth>MAX_WIDTH||imgHeight>MAX_HEIGHT){
doublesx=MAX_WIDTH/imgWidth;
doublesy=MAX_HEIGHT/imgHeight;
scaleRate=sx>sy?
sy:
sx;
img=scaleImg(img,imgWidth,imgHeight,scaleRate);
imgWidth=(int)(scaleRate*(double)imgWidth);
imgHeight=(int)(scaleRate*(double)imgHeight);
maxRainSize=4;
maxRainPower=60;
stoneSize=5;
stonePower=128;
}else{//雨点和石子大小根据图片尺寸的大小稍作调整
maxRainSize=3;
maxRainPower=50;
stoneSize=4;
stonePower=108;
}
imgRipper=newWritableImage(imgWidth,imgHeight);
arrWaveCurrent=newint[imgWidth*imgHeight];
arrWaveNext=newint[imgWidth*imgHeight];
imgView=newImageView();
imgView.setOnMousePressed(newEventHandler(){
@Override
publicvoidhandle(Eventevent){
MouseEvente=(MouseEvent)event;
dropStone((int)e.getX(),(int)e.getY(),stoneSize,stonePower);
}
});
timer=newAnimationTimer(){
@Override
publicvoidhandle(longnow){
genRipper();
imgView.setImage(imgRipper);
}
};
rainTimer=newAnimationTimer(){
@Override
publicvoidhandle(longnow){
rain();
imgView.setImage(imgRipper);
}
};
}
/**
*模拟下雨,随机抛出不同大小和位置的石子
*/
protectedvoidrain(){
intx=(int)(Math.random()*imgWidth);
inty=(int)(Math.random()*imgHeight);
intrainSize=(int)(Math.random()*maxRainSize);
intpower=(int)(Math.random()*maxRainPower)+maxRainPower;
dropStone(x,y,rainSize,power);
}
@Override
publicvoidstart(StageprimaryStage)throwsException{
Grouproot=newGroup();
VBoxvb=newVBox(10);
root.getChildren().add(vb);
imgView.setImage(img);
Scenescene=newScene(root);
primaryStage.setScene(scene);
HBoxhb=newHBox(10);
ButtonrainControl=newButton("停止下雨");
rainControl.setOnAction((ActionEventevent)->{
if("开始下雨".equals(rainControl.getText())){
rainTimer.start();
rainControl.setText("停止下雨");
}else{
rainTimer.stop();
rainControl.setText("开始下雨");
}
});
ButtonchangePic=newButton("更换图片");
changePic.setOnAction((ActionEventevent)->{
Filef=fc.showOpenDialog(primaryStage);
if(null!
=f){
//将默认目录设置为上次访问过的目录
fc.setInitialDirectory(f.getParentFile());
//ImageimgTmp=null;
try{
img=newImage(newFileInputStream(f));
//Desktop.getDesktop().open(f);
}catch(Exceptione){
e.printStackTrace();
}
if(img!
=null){
imgWidth=(int)img.getWidth();
imgHeight=(int)img.getHeight();
if(imgWidth>MAX_WIDTH||imgHeight>MAX_HEIGHT){
doublesx=MAX_WIDTH/imgWidth;
doublesy=MAX_HEIGHT/imgHeight;
scaleRate=sx>sy?
sy:
sx;
img=scaleImg(img,imgWidth,imgHeight,scaleRate);
imgWidth=(int)(scaleRate*(double)imgWidth);
imgHeight=(int)(scaleRate*(double)imgHeight);
maxRainSize=4;
maxRainPower=60;
stoneSize=5;
stonePower=128;
}else{
maxRainSize=3;
maxRainPower=50;
stoneSize=4;
stonePower=108;
}
imgRipper=newWritableImage(imgWidth,imgHeight);
arrWaveCurrent=newint[imgWidth*imgHeight];
arrWaveNext=newint[imgWidth*imgHeight];
imgView.setFitHeight(imgHeight);
imgView.setFitWidth(imgWidth);
primaryStage.setWidth(imgWidth);
imgView.setImage(img);
primaryStage.setHeight(imgHeight+40);
}
}
});
Buttonexit=newButton("退出");
exit.setOnAction((ActionEventevent)->{
primaryStage.close();
});
hb.getChildren().addAll(rainControl,changePic,exit);
vb.getChildren().addAll(imgView,hb);
primaryStage.setWidth(imgWidth);
hb.setAlignment(Pos.CENTER);
scene.setFill(Color.BLUEVIOLET);
primaryStage.initStyle(StageStyle.TRANSPARENT);
timer.start();
rainTimer.start();
primaryStage.show();
}
/*
*水波算法,原作者Imagic
*/
publicvoidgenRipper(){
PixelReaderreadImg=img.getPixelReader();
PixelWriterwriteRipper=imgRipper.getPixelWriter();
intindex=imgWidth;
intindexPreX=index-1;
intindexNextX=index+1;
intindexPreY=index-imgWidth;
intindexNextY=index+imgWidth;
for(inty=1;yfor(intx=1;xintx1=arrWaveCurrent[indexPreX++];
intx2=arrWaveCurrent[indexNextX++];
intx3=arrWaveCurrent[indexPreY++];
intx4=arrWaveCurrent[indexNextY++];
//波能扩散:
上下左右四点的波幅和的一半减去当前波幅
//X0'=,X1+X2+X3+X4,/2-X0
arrWaveNext[index]=((x1+x2+x3+x4)>>1)-arrWaveNext[index];
//波能衰减1/32
arrWaveNext[index]-=arrWaveNext[index]>>5;
//计算出偏移象素和原始象素的内存地址偏移量:
intxoffset=x2-x1;
intyoffset=x4-x3;
intoffset=imgWidth*yoffset+xoffset;
intposY=index/imgWidth;
intposX=index-posY*imgWidth;
intnewY=(index+offset)/imgWidth;
intnewX=(index+offset)-newY*imgWidth;
//判断坐标是否在窗口范围内
if(index+offset>0&&index+offsetwriteRipper.setColor(posX,posY,readImg.getColor(newX,newY));
}else{
writeRipper.setColor(posX,posY,readImg.getColor(posX,posY));
}
}
}
//交换波能数据缓冲区
int[]temp=arrWaveCurrent;
arrWaveCurrent=arrWaveNext;
arrWaveNext=temp;
}
/**
*模拟向水中投石子
*
*@paramx:
石子位置X坐标
*@paramy:
石子位置y坐标
*@paramstoneSize:
石子半径
*@parampower:
波能大小
*/
privatevoiddropStone(intx,inty,intstoneSize,intpower){
intminPosX=x-stoneSize,maxPosX=x+stoneSize;
intminPosY=y-stoneSize,maxPosY=y+stoneSize;
minPosX=minPosX<0?
0:
minPosX;
minPosY=minPosY<0?
0:
minPosY;
maxPosX=maxPosX>imgWidth?
imgWidth:
maxPosX;
maxPosY=maxPosY>imgHeight?
imgHeight:
maxPosY;
intvalue=stoneSize*stoneSize;
for(intposx=minPosX;posxfor(intposy=minPosY;posyif((posx-x)*(posx-x)+(posy-y)*(posy-y)arrWaveCurrent[posy*imgWidth+posx]=-power;
}
}
}
}
/**
*按比例缩小图片
*缩小图片算法:
x0=x/sx,y0=y/sy。
x0,y0分别为原图水平和垂直索引,
*sx,sy分别为水平和垂直缩小比例
*@paramsrc:
原始图片
*@paramimgW:
原始图片宽度
*@paramimgH:
原始图片长度
*@paramrate:
缩小后的图片不原图片尺寸的比例
*@return缩小后的图片
*/
privateImagescaleImg(Imagesrc,intimgW,intimgH,doublerate){
intwidth=(int)((double)imgW*rate);
intheight=(int)((double)imgH*rate);
WritableImageimgScaled=newWritableImage(width,height);
PixelReaderreadSrc=src.getPixelReader();
PixelWriterwriteDest=imgScaled.getPixelWriter();
for(intx=0;xfor(inty=0;ywriteDest.setColor(x,y,
readSrc.getColor((int)(x/rate),(int)(y/rate)));
}
}
returnimgScaled;
}
publicstaticvoidmain(String[]args){
launch(args);
}
}
里面用到了行序优先一维数组存储二维数组时两个数组索引之间的算术关系公式。
设一维数组索引为index,二维数组行索引为x,列索引为y,均从0开始,每行存储元素个数为width,则:
y=int(index/width),int为取整函数,如int(1.1)和int(1.9)都等于1x=index-y*width
index=width*y+x
截图: