对于一些复杂或不规则的UI,我们可能无法通过组合其它组件的方式来实现,比如我们需要一个

  • 正六边形
  • 一个渐变的圆形进度条
  • 一个棋盘等。

当然,有时候我们可以使用图片来实现,但在一些需要动态交互的场景静态图片也是实现不了的,比如要实现一个手写输入面板,这时,我们就需要来自己绘制UI外观

几乎所有的UI系统都会提供一个自绘UI的接口,这个接口通常会提供一块2D画布Canvas,Canvas内部封装了一些基本绘制的API,开发者可以通过Canvas绘制各种自定义图形。在Flutter中,提供了一个CustomPaint 组件,它可以结合画笔CustomPainter来实现自定义图形绘制。

CustomPaint

我们看看CustomPaint构造函数:

  1. CustomPaint({
  2. Key key,
  3. this.painter,
  4. this.foregroundPainter,
  5. this.size = Size.zero,
  6. this.isComplex = false,
  7. this.willChange = false,
  8. Widget child, //子节点,可以为空
  9. })
  • painter: 背景画笔,会显示在子节点后面;
  • foregroundPainter: 前景画笔,会显示在子节点前面
  • size:
    • 当child为null时,代表默认绘制区域大小
    • 如果有child则忽略此参数,画布尺寸则为child尺寸
    • 如果有child但是想指定画布为特定大小,可以使用SizeBox包裹CustomPaint实现。
  • isComplex:是否复杂的绘制,如果是,Flutter会应用一些缓存策略来减少重复渲染的开销。
  • willChange:和isComplex配合使用,当启用缓存时,该属性代表在下一帧中绘制是否会改变。

可以看到,绘制时我们需要提供前景或背景画笔,两者也可以同时提供。我们的画笔需要继承CustomPainter类,我们在画笔类中实现真正的绘制逻辑

注意

如果CustomPaint有子节点,为了避免子节点不必要的重绘并提高性能,通常情况下都会将子节点包裹在RepaintBoundary组件中,这样会在绘制时就会创建一个新的绘制层(Layer),其子组件将在新的绘制层(Layer)上绘制,而父组件将在原来Layer上绘制,也就是说RepaintBoundary 子组件的绘制将独立于父组件的绘制,RepaintBoundary会隔离其子节点和CustomPaint本身的绘制边界。示例如下:

  1. CustomPaint(
  2. size: Size(300, 300), //指定画布大小
  3. painter: MyPainter(),
  4. child: RepaintBoundary(child:...)),
  5. )

CustomPainter

CustomPainter中提定义了一个虚函数paint:

  1. void paint(Canvas canvas, Size size);

paint有两个参数:

  • Canvas:一个画布,包括各种绘制方法,我们列出一下常用的方法:
    • drawLine 画线
    • drawPoint 画点
    • drawPath 画路径
    • drawImage 画图像
    • drawRect 画矩形
    • drawCircle 画圆
    • drawOval 画椭圆
    • drawArc 画圆弧
  • Size:当前绘制区域大小

画笔Paint

现在画布有了,我们最后还缺一个画笔,Flutter提供了Paint类来实现画笔。在Paint中,我们可以配置画笔的各种属性如粗细、颜色、样式等。如:

  1. var paint = Paint() //创建一个画笔并配置其属性
  2. ..isAntiAlias = true //是否抗锯齿
  3. ..style = PaintingStyle.fill //画笔样式:填充
  4. ..color=Color(0x77cdb175);//画笔颜色

更多的配置属性读者可以参考Paint类定义。

示例:五子棋/盘

下面我们通过一个五子棋游戏中棋盘和棋子的绘制来演示自绘UI的过程,首先我们看一下我们的目标效果,如图所示:
自绘组件 (CustomPaint与Canvas) - 图1
代码:

  1. import 'package:flutter/material.dart';
  2. import 'dart:math';
  3. void main() => runApp(new MyApp());
  4. class MyApp extends StatelessWidget {
  5. @override
  6. Widget build(BuildContext context) {
  7. return MaterialApp(
  8. debugShowCheckedModeBanner: true,
  9. title: 'CustomPaintRoute',
  10. theme: ThemeData(
  11. primarySwatch: Colors.blue,
  12. ),
  13. home: Scaffold(
  14. appBar: AppBar(title: Text("CustomPaintRoute")),
  15. body: CustomPaintRoute(),
  16. ));
  17. }
  18. }
  19. class CustomPaintRoute extends StatelessWidget {
  20. @override
  21. Widget build(BuildContext context) {
  22. return Center(
  23. child: CustomPaint(
  24. size: Size(300, 300), //指定画布大小
  25. painter: MyPainter(),
  26. ),
  27. );
  28. }
  29. }
  30. class MyPainter extends CustomPainter {
  31. @override
  32. void paint(Canvas canvas, Size size) {
  33. double eWidth = size.width / 15;
  34. double eHeight = size.height / 15;
  35. //画棋盘背景
  36. var paint = Paint()
  37. ..isAntiAlias = true
  38. ..style = PaintingStyle.fill //填充
  39. ..color = Color(0x77cdb175); //背景为纸黄色
  40. canvas.drawRect(Offset.zero & size, paint);
  41. //画棋盘网格
  42. paint
  43. ..style = PaintingStyle.stroke //线
  44. ..color = Colors.black87
  45. ..strokeWidth = 1.0;
  46. for (int i = 0; i <= 15; ++i) {
  47. double dy = eHeight * i;
  48. canvas.drawLine(Offset(0, dy), Offset(size.width, dy), paint);
  49. }
  50. for (int i = 0; i <= 15; ++i) {
  51. double dx = eWidth * i;
  52. canvas.drawLine(Offset(dx, 0), Offset(dx, size.height), paint);
  53. }
  54. //画一个黑子
  55. paint
  56. ..style = PaintingStyle.fill
  57. ..color = Colors.black;
  58. canvas.drawCircle(
  59. Offset(size.width / 2 - eWidth / 2, size.height / 2 - eHeight / 2),
  60. min(eWidth / 2, eHeight / 2) - 2,
  61. paint,
  62. );
  63. //画一个白子
  64. paint.color = Colors.white;
  65. canvas.drawCircle(
  66. Offset(size.width / 2 + eWidth / 2, size.height / 2 - eHeight / 2),
  67. min(eWidth / 2, eHeight / 2) - 2,
  68. paint,
  69. );
  70. }
  71. //在实际场景中正确利用此回调可以避免重绘开销,本示例我们简单的返回true
  72. @override
  73. bool shouldRepaint(CustomPainter oldDelegate) => true;
  74. }

性能

绘制是比较昂贵的操作,所以我们在实现自绘控件时应该考虑到性能开销,下面是两条关于性能优化的建议:

  • 尽可能的利用好shouldRepaint返回值:在UI树重新build时,控件在绘制前都会先调用该方法以确定是否有必要重绘;
    • 假如我们绘制的UI不依赖外部状态,那么就应该始终返回false,因为外部状态改变导致重新build时不会影响我们的UI外观;
    • 如果绘制依赖外部状态,那么我们就应该在shouldRepaint中判断依赖的状态是否改变,如果已改变则应返回true来重绘,反之则应返回false不需要重绘。
  • 绘制尽可能多的分层:在上面五子棋的示例中,我们将棋盘和棋子的绘制放在了一起,这样会有一个问题:由于棋盘始终是不变的,用户每次落子时变的只是棋子,但是如果按照上面的代码来实现,每次绘制棋子时都要重新绘制一次棋盘,这是没必要的。优化的方法就是将棋盘单独抽为一个组件,并设置其shouldRepaint回调值为false,然后将棋盘组件作为背景。然后将棋子的绘制放到另一个组件中,这样每次落子时只需要绘制棋子。

    总结

    自绘控件非常强大,理论上可以实现任何2D图形外观,实际上Flutter提供的所有组件最终都是通过调用Canvas绘制出来的,只不过绘制的逻辑被封装起来了,读者有兴趣可以查看具有外观样式的组件源码,找到其对应的RenderObject对象,

  • 如Text对应的RenderParagraph对象最终会通过Canvas实现文本绘制逻辑

对比HTML+JS中的canvas

  1. <style>
  2. canvas { background:rgba(222, 222, 222, 0.3) }
  3. div { background: gray;float: left;}
  4. </style>
  5. <div></div><canvas id="myCanvas" width="400px" height="400px"></canvas></div>
  6. <div><canvas id="mycanvas2" width="600px" height="600px"></canvas></div>
  7. <div><canvas id="mycanvas3" width="800px" height="500px" style="background: red;"></canvas></div>
  8. <div> <canvas id="mycanvas4" width="500px" height="500px"></canvas></div>
  9. <script>
  10. var c = document.querySelector("#myCanvas");
  11. var ctx = c.getContext("2d");
  12. var cX = 200; //y轴坐标
  13. var cY = 200; //x轴坐标
  14. var r = 200; //半径
  15. var deg = 0; //角度
  16. var angle = deg * Math.PI / 180; //弧度
  17. var start = Math.PI / 2;
  18. var end = start + Math.PI;
  19. var ang = Math.PI / 180; //弧度
  20. function drawCirle(ctx, x, y, r, start, end, color = "#000", anti = false) {
  21. ctx.beginPath();
  22. ctx.fillStyle = color;
  23. ctx.arc(x, y, r, start, end, anti);
  24. ctx.fill();
  25. }
  26. setInterval(function () {
  27. ctx.clearRect(0, 0, c.width, c.height);
  28. drawCirle(ctx, cX, cY, r, start + deg * ang, end + deg * ang, "#000", false);
  29. drawCirle(ctx, cX, cY, r, start + deg * ang, end + deg * ang, "#fff", true);
  30. drawCirle(ctx, cX + (r / 2) * Math.sin(deg * ang), cY - (r / 2) * Math.cos(deg * ang), r / 2, 0, Math.PI * 2, "#000", true);
  31. drawCirle(ctx, cX - (r / 2) * Math.sin(deg * ang), cY + (r / 2) * Math.cos(deg * ang), r / 2, 0, Math.PI * 2, "#fff", false);
  32. drawCirle(ctx, cX + (r / 2) * Math.sin(deg * ang), cY - (r / 2) * Math.cos(deg * ang), 20, 0, Math.PI * 2, "#fff", true);
  33. drawCirle(ctx, cX - (r / 2) * Math.sin(deg * ang), cY + (r / 2) * Math.cos(deg * ang), 20, 0, Math.PI * 2, "#000", true);
  34. deg++;
  35. c.style.transform = "rotate(" + deg + "deg)";
  36. }, 100);
  37. </script>
  38. <script>
  39. var mycanvas = document.querySelector("#mycanvas2");
  40. var ctx2 = mycanvas.getContext("2d");
  41. setInterval(function () {
  42. ctx2.beginPath();
  43. ctx2.strokeStyle = "rgb(" + rand(0, 255) + "," + rand(0, 255) + "," + rand(0, 255) + ")";
  44. ctx2.lineWidth = 3;
  45. ctx2.moveTo(rand(0, 500), rand(0, 500));
  46. ctx2.lineTo(rand(0, 600), rand(0, 600));
  47. ctx2.stroke();
  48. }, 1000);
  49. function rand(min, max) {
  50. return Math.round(Math.random() * (max - min)) + min;
  51. }
  52. setInterval(function () {
  53. ctx2.beginPath();
  54. //填充坐标
  55. ctx2.fillRect(rand(0, 300), rand(0, 300), rand(0, 300), rand(0, 300));
  56. //边框颜色
  57. ctx2.strokeStyle = "rgb(" + rand(0, 200) + "," + rand(0, 200) + "," + rand(0, 200) + ")";
  58. ctx2.lineWidth = 2;
  59. //边框坐标
  60. ctx2.strokeRect(rand(1, 300), rand(1, 300), rand(1, 300), rand(1, 300));
  61. //填充颜色
  62. ctx2.fillStyle = "rgb(" + rand(0, 255) + "," + rand(0, 255) + "," + rand(0, 255) + ")";
  63. }, 3000)
  64. </script>
  65. <script type="text/javascript">
  66. var mycanvas=document.querySelector("#mycanvas3");
  67. var cx=mycanvas.getContext("2d");
  68. function drawstar(cxt,x,y,R,r,fillStyle,rot){
  69. cxt.beginPath();
  70. for (var i=0;i<5;i++) {
  71. cxt.lineTo(Math.cos((18+72*i)/180*Math.PI+rot)*R+x,-Math.sin((18+72*i)/180*Math.PI+rot)*R+y);
  72. cxt.lineTo(Math.cos((54+72*i)/180*Math.PI+rot)*r+x,-Math.sin((54+72*i)/180*Math.PI+rot)*r+y);
  73. }
  74. cxt.fillStyle=fillStyle;
  75. cxt.closePath();
  76. cxt.fill()
  77. }
  78. drawstar(cx,120,130,80,35,"yellow",0)
  79. drawstar(cx,250,50,30,12,"yellow",12)
  80. drawstar(cx,300,110,30,12,"yellow",-12)
  81. drawstar(cx,290,200,30,12,"yellow",-12)
  82. drawstar(cx,220,250,30,12,"yellow",12)
  83. </script>
  84. <script type="text/javascript">
  85. var mycanvas = document.querySelector("#mycanvas4");
  86. var cx = mycanvas.getContext("2d");
  87. function drawCircle({fillStyle,x,y,r,start,end,color}) {
  88. cx.beginPath();
  89. cx.fillStyle = color||"#fff";
  90. cx.arc(x||250, y||250, r||200, start||0, end||Math.PI*2)
  91. cx.fill();
  92. }
  93. function drawLine({strokeColor,lineWidth,y,r,start,end,color,x1,y1,x2,y2}) {
  94. cx.beginPath();
  95. cx.strokeStyle=strokeColor||"red";
  96. cx.lineWidth=lineWidth||"8"
  97. cx.moveTo(x1||240,y1||140);
  98. cx.lineTo(x2||160,y2||220);
  99. cx.stroke();
  100. }
  101. drawCircle({color:'red',x:250,y:250,r:200})
  102. drawCircle({color:'#fff',x:250,y:250,r:194})
  103. drawCircle({color:'red',x:250,y:250,r:190})
  104. drawCircle({color:'#fff',x:250,y:250,r:145})
  105. drawCircle({color:'red',x:250,y:250,r:139})
  106. drawCircle({color:'#fff',x:250,y:250,r:133})
  107. drawLine({x1:240,y1:140,x2:160,y2:220})
  108. drawLine({x1:160,y1:216,x2:228,y2:300})
  109. drawLine({x1:228,y1:300,x2:280,y2:300})
  110. </script>

JS画板绘图

  1. <style type="text/css">
  2. body{background-color:#8fbc8f;}
  3. #bj{margin:0 400px;width:530px;height:700px;border-radius:20px;background-color:#000;}
  4. canvas{margin:15px;border-radius:20px;background-color:#fff;}
  5. #to{margin:0 100px;color:#FFF;}
  6. input{margin:0 8px;}
  7. </style>
  8. <div id="bj">
  9. <canvas id="mycanvas" width="500px" height="600px"></canvas>
  10. <div id="to">
  11. 画笔颜色:<input id="color" type="color" /> 画笔大小:
  12. <input type="number" id="number" min="1" max="20" value="1" />
  13. </div>
  14. </div>
  15. <script>
  16. var mycanvas = document.querySelector("#mycanvas");
  17. var cxt = mycanvas.getContext("2d");
  18. var color = "#000";
  19. var lineWith = 2;
  20. //接触鼠标时候,然后在移动鼠标时候
  21. mycanvas.onmousedown = function (e) {
  22. start_draw();
  23. cxt.moveTo(e.clientX - mycanvas.offsetLeft, e.clientY - mycanvas.offsetTop);
  24. mycanvas.addEventListener("mousemove", draw);
  25. }
  26. //松开鼠标
  27. mycanvas.onmouseup = function () {
  28. mycanvas.removeEventListener("mousemove", draw);
  29. }
  30. //选择画笔
  31. document.querySelector("#color").oninput = function () {
  32. color = this.value;
  33. }
  34. //选择画笔大小
  35. document.querySelector("#number").oninput = function () {
  36. lineWith = this.value;
  37. }
  38. //开始画
  39. function start_draw() {
  40. cxt.beginPath();
  41. cxt.strokeStyle = color;
  42. cxt.lineWidth = lineWith;
  43. }
  44. function draw(e) {
  45. e.preventDefault();
  46. cxt.lineTo(e.clientX - mycanvas.offsetLeft, e.clientY - mycanvas.offsetTop);
  47. cxt.stroke();
  48. }
  49. </script>

JS canvas绘制象棋

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title></title>
  6. <!--<script src="http://libs.baidu.com/jquery/1.9.1/jquery.min.js"></script>-->
  7. <!--<link rel="stylesheet" type="text/css" href="css/chess.css"/>-->
  8. <script src="js/jquery.js" type="text/javascript" charset="utf-8"></script>
  9. </head>
  10. <style type="text/css">
  11. * {
  12. margin: 0;
  13. padding: 0
  14. }
  15. #Wrap {
  16. width: 700px;
  17. height: 640px;
  18. margin: 0 auto
  19. }
  20. #currActive {
  21. font-size: 30px;
  22. font-weight: 700;
  23. margin-left: 260px;
  24. margin: 8px 250px
  25. }
  26. #canvas {
  27. float: left;
  28. background: #EAC591;
  29. display: block
  30. }
  31. #take_notes {
  32. width: 80px;
  33. height: 645px;
  34. padding: 10px;
  35. float: right;
  36. overflow: hidden;
  37. display: flex;
  38. flex-direction: column-reverse;
  39. }
  40. #ul {
  41. list-style: none;
  42. margin: 0;
  43. padding: 0
  44. }
  45. #ul li {
  46. padding: 2px;
  47. overflow: hidden
  48. }
  49. #shuying {
  50. float: left
  51. }
  52. #shuying p {
  53. margin: 10px;
  54. font-size: 30px
  55. }
  56. #main {
  57. width: 600px;
  58. height: 150px;
  59. -webkit-justify-content: space-around;
  60. display: flex; //分横栏
  61. justify-content: space-around
  62. }
  63. #main div {
  64. width: 100px;
  65. height: 50px;
  66. line-height: 50px;
  67. text-align: center;
  68. margin-top: 10px;
  69. border-radius: 20%;
  70. font-weight: 700
  71. }
  72. #main div a {
  73. text-decoration: none;
  74. color: #000
  75. }
  76. #main div a:hover {
  77. color: #00f
  78. }
  79. #main div ul {
  80. list-style: none;
  81. position: relative
  82. }
  83. #main div ul {
  84. position: absolute
  85. }
  86. .daohang {
  87. position: absolute;
  88. display: none;
  89. margin-left: 18px;
  90. margin-top: -190px;
  91. background: pink;
  92. border-radius: 10%
  93. }
  94. .daohang a {
  95. font: "微软雅黑";
  96. font-size: 15px;
  97. margin-top: -6px
  98. }
  99. #dianji:hover .daohang {
  100. display: block
  101. }
  102. #red_zi {
  103. color: red
  104. }
  105. </style>
  106. <body>
  107. <div id="Wrap">
  108. <div id="currActive"></div>
  109. <canvas id="canvas" width="600" height="660"></canvas>
  110. <div id="take_notes">
  111. <div id="shuying"></div>
  112. <ul id="ul"></ul>
  113. </div>
  114. <div id="main">
  115. <div style="background-color:coral;"><a href="canvas绘制象棋.html">重新开始</a></div>
  116. <div style="background-color:lightblue;"><a href="#">提示</a></div>
  117. <div style="background-color:khaki;"><a href="#">悔棋</a></div>
  118. <div style="background-color:pink;" id="dianji">
  119. <a href="#">设置</a>
  120. <ul class="daohang">
  121. <li class="daohang_1"><a href="#">棋盘大小</a></li>
  122. <li class="daohang_2"><a href="#">棋子大小</a></li>
  123. <li class="daohang_3"><a href="#">棋子颜色</a></li>
  124. </ul>
  125. </div>
  126. </div>
  127. </body>
  128. <script>
  129. var obj = {};
  130. // 棋盘初始化
  131. obj.init = function (lqfs) {
  132. var canvas = document.querySelector("canvas");
  133. this.ctx = canvas.getContext("2d");
  134. // 棋盘初始化
  135. obj.init_back = function () {
  136. this.drawRowLine();
  137. this.drawColLine();
  138. this.ctx.clearRect(this.chunk + 1, this.chunk * 5 + 1, this.chunk * 8 - 2, this.chunk - 2);
  139. this.drawsharpS();
  140. this.drawX();
  141. this.drawText();
  142. }
  143. // this.chunk =lqfs&&lqfs.chunk?lqfs.chunk:50;
  144. // this.radius =lqfs&&lqfs.radius?lqfs.radius:23;
  145. // this.CandidateCircleR =lqfs&&lqfs.ccr?lqfs.ccr:5;
  146. /*---------------------------可以修改棋盘、棋子大小,颜色,谁先手 ------------------------------------------------------------*/
  147. obj.chunk = 60; //棋盘线格大小
  148. this.radius = this.chunk / 2 - 3; // 棋子半径
  149. this.CandidateCircleR = this.chunk / 10; //候选棋子的半径大小
  150. this.drawCandidateCircle_fillStyle = "#fff"; //候选的内圆颜色
  151. this.drawCandidateCircle_strokeStyle = "red"; //候选的内圆边框原色
  152. this.drawCandidateCircle_lineWidth = 2; //候选内圆的边框大小
  153. this.init_back();
  154. this.init_chess();
  155. $(canvas).unbind();
  156. this.addEvent();
  157. this.Notations = []; // 记录步骤
  158. this.currActive = "red"; // 先下
  159. $("#currActive").text("红方先下").css({ "color": "red", })
  160. /*---------------------------------------------------------------------------------------------------------------------------------*/
  161. }
  162. /*---------------------------------------------------------------------------------------------------------------------------------*/
  163. // 棋子初始化
  164. obj.init_chess = function () {
  165. //上方(红)
  166. var Car_b1 = { x: 1, y: 1, text: "車" }
  167. var Horse_b1 = { x: 2, y: 1, text: "馬" }
  168. var Elephant_b1 = { x: 3, y: 1, text: "象" }
  169. var Scholar_b1 = { x: 4, y: 1, text: "士" }
  170. var Boss_b = { x: 5, y: 1, text: "将" }
  171. var Scholar_b2 = { x: 6, y: 1, text: "士" }
  172. var Elephant_b2 = { x: 7, y: 1, text: "象" }
  173. var Horse_b2 = { x: 8, y: 1, text: "馬" }
  174. var Car_b2 = { x: 9, y: 1, text: "車" }
  175. var Cannon_b1 = { x: 2, y: 3, text: "炮" }
  176. var Cannon_b2 = { x: 8, y: 3, text: "炮" }
  177. var Soldier_b1 = { x: 1, y: 4, text: "卒" }
  178. var Soldier_b2 = { x: 3, y: 4, text: "卒" }
  179. var Soldier_b3 = { x: 5, y: 4, text: "卒" }
  180. var Soldier_b4 = { x: 7, y: 4, text: "卒" }
  181. var Soldier_b5 = { x: 9, y: 4, text: "卒" }
  182. //下方(黑)
  183. var Car_r1 = { x: 1, y: 10, text: "車" }
  184. var Horse_r1 = { x: 2, y: 10, text: "馬" }
  185. var Elephant_r1 = { x: 3, y: 10, text: "相" }
  186. var Scholar_r1 = { x: 4, y: 10, text: "仕" }
  187. var Boss_r = { x: 5, y: 10, text: "帅" }
  188. var Scholar_r2 = { x: 6, y: 10, text: "仕" }
  189. var Elephant_r2 = { x: 7, y: 10, text: "相" }
  190. var Horse_r2 = { x: 8, y: 10, text: "馬" }
  191. var Car_r2 = { x: 9, y: 10, text: "車" }
  192. var Cannon_r1 = { x: 2, y: 8, text: "炮" }
  193. var Cannon_r2 = { x: 8, y: 8, text: "炮" }
  194. var Soldier_r1 = { x: 1, y: 7, text: "兵" }
  195. var Soldier_r2 = { x: 3, y: 7, text: "兵" }
  196. var Soldier_r3 = { x: 5, y: 7, text: "兵" }
  197. var Soldier_r4 = { x: 7, y: 7, text: "兵" }
  198. var Soldier_r5 = { x: 9, y: 7, text: "兵" }
  199. this.cheer_arr_B = [Car_b1, Horse_b1, Elephant_b1, Scholar_b1, Boss_b, Scholar_b2, Elephant_b2, Horse_b2, Car_b2,
  200. Cannon_b1, Cannon_b2, Soldier_b1, Soldier_b2, Soldier_b3, Soldier_b4, Soldier_b5];
  201. this.cheer_arr_R = [Car_r1, Horse_r1, Elephant_r1, Scholar_r1, Boss_r, Scholar_r2, Elephant_r2, Horse_r2, Car_r2,
  202. Cannon_r1, Cannon_r2, Soldier_r1, Soldier_r2, Soldier_r3, Soldier_r4, Soldier_r5];
  203. var that = this;
  204. $.each(this.cheer_arr_B, function (i, e) {
  205. e.color = "#000"; //圆的文字颜色
  206. e.bgcolor = "#fff"; //圆的背景颜色
  207. e.bgColor_b = "#000"; //外边的框颜色
  208. e.type = "black";
  209. that.drawPiece(e);
  210. that.drawChessText(e);
  211. });
  212. $.each(this.cheer_arr_R, function (i, e) {
  213. e.color = "#f00"; //圆的文字颜色
  214. e.bgcolor = "#fff"; //圆的背景颜色
  215. e.bgColor_b = "#f00"; //外边的框颜色
  216. e.type = "red";
  217. that.drawPiece(e);
  218. that.drawChessText(e);
  219. });
  220. this.cheer_arr_ALL = this.cheer_arr_B.concat(this.cheer_arr_R);
  221. }
  222. /*--------------------------------------------------------------------------------------------------------------------------------------*/
  223. // 画棋子形状
  224. obj.drawPiece = function (e) {
  225. this.ctx.beginPath();
  226. this.ctx.fillStyle = e.bgcolor;
  227. this.ctx.strokeStyle = e.bgColor_b;
  228. this.ctx.lineWidth = 2;
  229. this.ctx.arc(e.x * this.chunk, e.y * this.chunk, this.radius, 0, Math.PI * 2, true); //this.radius棋子大小
  230. this.ctx.closePath();
  231. this.ctx.fill();
  232. this.ctx.stroke();
  233. }
  234. /*--------------------------------------------------------------------------------------------------------------------------------------*/
  235. // 画棋子文字
  236. obj.drawChessText = function (e) {
  237. var font = "" + this.chunk / 1.5 + "px "
  238. this.ctx.font = "bold " + font + " Courier New";
  239. this.ctx.fillStyle = e.color;
  240. var offset = this.ctx.measureText(e.text).width / 2;
  241. this.ctx.fillText(e.text, e.x * this.chunk - offset, e.y * this.chunk + this.chunk / 4.5);
  242. }
  243. /*---------------------------------------------------------------------------------------------------------------------------------*/
  244. // 画直线
  245. obj.drawLine = function (x0, y0, x1, y1, lw) {
  246. this.ctx.beginPath();
  247. var x0 = x0 * this.chunk;
  248. var y0 = y0 * this.chunk;
  249. var x1 = x1 * this.chunk;
  250. var y1 = y1 * this.chunk;
  251. this.ctx.strokeStyle = "#000";
  252. this.ctx.lineWidth = lw ? lw : 1;
  253. this.ctx.moveTo(x0, y0);
  254. this.ctx.lineTo(x1, y1);
  255. this.ctx.closePath();
  256. this.ctx.stroke();
  257. }
  258. /*---------------------------------------------------------------------------------------------------------------------------------*/
  259. // 画横线
  260. obj.drawRowLine = function () {
  261. for (var i = 1; i <= 10; i++) {
  262. this.drawLine(1, i, 9, i);
  263. }
  264. }
  265. // 画竖线
  266. obj.drawColLine = function () {
  267. for (var i = 1; i <= 9; i++) {
  268. this.drawLine(i, 1, i, 10);
  269. }
  270. }
  271. // 画X
  272. obj.drawX = function () {
  273. this.drawLine(4, 1, 6, 3, 0.5);
  274. this.drawLine(4, 3, 6, 1, 0.5);
  275. this.drawLine(4, 8, 6, 10, 0.5);
  276. this.drawLine(4, 10, 6, 8, 0.5);
  277. }
  278. /*---------------------------------------------------------------------------------------------------------------------------------*/
  279. // 画单个#
  280. obj.drawsharp = function (x, y) {
  281. var x = x * this.chunk;
  282. var y = y * this.chunk;
  283. var range_y = this.chunk / 4.3; /*#的竖的距离*/
  284. var range_x = this.chunk / 4.3; /*#的横的距离*/
  285. var range_close = this.chunk / 12; /*间隔距离*/
  286. /*var range_y=10;
  287. var range_x=10;
  288. var range_close=5; */
  289. this.ctx.beginPath();
  290. this.ctx.strokeStyle = "#000";
  291. this.ctx.lineWidth = 1;
  292. if (x != 1) {
  293. // 左上
  294. this.ctx.moveTo(x - range_close, y - range_y);
  295. this.ctx.lineTo(x - range_close, y - range_close);
  296. this.ctx.lineTo(x - range_x, y - range_close);
  297. // 左下
  298. this.ctx.moveTo(x - range_close, y + range_y);
  299. this.ctx.lineTo(x - range_close, y + range_close);
  300. this.ctx.lineTo(x - range_x, y + range_close);
  301. }
  302. if (x != 9) {
  303. // 右上
  304. this.ctx.moveTo(x + range_close, y - range_y);
  305. this.ctx.lineTo(x + range_close, y - range_close);
  306. this.ctx.lineTo(x + range_x, y - range_close);
  307. // 右下
  308. this.ctx.moveTo(x + range_close, y + range_y);
  309. this.ctx.lineTo(x + range_close, y + range_close);
  310. this.ctx.lineTo(x + range_x, y + range_close);
  311. }
  312. this.ctx.stroke();
  313. this.ctx.closePath();
  314. }
  315. // 画#
  316. /*obj.drawsharpS = function(){
  317. this.drawsharp(2,3);
  318. this.drawsharp(8,3);
  319. this.drawsharp(1,4);
  320. this.drawsharp(3,4);
  321. this.drawsharp(5,4);
  322. this.drawsharp(7,4);
  323. this.drawsharp(9,4);
  324. this.drawsharp(2,8);
  325. this.drawsharp(8,8);
  326. this.drawsharp(1,7);
  327. this.drawsharp(3,7);
  328. this.drawsharp(5,7);
  329. this.drawsharp(7,7);
  330. this.drawsharp(9,7);
  331. }*/
  332. //用循环函数的并和画#
  333. obj.drawsharpS = function () {
  334. this.drawsharp(2, 3);
  335. this.drawsharp(8, 3);
  336. for (var i = 1; i <= 5; i++) {
  337. this.drawsharp(2 * i - 1, 4)
  338. }
  339. for (var i = 1; i <= 5; i++) {
  340. this.drawsharp(2 * i - 1, 7)
  341. }
  342. this.drawsharp(2, 8);
  343. this.drawsharp(8, 8);
  344. }
  345. /*--------------------------------------------------------------------------------------------------------------------------------------*/
  346. // 画楚河/漢界
  347. obj.drawText = function () {
  348. var font = "" + this.chunk / 1.5 + "px ";
  349. this.ctx.font = "bold " + font + " KaiTi_GB2312";
  350. this.ctx.fillStyle = "#000";
  351. this.ctx.fillText("楚 河", this.chunk * 2, this.chunk * 5 + this.chunk / 1.3);
  352. this.ctx.fillText("漢 界", this.chunk * 6 + this.chunk / 3, this.chunk * 5 + this.chunk / 1.3);
  353. var font1 = "" + this.chunk / 6 + "px"
  354. this.ctx.font = "" + font1 + " Courier New";
  355. this.text_arr = ["九", "八", "七", "六", "五", "四", "三", "二", "一"];
  356. for (var i = 0; i < 9; i++) {
  357. this.ctx.fillText((i + 1).toString(), this.chunk * (i + 1) - 5, 20);
  358. this.ctx.fillText(this.text_arr[i], this.chunk * (i + 1) - 5, this.chunk * 11 - this.chunk / 6);
  359. }
  360. }
  361. /*---------------------------------------------------------------------------------------------------------------------------------*/
  362. // 画候选形状
  363. obj.drawCandidateCircle = function (x, y) {
  364. this.ctx.beginPath();
  365. this.ctx.fillStyle = this.drawCandidateCircle_fillStyle; //候选内圆的颜色
  366. this.ctx.strokeStyle = this.drawCandidateCircle_strokeStyle; //候选内圆的边框颜色
  367. this.ctx.lineWidth = this.drawCandidateCircle_lineWidth; //候选内圆的边框大小
  368. this.ctx.arc(x * this.chunk, y * this.chunk, this.CandidateCircleR, 0, Math.PI * 2, true);
  369. this.ctx.closePath();
  370. this.ctx.fill();
  371. this.ctx.stroke();
  372. }
  373. // 画选中棋子状态
  374. obj.drawChecked = function (p) {
  375. //画候选的形状
  376. var temp_x = p.x * this.chunk;
  377. var temp_y = p.y * this.chunk;
  378. this.ctx.beginPath();
  379. this.ctx.strokeStyle = "#00f";
  380. this.ctx.lineWidth = 1;
  381. this.ctx.moveTo(temp_x - this.radius, temp_y - this.radius + 10); //this.radius棋子大小
  382. this.ctx.lineTo(temp_x - this.radius, temp_y - this.radius);
  383. this.ctx.lineTo(temp_x - this.radius + 10, temp_y - this.radius);
  384. this.ctx.moveTo(temp_x - this.radius, temp_y + this.radius - 10);
  385. this.ctx.lineTo(temp_x - this.radius, temp_y + this.radius);
  386. this.ctx.lineTo(temp_x - this.radius + 10, temp_y + this.radius);
  387. this.ctx.moveTo(temp_x + this.radius, temp_y - this.radius + 10);
  388. this.ctx.lineTo(temp_x + this.radius, temp_y - this.radius);
  389. this.ctx.lineTo(temp_x + this.radius - 10, temp_y - this.radius);
  390. this.ctx.moveTo(temp_x + this.radius, temp_y + this.radius - 10);
  391. this.ctx.lineTo(temp_x + this.radius, temp_y + this.radius);
  392. this.ctx.lineTo(temp_x + this.radius - 10, temp_y + this.radius);
  393. this.ctx.stroke();
  394. this.ctx.closePath();
  395. }
  396. // 画候选位置
  397. obj.drawCandidate = function () {
  398. switch (this.action.text) {
  399. case "車":
  400. var temp_y = this.action.y;
  401. while (!this.inArray(this.action.x, ++temp_y) && temp_y <= 10) {
  402. this.drawCandidateCircle(this.action.x, temp_y);
  403. }
  404. var temp_y = this.action.y;
  405. while (!this.inArray(this.action.x, --temp_y) && temp_y > 0) {
  406. this.drawCandidateCircle(this.action.x, temp_y);
  407. }
  408. var temp_x = this.action.x;
  409. while (!this.inArray(++temp_x, this.action.y) && temp_x < 10) {
  410. this.drawCandidateCircle(temp_x, this.action.y);
  411. }
  412. var temp_x = this.action.x;
  413. while (!this.inArray(--temp_x, this.action.y) && temp_x > 0) {
  414. this.drawCandidateCircle(temp_x, this.action.y);
  415. }
  416. break;
  417. case "馬":
  418. if (!this.inArray(this.action.x - 2, this.action.y - 1)
  419. && this.action.x - 2 >= 1 && this.action.y - 1 >= 1
  420. && !this.inArray(this.action.x - 1, this.action.y)) {
  421. this.drawCandidateCircle(this.action.x - 2, this.action.y - 1);
  422. }
  423. if (!this.inArray(this.action.x - 1, this.action.y - 2)
  424. && this.action.x - 1 >= 1 && this.action.y - 2 >= 1
  425. && !this.inArray(this.action.x, this.action.y - 1)) {
  426. this.drawCandidateCircle(this.action.x - 1, this.action.y - 2);
  427. }
  428. if (!this.inArray(this.action.x + 1, this.action.y - 2)
  429. && this.action.x + 1 <= 9 && this.action.y - 2 >= 1
  430. && !this.inArray(this.action.x, this.action.y - 1)) {
  431. this.drawCandidateCircle(this.action.x + 1, this.action.y - 2);
  432. }
  433. if (!this.inArray(this.action.x + 2, this.action.y - 1)
  434. && this.action.x + 2 <= 9 && this.action.y - 1 >= 1
  435. && !this.inArray(this.action.x + 1, this.action.y)) {
  436. this.drawCandidateCircle(this.action.x + 2, this.action.y - 1);
  437. }
  438. if (!this.inArray(this.action.x + 2, this.action.y + 1)
  439. && this.action.x + 2 <= 9 && this.action.y + 1 <= 10
  440. && !this.inArray(this.action.x + 1, this.action.y)) {
  441. this.drawCandidateCircle(this.action.x + 2, this.action.y + 1);
  442. }
  443. if (!this.inArray(this.action.x + 1, this.action.y + 2)
  444. && this.action.x + 1 <= 9 && this.action.y + 2 <= 10
  445. && !this.inArray(this.action.x, this.action.y + 1)) {
  446. this.drawCandidateCircle(this.action.x + 1, this.action.y + 2);
  447. }
  448. if (!this.inArray(this.action.x - 1, this.action.y + 2)
  449. && this.action.x - 1 >= 1 && this.action.y + 2 <= 10
  450. && !this.inArray(this.action.x, this.action.y + 1)) {
  451. this.drawCandidateCircle(this.action.x - 1, this.action.y + 2);
  452. }
  453. if (!this.inArray(this.action.x - 2, this.action.y + 1)
  454. && this.action.x - 2 >= 1 && this.action.y + 1 <= 10
  455. && !this.inArray(this.action.x - 1, this.action.y)) {
  456. this.drawCandidateCircle(this.action.x - 2, this.action.y + 1);
  457. }
  458. break;
  459. case "相":
  460. if (this.action.y == 10) {
  461. if (!this.inArray(this.action.x - 2, this.action.y - 2)
  462. && !this.inArray(this.action.x - 1, this.action.y - 1)) {
  463. this.drawCandidateCircle(this.action.x - 2, this.action.y - 2);
  464. }
  465. if (!this.inArray(this.action.x + 2, this.action.y - 2)
  466. && !this.inArray(this.action.x + 1, this.action.y - 1)) {
  467. this.drawCandidateCircle(this.action.x + 2, this.action.y - 2);
  468. }
  469. } else if (this.action.y == 6) {
  470. if (!this.inArray(this.action.x - 2, this.action.y + 2)
  471. && !this.inArray(this.action.x - 1, this.action.y + 1)) {
  472. this.drawCandidateCircle(this.action.x - 2, this.action.y + 2);
  473. }
  474. if (!this.inArray(this.action.x + 2, this.action.y + 2)
  475. && !this.inArray(this.action.x + 1, this.action.y + 1)) {
  476. this.drawCandidateCircle(this.action.x + 2, this.action.y + 2);
  477. }
  478. } else if (this.action.x == 1) {
  479. if (!this.inArray(this.action.x + 2, this.action.y - 2)
  480. && !this.inArray(this.action.x + 1, this.action.y - 1)) {
  481. this.drawCandidateCircle(this.action.x + 2, this.action.y - 2);
  482. }
  483. if (!this.inArray(this.action.x + 2, this.action.y + 2)
  484. && !this.inArray(this.action.x + 1, this.action.y + 1)) {
  485. this.drawCandidateCircle(this.action.x + 2, this.action.y + 2);
  486. }
  487. } else if (this.action.x == 9) {
  488. if (!this.inArray(this.action.x - 2, this.action.y - 2)
  489. && !this.inArray(this.action.x - 1, this.action.y - 1)) {
  490. this.drawCandidateCircle(this.action.x - 2, this.action.y - 2);
  491. }
  492. if (!this.inArray(this.action.x - 2, this.action.y + 2)
  493. && !this.inArray(this.action.x - 1, this.action.y + 1)) {
  494. this.drawCandidateCircle(this.action.x - 2, this.action.y + 2);
  495. }
  496. } else {
  497. if (!this.inArray(this.action.x + 2, this.action.y - 2)
  498. && !this.inArray(this.action.x + 1, this.action.y - 1)) {
  499. this.drawCandidateCircle(this.action.x + 2, this.action.y - 2);
  500. }
  501. if (!this.inArray(this.action.x + 2, this.action.y + 2)
  502. && !this.inArray(this.action.x + 1, this.action.y + 1)) {
  503. this.drawCandidateCircle(this.action.x + 2, this.action.y + 2);
  504. }
  505. if (!this.inArray(this.action.x - 2, this.action.y - 2)
  506. && !this.inArray(this.action.x - 1, this.action.y - 1)) {
  507. this.drawCandidateCircle(this.action.x - 2, this.action.y - 2);
  508. }
  509. if (!this.inArray(this.action.x - 2, this.action.y + 2)
  510. && !this.inArray(this.action.x - 1, this.action.y + 1)) {
  511. this.drawCandidateCircle(this.action.x - 2, this.action.y + 2);
  512. }
  513. }
  514. break;
  515. case "象":
  516. if (this.action.y == 1) {
  517. if (!this.inArray(this.action.x - 2, this.action.y + 2)
  518. && !this.inArray(this.action.x - 1, this.action.y + 1)) {
  519. this.drawCandidateCircle(this.action.x - 2, this.action.y + 2);
  520. }
  521. if (!this.inArray(this.action.x + 2, this.action.y + 2)
  522. && !this.inArray(this.action.x + 1, this.action.y + 1)) {
  523. this.drawCandidateCircle(this.action.x + 2, this.action.y + 2);
  524. }
  525. } else if (this.action.y == 5) {
  526. if (!this.inArray(this.action.x - 2, this.action.y - 2)
  527. && !this.inArray(this.action.x - 1, this.action.y - 1)) {
  528. this.drawCandidateCircle(this.action.x - 2, this.action.y - 2);
  529. }
  530. if (!this.inArray(this.action.x + 2, this.action.y - 2)
  531. && !this.inArray(this.action.x + 1, this.action.y - 1)) {
  532. this.drawCandidateCircle(this.action.x + 2, this.action.y - 2);
  533. }
  534. } else if (this.action.x == 1) {
  535. if (!this.inArray(this.action.x + 2, this.action.y - 2)
  536. && !this.inArray(this.action.x + 1, this.action.y - 1)) {
  537. this.drawCandidateCircle(this.action.x + 2, this.action.y - 2);
  538. }
  539. if (!this.inArray(this.action.x + 2, this.action.y + 2)
  540. && !this.inArray(this.action.x + 1, this.action.y + 1)) {
  541. this.drawCandidateCircle(this.action.x + 2, this.action.y + 2);
  542. }
  543. } else if (this.action.x == 9) {
  544. if (!this.inArray(this.action.x - 2, this.action.y - 2)
  545. && !this.inArray(this.action.x - 1, this.action.y - 1)) {
  546. this.drawCandidateCircle(this.action.x - 2, this.action.y - 2);
  547. }
  548. if (!this.inArray(this.action.x - 2, this.action.y + 2)
  549. && !this.inArray(this.action.x - 1, this.action.y + 1)) {
  550. this.drawCandidateCircle(this.action.x - 2, this.action.y + 2);
  551. }
  552. } else {
  553. if (!this.inArray(this.action.x + 2, this.action.y - 2)
  554. && !this.inArray(this.action.x + 1, this.action.y - 1)) {
  555. this.drawCandidateCircle(this.action.x + 2, this.action.y - 2);
  556. }
  557. if (!this.inArray(this.action.x + 2, this.action.y + 2)
  558. && !this.inArray(this.action.x + 1, this.action.y + 1)) {
  559. this.drawCandidateCircle(this.action.x + 2, this.action.y + 2);
  560. }
  561. if (!this.inArray(this.action.x - 2, this.action.y - 2)
  562. && !this.inArray(this.action.x - 1, this.action.y - 1)) {
  563. this.drawCandidateCircle(this.action.x - 2, this.action.y - 2);
  564. }
  565. if (!this.inArray(this.action.x - 2, this.action.y + 2)
  566. && !this.inArray(this.action.x - 1, this.action.y + 1)) {
  567. this.drawCandidateCircle(this.action.x - 2, this.action.y + 2);
  568. }
  569. }
  570. break;
  571. case "仕":
  572. if (this.action.x == 5 && this.action.y == 9) {
  573. if (!this.inArray(this.action.x - 1, this.action.y - 1)) {
  574. this.drawCandidateCircle(this.action.x - 1, this.action.y - 1);
  575. }
  576. if (!this.inArray(this.action.x - 1, this.action.y + 1)) {
  577. this.drawCandidateCircle(this.action.x - 1, this.action.y + 1);
  578. }
  579. if (!this.inArray(this.action.x + 1, this.action.y - 1)) {
  580. this.drawCandidateCircle(this.action.x + 1, this.action.y - 1);
  581. }
  582. if (!this.inArray(this.action.x + 1, this.action.y + 1)) {
  583. this.drawCandidateCircle(this.action.x + 1, this.action.y + 1);
  584. }
  585. } else {
  586. this.drawCandidateCircle(5, 9);
  587. }
  588. break;
  589. case "士":
  590. if (this.action.x == 5 && this.action.y == 2) {
  591. if (!this.inArray(this.action.x - 1, this.action.y - 1)) {
  592. this.drawCandidateCircle(this.action.x - 1, this.action.y - 1);
  593. }
  594. if (!this.inArray(this.action.x - 1, this.action.y + 1)) {
  595. this.drawCandidateCircle(this.action.x - 1, this.action.y + 1);
  596. }
  597. if (!this.inArray(this.action.x + 1, this.action.y - 1)) {
  598. this.drawCandidateCircle(this.action.x + 1, this.action.y - 1);
  599. }
  600. if (!this.inArray(this.action.x + 1, this.action.y + 1)) {
  601. this.drawCandidateCircle(this.action.x + 1, this.action.y + 1);
  602. }
  603. } else {
  604. this.drawCandidateCircle(5, 2);
  605. }
  606. break;
  607. case "帅":
  608. if (!this.inArray(this.action.x, this.action.y - 1) && this.action.y > 8) {
  609. this.drawCandidateCircle(this.action.x, this.action.y - 1);
  610. }
  611. if (!this.inArray(this.action.x, this.action.y + 1) && this.action.y < 10) {
  612. this.drawCandidateCircle(this.action.x, this.action.y + 1);
  613. }
  614. if (!this.inArray(this.action.x - 1, this.action.y) && this.action.x > 4) {
  615. this.drawCandidateCircle(this.action.x - 1, this.action.y);
  616. }
  617. if (!this.inArray(this.action.x + 1, this.action.y) && this.action.x < 6) {
  618. this.drawCandidateCircle(this.action.x + 1, this.action.y);
  619. }
  620. break;
  621. case "将":
  622. if (!this.inArray(this.action.x, this.action.y - 1) && this.action.y > 1) {
  623. this.drawCandidateCircle(this.action.x, this.action.y - 1);
  624. }
  625. if (!this.inArray(this.action.x, this.action.y + 1) && this.action.y < 3) {
  626. this.drawCandidateCircle(this.action.x, this.action.y + 1);
  627. }
  628. if (!this.inArray(this.action.x - 1, this.action.y) && this.action.x > 4) {
  629. this.drawCandidateCircle(this.action.x - 1, this.action.y);
  630. }
  631. if (!this.inArray(this.action.x + 1, this.action.y) && this.action.x < 6) {
  632. this.drawCandidateCircle(this.action.x + 1, this.action.y);
  633. }
  634. break;
  635. case "兵":
  636. if (this.action.y > 5 && !this.inArray(this.action.x, this.action.y - 1)) {
  637. this.drawCandidateCircle(this.action.x, this.action.y - 1);
  638. } else if (this.action.y <= 5) {
  639. if (!this.inArray(this.action.x, this.action.y - 1) && this.action.y > 1) {
  640. this.drawCandidateCircle(this.action.x, this.action.y - 1);
  641. }
  642. if (!this.inArray(this.action.x - 1, this.action.y) && this.action.x > 1) {
  643. this.drawCandidateCircle(this.action.x - 1, this.action.y);
  644. }
  645. if (!this.inArray(this.action.x + 1, this.action.y) && this.action.x < 9) {
  646. this.drawCandidateCircle(this.action.x + 1, this.action.y);
  647. }
  648. }
  649. break;
  650. case "卒":
  651. if (this.action.y <= 5 && !this.inArray(this.action.x, this.action.y + 1)) {
  652. this.drawCandidateCircle(this.action.x, this.action.y + 1);
  653. } else if (this.action.y > 5) {
  654. if (!this.inArray(this.action.x, this.action.y + 1) && this.action.y < 10) {
  655. this.drawCandidateCircle(this.action.x, this.action.y + 1);
  656. }
  657. if (!this.inArray(this.action.x - 1, this.action.y) && this.action.x > 1) {
  658. this.drawCandidateCircle(this.action.x - 1, this.action.y);
  659. }
  660. if (!this.inArray(this.action.x + 1, this.action.y) && this.action.x < 9) {
  661. this.drawCandidateCircle(this.action.x + 1, this.action.y);
  662. }
  663. }
  664. break;
  665. case "炮":
  666. var temp_y = this.action.y;
  667. while (!this.inArray(this.action.x, ++temp_y) && temp_y <= 10) {
  668. this.drawCandidateCircle(this.action.x, temp_y);
  669. }
  670. var temp_y = this.action.y;
  671. while (!this.inArray(this.action.x, --temp_y) && temp_y > 0) {
  672. this.drawCandidateCircle(this.action.x, temp_y);
  673. }
  674. var temp_x = this.action.x;
  675. while (!this.inArray(++temp_x, this.action.y) && temp_x < 10) {
  676. this.drawCandidateCircle(temp_x, this.action.y);
  677. }
  678. var temp_x = this.action.x;
  679. while (!this.inArray(--temp_x, this.action.y) && temp_x > 0) {
  680. this.drawCandidateCircle(temp_x, this.action.y);
  681. }
  682. break;
  683. }
  684. }
  685. /*---------------------------------------------------------------------------------------------------------------------------------*/
  686. // 更新棋局
  687. obj.updateChess = function (e) {
  688. var lqf =
  689. this.ctx.clearRect(0, 0, canvas.width, canvas.height);
  690. this.init_back();
  691. var that = this;
  692. $.each(this.cheer_arr_ALL, function (i, e) {
  693. that.drawPiece(e);
  694. that.drawChessText(e);
  695. });
  696. $("#ul").empty(); //清空
  697. $.each(this.Notations, function (iii, lqfs) {
  698. var cars = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25"]
  699. for (var i = 1; i < 2; i++) {
  700. var html_2 = '<li data-id="' + cars[i] + '" class="lqf' + i * 6 + '" style="color: red" >' + lqfs + '</li>';
  701. var html_1 = '<li data-id="' + cars[3] + '" class="lqf' + i * 7 + '" style="color: black">' + lqfs + '</li>';
  702. var tas = true;
  703. if (tas) { $("#ul").append(html_1); tas = false; }
  704. else { $("#ul").append(html_2); tas = true; }
  705. }
  706. });
  707. }
  708. /*---------------------------------------------------------------------------------------------------------------------------------*/
  709. // 增加点击事件
  710. obj.addEvent = function () {
  711. var that = this;//保存当前对象
  712. this.checked = false;//保存选中当前棋子的索引
  713. $(canvas).on("mousedown", function (ev) { //鼠标按下
  714. for (var j = 1; j <= 10; j++) {
  715. for (var i = 1; i <= 9; i++) {
  716. //计算当前棋子的位置坐标
  717. var temp_i = i * obj.chunk;
  718. var temp_j = j * obj.chunk;
  719. //计算当前位置和鼠标位置之间的距离
  720. var distanct = Math.sqrt(Math.pow(temp_i - ev.offsetX, 2) + Math.pow(temp_j - ev.offsetY, 2));
  721. if (distanct <= that.radius) {
  722. var overChess = false;
  723. $.each(that.cheer_arr_ALL, function (ii, ee) {
  724. if (ee.x == i && ee.y == j) {
  725. overChess = true;
  726. var p = { x: ee.x, y: ee.y };
  727. // console.log(that.checked);
  728. if (that.currActive != ee.type && !that.checked) {
  729. return false;
  730. }
  731. if (!that.checked) {
  732. console.log("选中一个棋子");
  733. that.drawChecked(p);
  734. that.action = ee;
  735. that.drawCandidate();
  736. that.checked = true;
  737. } else if (that.action.x == ee.x && that.action.y == ee.y) {
  738. console.log("点在原棋子上");
  739. that.updateChess();
  740. that.checked = false;
  741. } else if (that.action.type == ee.type) {
  742. console.log("切换棋子");
  743. that.updateChess();
  744. that.drawChecked(p);
  745. that.action = ee;
  746. that.drawCandidate();
  747. } else {
  748. // 是否能吃子
  749. if (that.Eating_ruless(i, j)) {
  750. that.eat(ii, ee, i, j);
  751. } else if (that.action.text == "帅") { // 对将
  752. if (that.action.x == i) {
  753. var canEat = true;
  754. $.each(that.cheer_arr_ALL, function (iii, lqfs) {
  755. if (lqfs.x == that.action.x && lqfs.y == j) {
  756. if (lqfs.text == "将") {
  757. for (var t = that.action.y - 1; t > j; t--) {
  758. if (that.inArray(that.action.x, t)) {
  759. canEat = false;
  760. break;
  761. }
  762. }
  763. } else {
  764. canEat = false;
  765. }
  766. return false;
  767. }
  768. });
  769. if (canEat) {
  770. that.eat(ii, ee, i, j);
  771. }
  772. }
  773. } else if (that.action.text == "将") {
  774. if (that.action.x == i) {
  775. var canEat = true;
  776. $.each(that.cheer_arr_ALL, function (iii, lqfs) {
  777. if (lqfs.x == that.action.x && lqfs.y == j) {
  778. if (lqfs.text == "帅") {
  779. for (var t = that.action.y + 1; t < j; t++) {
  780. if (that.inArray(that.action.x, t)) {
  781. canEat = false;
  782. break;
  783. }
  784. }
  785. } else {
  786. canEat = false;
  787. }
  788. return false;
  789. }
  790. });
  791. if (canEat) {
  792. that.eat(ii, ee, i, j);
  793. }
  794. }
  795. }
  796. }
  797. return false;
  798. }
  799. });
  800. if (overChess) {
  801. console.log("点在棋子上");
  802. } else {
  803. // 是否能移动
  804. if (that.checked && that.Move_rules(i, j)) {
  805. console.log("移动棋子");
  806. that.move(i, j);
  807. }
  808. }
  809. }
  810. }
  811. }
  812. });
  813. }
  814. // 记谱
  815. obj.book = function (ee, i, j) {
  816. var distance = Math.abs(ee.y - j);
  817. var Notation;
  818. if (ee.type == "red") {
  819. $("#currActive").text("轮到黑方").css({ "color": "black", })
  820. var oldP = this.text_arr[ee.x - 1];
  821. var newP = this.text_arr[i - 1];
  822. var num = this.text_arr[9 - distance];
  823. if (j < ee.y) {
  824. if (ee.x == i) {
  825. var tag_1 = '<span style="color: red">进<span>';
  826. var tag_2 = '<span style="color: red">' + ee.text + '<span>';
  827. console.log(ee.text + oldP + "进" + num);
  828. Notation = tag_2 + oldP + tag_1 + num;
  829. } else {
  830. var tag_1 = '<span style="color: red">进<span>';
  831. var tag_2 = '<span style="color: red">' + ee.text + '<span>';
  832. console.log(ee.text + oldP + "进" + newP);
  833. Notation = tag_2 + oldP + tag_1 + newP;
  834. }
  835. } else if (j > ee.y) {
  836. if (ee.x == i) {
  837. var tag_1 = '<span style="color: red">退<span>';
  838. var tag_2 = '<span style="color: red">' + ee.text + '<span>';
  839. console.log(ee.text + oldP + "退" + num);
  840. Notation = tag_2 + oldP + tag_1 + num;
  841. } else {
  842. var tag_1 = '<span style="color: red">退<span>';
  843. var tag_2 = '<span style="color: red">' + ee.text + '<span>';
  844. console.log(ee.text + oldP + "退" + newP);
  845. Notation = tag_2 + oldP + tag_1 + newP;
  846. }
  847. } else {
  848. var tag_1 = '<span style="color: red">平<span>';
  849. var tag_2 = '<span style="color: red">' + ee.text + '<span>';
  850. console.log(ee.text + oldP + "平" + newP);
  851. Notation = tag_2 + oldP + tag_1 + newP;
  852. }
  853. } else {
  854. $("#currActive").text("轮到红方").css({ "color": "red", })
  855. if (j > ee.y) {
  856. if (ee.x == i) {
  857. console.log(ee.text + ee.x + "进" + distance);
  858. Notation = ee.text + ee.x + "进" + distance
  859. } else {
  860. console.log(ee.text + ee.x + "进" + i);
  861. Notation = ee.text + ee.x + "进" + i;
  862. }
  863. } else if (j < ee.y) {
  864. if (ee.x == i) {
  865. console.log(ee.text + ee.x + "退" + distance);
  866. Notation = ee.text + ee.x + "退" + distance;
  867. } else {
  868. console.log(ee.text + ee.x + "退" + i);
  869. Notation = ee.text + ee.x + "退" + i;
  870. }
  871. } else {
  872. console.log(ee.text + ee.x + "平" + i);
  873. Notation = ee.text + ee.x + "平" + i;
  874. }
  875. }
  876. this.Notations.push(Notation);
  877. }
  878. // 是否结束
  879. obj.isOver = function (ee) {
  880. if (ee.text == "将") {
  881. $("#shuying").html('<p>' + "结束红方赢" + '</p>').css({ "background": "yellow", "color": "red" });
  882. $("#ul").empty();
  883. return true;
  884. } else if (ee.text == "帅") {
  885. $("#shuying").html('<p>' + "结束黑方赢" + '</p>').css({ "background": "yellow", "color": "black" });
  886. $("#ul").empty();
  887. return true;
  888. } else {
  889. return false;
  890. }
  891. }
  892. /*--------------------------------------------------------------------------------------------------------------------------------------*/
  893. // 移动
  894. obj.move = function (i, j) {
  895. var that = this;
  896. $.each(that.cheer_arr_ALL, function (iii, lqfs) {
  897. if (lqfs.x == that.action.x && lqfs.y == that.action.y) {
  898. that.book(lqfs, i, j);
  899. lqfs.x = i;
  900. lqfs.y = j;
  901. that.currActive = lqfs.type == "red" ? "black" : "red";
  902. return false;
  903. }
  904. });
  905. that.updateChess();
  906. that.checked = false;
  907. }
  908. // 吃子
  909. obj.eat = function (ii, ee, i, j) {
  910. this.cheer_arr_ALL.splice(ii, 1);
  911. this.move(i, j);
  912. if (this.isOver(ee)) {
  913. this.ctx.clearRect(0, 0, canvas.width, canvas.height);
  914. this.init();
  915. return false;
  916. };
  917. }
  918. /*---------------------------------------------------------------------------------------------------------------------------------*/
  919. // 棋子移动规则
  920. obj.Move_rules = function (i, j) {
  921. switch (this.action.text) {
  922. case "車":
  923. return this.rules_Car(i, j);
  924. case "馬":
  925. return this.rules_Horse(i, j);
  926. case "相":
  927. return this.rules_Elephant_r(i, j);
  928. case "象":
  929. return this.rules_Elephant_b(i, j);
  930. case "仕":
  931. return this.rules_Scholar_r(i, j);
  932. case "士":
  933. return this.rules_Scholar_b(i, j);
  934. case "帅":
  935. return this.rules_Boss_r(i, j);
  936. case "将":
  937. return this.rules_Boss_b(i, j);
  938. case "兵":
  939. return this.rules_Soldier_r(i, j);
  940. case "卒":
  941. return this.rules_Soldier_b(i, j);
  942. case "炮":
  943. if (this.rules_Cannon(i, j) == 0) {
  944. return true;
  945. }
  946. return false;
  947. }
  948. }
  949. /*---------------------------------------------------------------------------------------------------------------------------------*/
  950. // 棋子吃子规则
  951. obj.Eating_ruless = function (i, j) {
  952. switch (this.action.text) {
  953. case "車":
  954. return this.rules_Car(i, j);
  955. case "馬":
  956. return this.rules_Horse(i, j);
  957. case "相":
  958. return this.rules_Elephant_r(i, j);
  959. case "象":
  960. return this.rules_Elephant_b(i, j);
  961. case "仕":
  962. return this.rules_Scholar_r(i, j);
  963. case "士":
  964. return this.rules_Scholar_b(i, j);
  965. case "帅":
  966. return this.rules_Boss_r(i, j);
  967. case "将":
  968. return this.rules_Boss_b(i, j);
  969. case "兵":
  970. return this.rules_Soldier_r(i, j);
  971. case "卒":
  972. return this.rules_Soldier_b(i, j);
  973. case "炮":
  974. if (this.rules_Cannon(i, j) == 1) {
  975. return true;
  976. }
  977. return false;
  978. }
  979. }
  980. /*---------------------------------------------------------------------------------------------------------------------------------*/
  981. //各个棋子的规则
  982. // 車的规则
  983. obj.rules_Car = function (i, j) {
  984. if (this.action.x == i || this.action.y == j) {
  985. if (this.action.x == i) {
  986. if (this.action.y < j) {
  987. console.log("车下");
  988. var hasObstacle = false;
  989. for (var p = this.action.y + 1; p < j; p++) {
  990. if (this.inArray(i, p)) {
  991. hasObstacle = true;
  992. break;
  993. }
  994. }
  995. if (hasObstacle) {
  996. return false;
  997. }
  998. }
  999. if (this.action.y > j) {
  1000. console.log("车上");
  1001. var hasObstacle = false;
  1002. for (var p = this.action.y - 1; p > j; p--) {
  1003. if (this.inArray(i, p)) {
  1004. hasObstacle = true;
  1005. break;
  1006. }
  1007. }
  1008. if (hasObstacle) {
  1009. return false;
  1010. }
  1011. }
  1012. }
  1013. if (this.action.y == j) {
  1014. if (this.action.x < i) {
  1015. console.log("车右");
  1016. var hasObstacle = false;
  1017. for (var p = this.action.x + 1; p < i; p++) {
  1018. if (this.inArray(p, j)) {
  1019. hasObstacle = true;
  1020. break;
  1021. }
  1022. }
  1023. if (hasObstacle) {
  1024. return false;
  1025. }
  1026. }
  1027. if (this.action.x > i) {
  1028. console.log("车左");
  1029. var hasObstacle = false;
  1030. for (var p = this.action.x - 1; p > i; p--) {
  1031. if (this.inArray(p, j)) {
  1032. hasObstacle = true;
  1033. break;
  1034. }
  1035. }
  1036. if (hasObstacle) {
  1037. return false;
  1038. }
  1039. }
  1040. }
  1041. return true;
  1042. }
  1043. return false;
  1044. }
  1045. // 馬的规则
  1046. obj.rules_Horse = function (i, j) {
  1047. var hasObstacle = false;
  1048. var that = this;
  1049. if ((Math.abs(this.action.x - i) == 1 && Math.abs(this.action.y - j) == 2)
  1050. || (Math.abs(this.action.x - i) == 2 && Math.abs(this.action.y - j) == 1)) {
  1051. if (this.action.x - i == 2) { // 左
  1052. $.each(that.cheer_arr_ALL, function (ii, ee) {
  1053. if (ee.x == that.action.x - 1 && ee.y == that.action.y) {
  1054. hasObstacle = true;
  1055. return false;
  1056. }
  1057. });
  1058. if (hasObstacle) {
  1059. return false;
  1060. }
  1061. } else if (i - that.action.x == 2) { // 右
  1062. $.each(that.cheer_arr_ALL, function (ii, ee) {
  1063. if (ee.x == that.action.x + 1 && ee.y == that.action.y) {
  1064. hasObstacle = true;
  1065. return false;
  1066. }
  1067. });
  1068. if (hasObstacle) {
  1069. return false;
  1070. }
  1071. } else if (that.action.y - j == 2) { // 上
  1072. $.each(that.cheer_arr_ALL, function (ii, ee) {
  1073. if (ee.x == that.action.x && ee.y == that.action.y - 1) {
  1074. hasObstacle = true;
  1075. return false;
  1076. }
  1077. });
  1078. if (hasObstacle) {
  1079. return false;
  1080. }
  1081. } else if (j - that.action.y == 2) { // 下
  1082. $.each(that.cheer_arr_ALL, function (ii, ee) {
  1083. if (ee.x == that.action.x && ee.y == that.action.y + 1) {
  1084. hasObstacle = true;
  1085. return false;
  1086. }
  1087. });
  1088. if (hasObstacle) {
  1089. return false;
  1090. }
  1091. }
  1092. return true;
  1093. }
  1094. return false;
  1095. }
  1096. // 红相的规则
  1097. obj.rules_Elephant_r = function (i, j) {
  1098. var hasObstacle = false;
  1099. var that = this;
  1100. if ((Math.abs(that.action.x - i) == 2 && Math.abs(that.action.y - j) == 2) && j >= 6) {
  1101. var vgaX = (that.action.x + i) / 2;
  1102. var vgaY = (that.action.y + j) / 2;
  1103. console.log(vgaX);
  1104. $.each(that.cheer_arr_ALL, function (ii, ee) {
  1105. if (ee.x == vgaX && ee.y == vgaY) {
  1106. hasObstacle = true;
  1107. return false;
  1108. }
  1109. });
  1110. if (hasObstacle) {
  1111. return false;
  1112. }
  1113. return true;
  1114. }
  1115. return false;
  1116. }
  1117. // 黑象的规则
  1118. obj.rules_Elephant_b = function (i, j) {
  1119. var hasObstacle = false;
  1120. var that = this;
  1121. if ((Math.abs(that.action.x - i) == 2 && Math.abs(that.action.y - j) == 2) && j < 6) {
  1122. var vgaX = (that.action.x + i) / 2;
  1123. var vgaY = (that.action.y + j) / 2;
  1124. // console.log(vgaX);
  1125. $.each(that.cheer_arr_ALL, function (ii, ee) {
  1126. if (ee.x == vgaX && ee.y == vgaY) {
  1127. hasObstacle = true;
  1128. return false;
  1129. }
  1130. });
  1131. if (hasObstacle) {
  1132. return false;
  1133. }
  1134. return true;
  1135. }
  1136. return false;
  1137. }
  1138. // 红仕的规则
  1139. obj.rules_Scholar_r = function (i, j) {
  1140. if (this.action.x == 5 && this.action.y == 9) {
  1141. if (Math.abs(this.action.x - i) == 1 && Math.abs(this.action.y - j) == 1) {
  1142. return true;
  1143. }
  1144. }
  1145. else if (i == 5 && j == 9) {
  1146. return true;
  1147. }
  1148. return false;
  1149. }
  1150. // 黑仕的规则
  1151. obj.rules_Scholar_b = function (i, j) {
  1152. if (this.action.x == 5 && this.action.y == 2) {
  1153. if (Math.abs(this.action.x - i) == 1 && Math.abs(this.action.y - j) == 1) {
  1154. return true;
  1155. }
  1156. } else if (i == 5 && j == 2) {
  1157. return true;
  1158. }
  1159. return false;
  1160. }
  1161. // 帅的规则
  1162. obj.rules_Boss_r = function (i, j) {
  1163. if ((Math.abs(this.action.x - i) == 1 && this.action.y == j)
  1164. || (this.action.x == i && Math.abs(this.action.y - j) == 1)) {
  1165. if (i >= 4 && i <= 6 && j >= 8 && j <= 10) {
  1166. return true;
  1167. } else {
  1168. return false;
  1169. }
  1170. }
  1171. return false;
  1172. }
  1173. // 将的规则
  1174. obj.rules_Boss_b = function (i, j) {
  1175. if ((Math.abs(this.action.x - i) == 1 && this.action.y == j)
  1176. || (this.action.x == i && Math.abs(this.action.y - j) == 1)) {
  1177. if (i >= 4 && i <= 6 && j >= 1 && j <= 3) {
  1178. return true;
  1179. } else {
  1180. return false;
  1181. }
  1182. }
  1183. return false;
  1184. }
  1185. // 兵的规则
  1186. obj.rules_Soldier_r = function (i, j) {
  1187. if (this.action.y <= 5) {
  1188. if ((this.action.x == i && this.action.y - 1 == j) || (this.action.x - 1 == i && this.action.y == j) || (this.action.x + 1 == i && this.action.y == j)) {
  1189. return true;
  1190. }
  1191. } else {
  1192. if (this.action.x == i && this.action.y - 1 == j) {
  1193. return true;
  1194. }
  1195. }
  1196. return false;
  1197. }
  1198. // 卒的规则
  1199. obj.rules_Soldier_b = function (i, j) {
  1200. if (this.action.y > 5) {
  1201. if ((this.action.x == i && this.action.y + 1 == j) || (this.action.x - 1 == i && this.action.y == j) || (this.action.x + 1 == i && this.action.y == j)) {
  1202. return true;
  1203. }
  1204. } else {
  1205. if (this.action.x == i && this.action.y + 1 == j) {
  1206. return true;
  1207. }
  1208. }
  1209. return false;
  1210. }
  1211. // 炮的规则
  1212. obj.rules_Cannon = function (i, j) {
  1213. var that = this;
  1214. if (this.action.x == i || this.action.y == j) {
  1215. var t = 0;
  1216. if (this.action.x == i) {
  1217. var temp = this.action.y;
  1218. if (temp < j) {
  1219. while (++temp != j) {
  1220. $.each(this.cheer_arr_ALL, function (ii, ee) {
  1221. if (ee.x == that.action.x && ee.y == temp) {
  1222. t++;
  1223. return false;
  1224. }
  1225. });
  1226. }
  1227. return t;
  1228. } else {
  1229. while (--temp != j) {
  1230. $.each(this.cheer_arr_ALL, function (ii, ee) {
  1231. if (ee.x == that.action.x && ee.y == temp) {
  1232. t++;
  1233. return false;
  1234. }
  1235. });
  1236. }
  1237. return t;
  1238. }
  1239. } else {
  1240. var temp = this.action.x;
  1241. if (temp < i) {
  1242. while (++temp != i) {
  1243. $.each(this.cheer_arr_ALL, function (ii, ee) {
  1244. if (ee.x == temp && ee.y == that.action.y) {
  1245. t++;
  1246. return false;
  1247. }
  1248. });
  1249. }
  1250. return t;
  1251. } else {
  1252. while (--temp != i) {
  1253. $.each(this.cheer_arr_ALL, function (ii, ee) {
  1254. if (ee.x == temp && ee.y == that.action.y) {
  1255. t++;
  1256. return false;
  1257. }
  1258. });
  1259. }
  1260. return t;
  1261. }
  1262. }
  1263. }
  1264. return 2;
  1265. }
  1266. obj.inArray = function (x, y) {
  1267. var hasObstacle = false;
  1268. $.each(this.cheer_arr_ALL, function (ii, ee) {
  1269. if (ee.x == x && ee.y == y) {
  1270. hasObstacle = true;
  1271. return false;
  1272. }
  1273. });
  1274. return hasObstacle;
  1275. }
  1276. obj.init();
  1277. </script>
  1278. </html>