1 /** 2 * Java 动态写轮眼 SharingganJPanel (整理) 3 * 4 * 2016-1-2 深圳 南山平山村 曾剑锋 5 * 设计声明: 6 * 1、虽然岸本是日本人,而我个人作为其模仿者,依然想表示个人对其的敬意,希望作品 7 * 不会玷污写轮眼的名声。 8 * 2、由于本次设计包含过多数学计算,所以强烈建议您通过《注意事项》中的5查看图是 9 * 如何设计的,先不要看源码,然后自己试图去设计,而您仅需要知道:计算机计算 10 * 开方、三角函数、反三角等会造成误差,仅此而已。 11 * 3、由于我本人在《动态中国太极图——Java原创》中改了一个注释错误,导致《动态中国 12 * 太极图——Java原创》不能再次通过系统的审批,不知何故,所以导致大家可能看不 13 * 到《动态中国太极图——Java原创》里面详细的注释,深表遗憾。同时也对我自己没 14 * 能认真做好本质工作表示深深的反思。 15 * 4、如果您对这个很好奇,那么请现查阅本人发表的《动态中国太极图——Java原创》(不 16 * 知道还能不能通过审批)、《动态眨眼效果——Java原创》,因为那两个是这个设计 17 * 的引导+铺垫。 18 * 19 * 20 * 注意事项: 21 * 1、本程序为java程序,同时感谢您花费宝贵的时间来阅读本文档; 22 * 2、请注意包名为:practice,文件名为:SharinganJFrame(Sharingan是写轮眼的英文), 23 * 注意检查,以防一些不必要的麻烦; 24 * 3、在完成确认2中的内容后,本程序可以直接运行,因为本软件说明都在注释中; 25 * 4、由于本设计是继《动态中国太极图——Java原创》、《动态眨眼效果——Java原创》后编 26 * 写,所以在那里面已说明的东西,不再详述,同时由于个人能力、阅历等原因,有 27 * 些细节可能并没有详细阐述,或者有疏漏,请谅解。 28 * 5、您可以注释paint()方法中的一些代码,因为效果很直观,也许这样您理解得更快, 29 建议如下: 30 * 5.1 先看paint()方法第1条语句效果,把paint()方法中的其它语句注释掉; 31 * 5.2 再看paint()方法第1、2条语句效果,把paint()方法中的其它语句注释掉; 32 * 5.3 再看paint()方法第1、2、3条语句效果,把paint()方法中的其它语句注释掉; 33 * 5.4 以上面的方法类推,直到paint()方法中没有语句被注释,相信等您看完,您就 34 * 理解了。 35 * 36 * 设计目标:在一个JFrame中绘制一个动态的写轮眼(动漫《火影》中的特殊眼睛)。 37 * 38 * 设计说明: 39 * 相信很多人不喜欢看那些很长文字的说明,所以本人只简单介绍一下个人想法及遇到的一 40 * 些问题: 41 * 1、设计的源头来自我和同桌(程梦真)无意中提到写轮眼,所以打算把它用Java绘出来, 42 * 而且要是动态的; 43 * 2、目前的版本的写轮眼和最初设计的写轮眼有很大差别,很多单词的定义已经没有了 44 * 当初的味道。 45 * 3、绘制这个动态的写轮眼的时候遇到最严重的的问题是计算误差,就是苍老师说的 46 * 精确但不准确,所以angleErr是用来做误差补偿的,当然这个还是不是很准确,没 47 * 有更深入的去探究,由于本来就是娱乐,所以没打算去深究,如果你感兴趣,想去 48 * 琢磨,请注意一下angleErr所出现的地方,本人因为这个误差,重画了进3遍才找 49 * 出根本原因。 50 * 51 * 2014-1-13 星期一 晴 8度 微风 南昌 52 * 53 */ 54 package demo; 55 56 import java.awt.Color; 57 import java.awt.Graphics; 58 59 import javax.swing.JFrame; 60 import javax.swing.JPanel; 61 62 public class SharinganJPanel extends JPanel { 63 //设置中心坐标,个人喜欢一种心点作为物体的坐标,物理里面成为质点 64 static int centerX = 600/2; 65 static int centerY = 600/2; 66 //外圆半径,写轮眼转动所依赖的红线 67 double extendCircleSemi = 25; // double extendCircleSemi = 200; 68 //写轮眼的旋转部分有一个固定的大小的圆,刚开始设计的时候是定义为白色的,后来改为了红色,好看 ^_^ 69 double whiteOfSharingan = extendCircleSemi/3; // double whiteOfSharingan = extendCircleSemi/6; 70 //在上面的红色的圆旁边有一个随着旋转角度变大而变大的圆,其半径为miniCircleSemi 71 double miniCircleSemi = 0; 72 //这里用到了绘制太极时的思路,这个圆和上面的两个圆(whiteOfSharingan和miniCircleSemi)外切 73 double sharinganSemi = 0; 74 //旋转度角范围的变量,您可以通过观察旋转的循环角度是120*2 75 int angleCircle = 0; 76 //小圆(miniCircleSemi)和白圆(whiteOfSharingan)在外圆(extendCircleSemi)上的角度 77 double angleOfWhiteMini = 0; 78 //写轮眼沟玉圆(sharinganSemi)和白圆(whiteOfSharingan)在外圆(extendCircleSemi)的角度 79 double angleOfwhiteShar = 0; 80 //用于判断圆是否到达极限位置,还句话说就是是否到达了正确的角度,进行下一次循环 81 boolean flag = true; 82 //沟玉球心到弦(whiteOfSharingan圆中心和miniCircleSemi圆中心所连成的线)中心距离; 83 double distansOfWhiteShar =0; 84 //沟玉球心到中心点的距离,这一步主要是为了计算坐标 85 double distansOfSharSemi = 0; 86 //在绘图过程中,由于计算机计算开方、三角函数、反三角等造成了误差,这是一个补偿值,但要注意真正补偿的 87 //误差为这个误差的angleErr*2/3,因为这个值是本人通过理想设置临界状态得到的值。 88 double angleErr = 0.02500260489936114; 89 //沟玉球(whiteOfSharingan)中的黑球半径,这个值是动态的 90 int blackOfWhiteSemi = 0; 91 //中心眼睛所占的长度 92 int eyeLength = 300; 93 //眼睛幅值 94 int amplitude = 50; 95 96 public SharinganJPanel() { 97 98 startRun(); 99 } 100 101 @Override 102 public void paint(Graphics graphics) { 103 System.out.println(); 104 super.paint(graphics); 105 this.setBackground(Color.black); 106 //绘制一个眼睛作为背景 107 graphics.setColor(Color.red); 108 for (int i = 0; i < eyeLength; i++) { 109 graphics.drawLine( centerX-eyeLength/2+i, 110 centerY-(int)(Math.sin(Math.PI*i/eyeLength)*amplitude), 111 centerX-eyeLength/2+i, 112 centerY+(int)(Math.sin(Math.PI*i/eyeLength)*amplitude)); 113 } 114 //在眼睛的中心绘制一个黑色的圆 115 graphics.setColor(Color.black); 116 graphics.fillOval( centerX-90/2, 117 centerY-90/2, 118 90, 119 90); 120 //在眼睛的中心绘制一个白色的圆 121 graphics.setColor(Color.white); 122 graphics.fillOval( centerX-60/2/2/2, 123 centerY-60/2/2/2, 124 60/2/2, 125 60/2/2); 126 //用于绘制3个不同角度、在不断变化、不同位置的太极图,图是有顺序的 127 for (int i = 0; i < 3; i++) { 128 //绘制写轮眼球(sharinganSemi) 129 graphics.setColor(Color.red); 130 graphics.fillArc( (int)(centerX-sharinganSemi+Math.cos(Math.PI*(angleCircle+90+(i*120))/180-angleOfWhiteMini/2+angleOfwhiteShar+angleErr*2/3)*distansOfSharSemi), 131 (int)(centerY-sharinganSemi-Math.sin(Math.PI*(angleCircle+90+(i*120))/180-angleOfWhiteMini/2+angleOfwhiteShar+angleErr*2/3)*distansOfSharSemi), 132 (int)(sharinganSemi*2), 133 (int)(sharinganSemi*2),angleCircle+i*120,180); 134 //绘制写轮眼固定圆(whiteOfSharingan) 135 graphics.setColor(Color.red); 136 graphics.fillOval( (int)(centerX-whiteOfSharingan+Math.cos(Math.PI*(angleCircle+90+(i*120))/180)*extendCircleSemi), 137 (int)(centerY-whiteOfSharingan-Math.sin(Math.PI*(angleCircle+90+(i*120))/180)*extendCircleSemi), 138 (int)(whiteOfSharingan*2), 139 (int)(whiteOfSharingan*2)); 140 //绘制写轮眼黑球(miniCircleSemi) 141 graphics.setColor(Color.black); 142 graphics.fillOval( (int)(centerX-miniCircleSemi+Math.cos(Math.PI*(angleCircle+90+(i*120))/180-angleOfWhiteMini)*extendCircleSemi), 143 (int)(centerY-miniCircleSemi-Math.sin(Math.PI*(angleCircle+90+(i*120))/180-angleOfWhiteMini)*extendCircleSemi), 144 (int)(miniCircleSemi*2), 145 (int)(miniCircleSemi*2)); 146 //绘制写轮眼固定球内黑球(blackOfWhiteSemi) 147 graphics.setColor(Color.black); 148 graphics.fillOval( (int)(centerX-(blackOfWhiteSemi+0.0)/120*whiteOfSharingan/2+Math.cos(Math.PI*(angleCircle+90+(i*120))/180)*extendCircleSemi), 149 (int)(centerY-(blackOfWhiteSemi+0.0)/120*whiteOfSharingan/2-Math.sin(Math.PI*(angleCircle+90+(i*120))/180)*extendCircleSemi), 150 (int)((blackOfWhiteSemi+0.0)/120*whiteOfSharingan), 151 (int)((blackOfWhiteSemi+0.0)/120*whiteOfSharingan)); 152 } 153 //绘制外圆 154 graphics.setColor(Color.red); 155 graphics.drawOval( (int)(centerX-extendCircleSemi), 156 (int)(centerY-extendCircleSemi), 157 (int)(extendCircleSemi)*2, 158 (int)(extendCircleSemi)*2); 159 } 160 public void startRun() { 161 new Thread(){ 162 public void run() { 163 while(true){ 164 if (flag) { 165 angleCircle += 2 ; 166 //沟玉白球中的黑球半径 167 blackOfWhiteSemi = angleCircle; 168 //System.out.println(angleCircle); 169 //根据目前写轮眼沟玉球转过角度来确定mini小球目前的对应的半径 170 miniCircleSemi = (angleCircle+0.0)/120*whiteOfSharingan; 171 //System.out.println(miniCircleSemi); 172 //沟玉球半径 173 sharinganSemi = miniCircleSemi+whiteOfSharingan; 174 //System.out.println(sharinganSemi); 175 //由于白球和mini小球都是在外圆上,所以通过弦对应的角度来求的小圆落后于白球的角度 176 angleOfWhiteMini = Math.asin(sharinganSemi/2/extendCircleSemi)*2; 177 //System.out.println(angleOfWhiteMini); 178 //沟玉球心到白球中心距离 179 distansOfWhiteShar = (whiteOfSharingan-miniCircleSemi)/2; 180 //沟玉球心到中心点的距离 181 distansOfSharSemi = Math.sqrt( extendCircleSemi*extendCircleSemi 182 -((whiteOfSharingan+miniCircleSemi)/2)*((whiteOfSharingan+miniCircleSemi)/2) 183 +((whiteOfSharingan-miniCircleSemi)/2)*((whiteOfSharingan-miniCircleSemi)/2)); 184 //沟玉球心和白求在中心圆上所成的角度 185 //通过这里可以可以求出由于计算机计算产生的计算误差为(0.02500260489936114) 186 //System.out.println(distansOfSharSemi); 187 angleOfwhiteShar = Math.asin(distansOfWhiteShar/2/distansOfSharSemi); 188 //System.out.println(angleOfwhiteShar); 189 if (angleCircle == 120) { 190 flag = false; 191 } 192 }else { 193 angleCircle += 2; 194 //沟玉白球中的黑球半径 195 blackOfWhiteSemi = 240-angleCircle; 196 //根据目前写轮眼沟玉球转过角度来确定mini小球目前的对应的半径 197 miniCircleSemi = (240.0-angleCircle)/120*whiteOfSharingan; 198 //miniCircleSemi = (angleCircle+0.0)/120*whiteOfSharingan; 199 //沟玉球半径 200 sharinganSemi = miniCircleSemi+whiteOfSharingan; 201 //由于白球和mini小球都是在外圆上,所以通过弦对应的角度来求的小圆落后于白球的角度 202 angleOfWhiteMini = Math.asin(sharinganSemi/2/extendCircleSemi)*2; 203 //沟玉球心到白球中心距离 204 distansOfWhiteShar = (whiteOfSharingan-miniCircleSemi)/2; 205 //沟玉球心到中心点的距离 206 distansOfSharSemi = Math.sqrt( extendCircleSemi*extendCircleSemi 207 -((whiteOfSharingan+miniCircleSemi)/2)*((whiteOfSharingan+miniCircleSemi)/2) 208 +((whiteOfSharingan-miniCircleSemi)/2)*((whiteOfSharingan-miniCircleSemi)/2)); 209 //沟玉球心和白求在中心圆上所成的角度 210 angleOfwhiteShar = Math.asin(distansOfWhiteShar/2/distansOfSharSemi); 211 if (angleCircle == 240) { 212 angleCircle = 0; 213 flag = true; 214 } 215 } 216 try { 217 Thread.sleep(20); 218 } catch (InterruptedException e) { 219 e.printStackTrace(); 220 } 221 repaint(); 222 } 223 }; 224 }.start(); 225 } 226 public static void main(String[] args) { 227 JFrame jFrame = new JFrame(); 228 jFrame.setTitle("Sharingan"); 229 jFrame.setSize(centerX*2, centerY*2); 230 jFrame.getContentPane().setBackground(Color.black); 231 jFrame.setLocationRelativeTo(null); 232 jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 233 234 JPanel jPanel = new SharinganJPanel(); 235 jFrame.add(jPanel); 236 jFrame.setVisible(true); 237 } 238 }
效果如图: