js+canvas 一只一担小游戏
<!DOCTYPE html>
<html> <head>
<meta charset="UTF-8">
<title></title>
<style>
* {
padding: 0;
margin: 0;
text-align: center;
vertical-align: middle;
}
</style>
</head> <body>
<canvas id="one-two">此浏览器可能不支持canvas</canvas>
</body>
<script type="text/javascript" charset="utf-8">
class OneTwo {
constructor(canvasId) {
this.rowCount = 3;
this.colCount = this.rowCount;
this.canvas = document.getElementById(canvasId);
this.ctx = this.canvas.getContext("2d");
this.cellWidth = 100; // 单位px this.offsetX = this.cellWidth / 2;
this.offsetY = this.offsetX;
this.width = this.cellWidth * this.rowCount + this.offsetX * 2;
this.height = this.width;
this.canvasId = canvasId; this.pieces = [];
this.active = 2; //
this.status = 1; // 1添加子 2移动子
this.R = this.cellWidth * 0.3;
this.hint = []; // 提示位置
this.toMove = {}; // 即将移动的棋子 this.init();
} // 渲染页面
renderUi() {
//清除之前的画布
this.ctx.clearRect(0, 0, this.width, this.height); // 重绘画布
this.drawMap();
this.drawPieces();
console.log(this.toMove, "渲染前选择的棋子")
this.highLight();
} //初始化
init() {
this.initCanvas();
this.drawMap();
this.initPieces();
} //
initCanvas() {
this.canvas.width = this.width;
this.canvas.height = this.height;
} // initPieces(){}
initPieces() {
for(let i = 0; i <= this.rowCount; i++) {
this.pieces[i] = [];
for(let j = 0; j <= this.colCount; j++) {
this.pieces[i][j] = 0;
}
}
console.log(this.pieces);
} // 画地图
drawMap() {
// 背景
this.ctx.beginPath();
this.ctx.rect(0, 0, this.width, this.height);
this.ctx.closePath();
this.ctx.fillStyle = "#0099CC";
this.ctx.fill(); // 画横线
this.ctx.strokeStyle = "black";
this.ctx.beginPath();
for(let i = 0; i <= this.rowCount; i++) {
this.ctx.moveTo(0 + this.offsetX, this.cellWidth * i + this.offsetY);
this.ctx.lineTo(this.cellWidth * this.rowCount + this.offsetX, this.cellWidth * i + this.offsetY);
}
this.ctx.stroke(); // 画纵线
this.ctx.beginPath();
for(let i = 0; i <= this.colCount; i++) {
this.ctx.moveTo(this.cellWidth * i + this.offsetX, 0 + this.offsetY);
this.ctx.lineTo(this.cellWidth * i + this.offsetX, this.cellWidth * this.colCount + this.offsetY);
}
this.ctx.stroke();
} //画一个棋子或一个提示圆点
drawDot(x, y, r, color) {
this.ctx.beginPath();
this.ctx.arc(x, y, r, 0, 2 * Math.PI);
this.ctx.closePath(); this.ctx.fillStyle = color;
this.ctx.fill();
} // 画所有的棋子
drawPieces() {
//console.log(this.pieces)
for(var i = 0; i < this.pieces.length; i++) {
for(let j = 0; j < this.pieces[i].length; j++) {
if(this.pieces[i][j] !== 0) {
this.drawOnePiece(i, j, this.R, this.getColor(this.pieces[i][j]));
}
}
}
} //
drawOnePiece(i, j, r, color) {
var x = i * this.cellWidth + this.offsetX;
var y = j * this.cellWidth + this.offsetY;
this.drawDot(x, y, r, color);
} // 获取某个棋子的颜色 0——空 1——白 2——黑
getColor(num) {
var res = "";
switch(num) {
case 0:
res = "";
break;
case 2:
res = "black";
break;
case 1:
res = "white";
break;
case -1:
res = "yellow";
break;
default:
res = "red"; // 错误了
}
return res;
} // cango
canGo(x, y) {
if(this.pieces[x][y] == 0) {
return true;
} else {
return false;
}
} // 添加棋
add(x, y) {
if(this.canGo(x, y)) {
this.pieces[x][y] = this.active;
// this.drawOnePiece(x,y);
this.weed(x, y);
this.renderUi();
this.exchange();
console.log(this.pieces, "当前棋局")
} else {
console.warn("这里貌似不能添加棋子");
}
} // 是否可以移动
oneCanMove(x, y, oneSide) {
var moveRange = this.getMoveRange(x, y, oneSide);
if(moveRange.length) {
return true;
} else {
return false;
}
}
// 所有的棋子是否可以移动
hasCanMovePiece(oneSide) {
var oneSideCanMove = false;
for(let i = 0, len = this.pieces.length; i < len; i++) {
for(let j = 0, len1 = this.pieces[i].length; j < len1; j++) {
if(this.pieces[i][j] === oneSide) {
if(this.oneCanMove(i, j, oneSide)) {
oneSideCanMove = true;
break;
}
}
}
}
return oneSideCanMove;
} // 获取某一方的棋子数量
getCount(oneSide) {
var count = 0;
for(let i = 0, len = this.pieces.length; i < len; i++) {
for(let j = 0, len1 = this.pieces[i].length; j < len1; j++) {
if(this.pieces[i][j] === oneSide) {
count++;
}
}
}
return count;
} // 没有棋走,自己拔出自己的一颗子
weedOne(x, y) {
this.pieces[x][y] = 0;
} // 转换状态从添加棋子到移动棋子
changeStatus() {
console.log("状态转换了");
this.status = 2;
}
// 当棋盘下满的时候转换状态。下满的时候黑方+白方的总数等于16
wetherChangeStatus() {
console.log("判断是否要转换状态", this.getCount(1) + this.getCount(2))
if(this.getCount(1) + this.getCount(2) == 16) {
return true;
} else {
return false;
}
} // 切换角色(换着走棋)
exchange() {
this.active = this.active == 1 ? 2 : 1;
} // 获取敌方数字
getEnemy(oneSide) {
return oneSide == 1 ? 2 : 1;
} // 选择移动的棋子
chooseToMove(x, y, oneSide) {
if(this.oneCanMove(x, y, oneSide)) {
this.toMove.x = x;
this.toMove.y = y;
//this.highLight();
} else {
delete this.toMove.x;
delete this.toMove.y;
console.warn("这个棋子不可以移动");
}
console.log(this.toMove, "选择后的要移动的棋子")
} // 判断移动的最终位置是否在移动范围内
inRange(x, y, oneSide) {
var moveRange = this.getMoveRange(this.toMove.x, this.toMove.y, oneSide);
console.log(moveRange, "移动范围");
var flag = false;
for(let i = 0, len = moveRange.length; i < len; i++) {
if(x == moveRange[i].x && y == moveRange[i].y) {
flag = true;
break;
}
}
return flag;
} // 移动 1.选择需要移动的棋子 2.点击推荐的可以移动的位置 3.之前的位置赋值为空,结束的位置赋值为当前棋子
move(x, y) {
if(Object.keys(this.toMove).length) { // 已经选了将要移动的棋子 移动
if(this.pieces[x][y] == this.active) { // 这时候想移动别的棋子
console.log("重新选择要移动的棋子x:" + x + ";y:" + y);
this.chooseToMove(x, y, this.active);
//this.renderUi();
} else {
if(this.inRange(x, y, this.active)) { // 移动当前棋子 删除己选择的移动棋子 去除已选择状态
console.log("移动棋子");
this.pieces[x][y] = this.active;
this.pieces[this.toMove.x][this.toMove.y] = 0;
this.weed(x, y); this.exchange();
delete this.toMove.x;
delete this.toMove.y;
//this.renderUi(); } else {
console.log("移动范围超标了,每次只可以横向或纵向移动一格");
}
}
} else { // 选择要移动的棋子
console.log("选择要移动的棋子x:" + x + ";y:" + y);
this.chooseToMove(x, y, this.active);
//this.renderUi();
} this.renderUi();
} // goStep
goStep(x, y) {
if(this.status == 1) { // 添加
this.add(x, y);
if(this.wetherChangeStatus()) {
this.changeStatus();
}
} else { // 拔子 移动
if(this.hasCanMovePiece(this.active)) { // 移动
console.log("移动主流程");
this.move(x, y);
this.getVictor();
} else { //拔子
console.log("拔子主流程");
if(this.pieces[x][y] == this.active) {
this.weedOne(x, y);
this.exchange();
this.renderUi();
this.getVictor();
} else {
console.log("子不是你的,拔个锤子");
}
}
}
} // 给选择的棋子加上高亮标记
highLight() {
console.log(this.toMove);
if(!Object.keys(this.toMove).length) {
return;
}
console.log("要开始画了")
var x = this.toMove.x;
var y = this.toMove.y;
var pArr = ["lt", "ld", "rd", "rt"];
for(let i = 0, len = pArr.length; i < len; i++) {
var xx = x * this.cellWidth + this.offsetX;
var yy = y * this.cellWidth + this.offsetY;
this.drawRightAngle(xx, yy, this.R, 10, pArr[i]);
}
} // 画直角 以圆心为起点,2r为边的正方形的角,length为画的线的长度,p为需要画的角(如:左上角lt)
drawRightAngle(x, y, r, length, p, color) {
color = color || "yellow";
var nd = this.nd(p);
var h = nd.h;
var v = nd.v;
this.ctx.beginPath();
this.ctx.strokeStyle = color;
this.ctx.moveTo(x + h * r, y + v * r);
this.ctx.lineTo(x + h * r - h * length, y + v * r);
this.ctx.moveTo(x + h * r, y + v * r);
this.ctx.lineTo(x + h * r, y + v * r - v * length);
this.ctx.closePath();
this.ctx.stroke();
} // 显示提示位置 // 显示移动范围
getMoveRange(x, y, oneSide) {
var moveRange = [];
if(this.pieces[x][y] === oneSide) {
var dArr = ["r", "d", "l", "t"];
var direction, h, v;
for(let i = 0, len = dArr.length; i < len; i++) {
direction = this.nd(dArr[i]);
h = direction.h;
v = direction.v;
if(this.pieces[x + h] && this.pieces[x + h][y + v] === 0) {
moveRange.push({
x: x + h,
y: y + v
});
}
}
} else {
console.log("空位置和敌方的棋子不可以移动,请移动自己的棋子到空位置");
}
return moveRange;
} // 获取赢家
getVictor() {
var white = this.getCount(1);
var black = this.getCount(2);
if(white == 0) {
setTimeout(() => {
alert("黑棋赢咧");
}, 50);
} else if(black == 0) {
setTimeout(() => {
alert("白棋赢咧");
}, 50);
}
} // 拔子
weed(x, y) {
var dArr = ["r", "d", "l", "t"];
var direction, h, v, np;
for(let i = 0, len = dArr.length; i < len; i++) {
direction = this.nd(dArr[i]);
h = direction.h;
v = direction.v;
np = this.np(x, y, dArr[i]);
// 先看自己有没有凑够两个子,凑够了才可以吃子
if(np === 0) {
if(this.pieces[x + h] && this.pieces[x + h][y + v] === this.active) { //AA??
if(this.pieces[x + 2 * h] && this.pieces[x + 2 * h][y + 2 * v] === this.getEnemy(this.active)) { //AAB?
if(this.pieces[x + 3 * h] && this.pieces[x + 3 * h][y + 3 * v] === this.getEnemy(this.active)) { //AABB
this.pieces[x + 2 * h][y + 2 * v] = 0;
this.pieces[x + 3 * h][y + 3 * v] = 0;
} else if(this.pieces[x + 3 * h] && this.pieces[x + 3 * h][y + 3 * v] === this.active) { //AABA
continue;
} else { // AABO
this.pieces[x + 2 * h][y + 2 * v] = 0;
}
} else { // AAA? 或AAO?
continue;
}
} else { //AB??或AO??
continue;
}
} else if(np === 1) {
if(this.pieces[x + h] && this.pieces[x + h][y + v] === this.active) { //?AA?
if(this.pieces[x - h] && this.pieces[x - h][y - v] === this.getEnemy(this.active)) { //BAA?
if(this.pieces[x + 2 * h] && this.pieces[x + 2 * h][y + 2 * v] === this.getEnemy(this.active)) { //BAAB
continue;
} else if(this.pieces[x + 2 * h] && this.pieces[x + 2 * h][y + 2 * v] === this.active) { //BAAA
continue;
} else { //BAAO
this.pieces[x - h][y - v] = 0;
}
} else if(this.pieces[x - h] && this.pieces[x - h][y - v] === this.active) { // AAA?
continue;
} else { //OAA?
if(this.pieces[x + 2 * h] && this.pieces[x + 2 * h][y + 2 * v] === this.getEnemy(this.active)) {
this.pieces[x + 2 * h][y + 2 * v] = 0;
} else {
continue;
}
}
} else if(this.pieces[x + h] && this.pieces[x + h][y + v] === this.getEnemy(this.active)) { //?AB?
if(this.pieces[x - h] && this.pieces[x - h][y - v] === this.active) { //AAB?
if(this.pieces[x + 2 * h] && this.pieces[x + 2 * h][y + 2 * v] === this.getEnemy(this.active)) { //AABB
this.pieces[x + h][y + v] = 0;
this.pieces[x + 2 * h][y + 2 * v] = 0;
} else if(this.pieces[x + 2 * h] && this.pieces[x + 2 * h][y + 2 * v] === this.active) { //AABA
continue;
} else { //AABO
this.pieces[x + h][y + v] = 0;
}
} else { //OAB? BAB?
continue;
}
} else { //?AO?
continue;
}
} else if(np === 2) {
if(this.pieces[x + h] && this.pieces[x + h][y + v] === this.active) { //??AA
if(this.pieces[x - h] && this.pieces[x - h][y - v] === this.getEnemy(this.active)) { //?BAA
if(this.pieces[x - 2 * h] && this.pieces[x - 2 * h][y - 2 * v] === this.getEnemy(this.active)) { //BBAA
this.pieces[x - 2 * h][y - 2 * v] = 0;
this.pieces[x - h][y - v] = 0;
} else if(this.pieces[x - 2 * h] && this.pieces[x - 2 * h][y - 2 * v] === this.active) { //ABAA
continue;
} else { //OBAA
this.pieces[x - h][y - v] = 0;
}
} else { // ?OAA 或?AAA
continue;
}
} else if(this.pieces[x + h] && this.pieces[x + h][y + v] === this.getEnemy(this.active)) { //??AB
if(this.pieces[x - h] && this.pieces[x - h][y - v] === this.active) { //?AAB
if(this.pieces[x - 2 * h] && this.pieces[x - 2 * h][y - 2 * v] === this.getEnemy(this.active)) { //BAAB
// this.pieces[x-h][y-v] = 0;
// this.pieces[x-2*h][y-2*v] = 0;
continue;
} else if(this.pieces[x - 2 * h] && this.pieces[x - 2 * h][y - 2 * v] === this.active) { //AAAB
continue;
} else { //OAAB
this.pieces[x + h][y + v] = 0;
}
} else { //?BAB或?OAB
continue;
}
} else { //??AO
if(this.pieces[x - h] && this.pieces[x - h][y - v] === this.active) { //?AAO
if(this.pieces[x - 2 * h] && this.pieces[x - 2 * h][y - 2 * v] === this.getEnemy(this.active)) { //BAAO
this.pieces[x - 2 * h][y - 2 * v] = 0;
} else { //AAAO或OAAO
continue;
}
} else { //?BAO 或?OAO
continue;
}
}
} else if(np === 3) {
if(this.pieces[x - h] && this.pieces[x - h][y - v] === this.active) { //??AA
if(this.pieces[x - 2 * h] && this.pieces[x - 2 * h][y - 2 * v] === this.getEnemy(this.active)) { //?BAA
if(this.pieces[x - 3 * h] && this.pieces[x - 3 * h][y - 3 * v] === this.getEnemy(this.active)) { //BBAA
this.pieces[x - 2 * h][y - 2 * v] = 0;
this.pieces[x - 3 * h][y - 3 * v] = 0;
} else if(this.pieces[x - 3 * h] && this.pieces[x - 3 * h][y - 3 * v] === this.active) { //ABAA
continue;
} else { // OBAA
this.pieces[x - 2 * h][y - 2 * v] = 0;
}
} else { // ?AAA 或?OAA
continue;
}
} else { //??BA或??OA
continue;
}
}
}
} // 方向数字化numberDirection r(1,0) rd(1,1) ld(-1,1)
nd(direction) {
var res = {
h: 0,
v: 0
}; // h horizontal v vertical
switch(direction) {
case "r":
res.h = 1;
res.v = 0;
break;
case "rd":
res.h = 1;
res.v = 1;
break;
case "d":
res.h = 0;
res.v = 1;
break;
case "ld":
res.h = -1;
res.v = 1;
break;
case "l":
res.h = -1;
res.v = 0;
break;
case "lt":
res.h = -1;
res.v = -1;
break;
case "t":
res.h = 0;
res.v = -1;
break;
case "rt":
res.h = 1;
res.v = -1;
break;
default:
console.error("方向输入有误");
}
return res;
} // 某个方向上当前(x,y)是第几个位置
np(x, y, direction) {
var d = this.nd(direction);
if(d.h !== 0) {
if(d.h === 1) {
return x;
} else { // -1
return 3 - x;
}
} else {
if(d.v === 1) {
return y;
} else { //-1
return 3 - y;
}
}
console.error("np出错了");
return 0;
} // 深拷贝
deepClone(values) {
var copy; // Handle the 3 simple types, and null or undefined
if(null == values || "object" != typeof values) return values; // Handle Date
if(values instanceof Date) {
copy = new Date();
copy.setTime(values.getTime());
return copy;
} // Handle Array
if(values instanceof Array) {
copy = [];
for(var i = 0, len = values.length; i < len; i++) {
copy[i] = this.deepClone(values[i]);
}
return copy;
} // Handle Object
if(values instanceof Object) {
copy = {};
for(var attr in values) {
if(values.hasOwnProperty(attr)) copy[attr] = this.deepClone(values[attr]);
}
return copy;
} throw new Error("Unable to copy values! Its type isn't supported.");
}
}
</script>
<script type="text/javascript">
var oneTwo = new OneTwo("one-two");
var canvas = document.getElementById("one-two");
console.log(canvas.getBoundingClientRect());
var offset;
canvas.addEventListener("click", function(e) {
offset = canvas.getBoundingClientRect();
var x = Math.floor((e.clientX - offset.left) / oneTwo.cellWidth);
var y = Math.floor((e.clientY - offset.top) / oneTwo.cellWidth);
console.log(x, y, "点击位置"); // 走棋
oneTwo.goStep(x, y);
}, false);
</script> </html>
玩法简介:
1. 一只一担是流传在中国米黄玉之乡的谭山镇的一种双人、益智、棋牌类游戏。棋盘是4*4的方格线,棋子放在线与线的交界处。棋子可以用任意两种可以区分的道具,如纸团、石子、木棍等。因此随时随地,只要在地上画个4*4的方格,弄几个石子什么的就可以玩了,非常的方便。
这个游戏分为两个阶段——谋篇布阵和征战。
刚开始,双方在棋盘上抢占有利位置,并互相征战。棋盘上位置占满后,一方弃掉自己的一个子,棋盘上就有空位置了,双方就开始交替移动棋子,进行征战,没有可以移动的棋子时,弃掉自己的一个棋子,轮到对方走棋。直到最后一方没有棋子了结束战斗。
吃子规则
两个(一担)可以吃掉对方的一个或者两个(一担)。具体图形如下(A——甲方,B——乙方,O——空位置):
1. AABO——甲由?ABO,在第一个位置走一个棋后变成AABO;或者由A?BO,在第二个位置走一个棋后变成AABO。则第三个位置的乙方棋子被剔除棋盘,变成空位置,再由双方来争夺这个位置。
2.OAAB——甲由OA?B,在第三个位置走一个棋后变成OAAB;或者由O?AB,在第二个位置有一个棋后变成功能OAAB。则第四个位置的乙方棋子被剔除棋盘,变成空位置,再由双方来争夺这个位置。
3. AABB——甲由?ABB,在第一个位置走一个棋后变成AABB;或者由A?BB,在第二个位置有一个棋后变成功能AABB。则第三和第四个位置的乙方棋子都被剔除棋盘,变成空位置,再由双方来争夺这个位置。
其他的情况不可以吃棋。如以下两个图形。
1. AABA
2.AAAB
通常用这两种棋来防守,防止对方吃掉自己的棋子。
赶紧复制以下去玩玩吧~
js+canvas 一只一担小游戏的更多相关文章
- canvas写个简单的小游戏
之前在HTML5 Canvas属性和方法汇总一文中,介绍过Canvas的各种属性以及方法的说明,并列举了自己写的一些Canvas demo,接下来开始写一个简单的小游戏吧,有多简单,这么说吧,代码不到 ...
- html5+Canvas实现酷炫的小游戏
最近除了做业务,也在尝试学习h5和移动端,在这个过程中,学到了很多,利用h5和canvas做了一个爱心鱼的小游戏.点这里去玩一下 PS: 貌似有点闪屏,亲测多刷新两下就好了==.代码在本地跑都不会闪, ...
- JS实现别踩白块小游戏
最近有朋友找我用JS帮忙仿做一个别踩白块的小游戏程序,但他给的源代码较麻烦,而且没有注释,理解起来很无力,我就以自己的想法自己做了这个小游戏,主要是应用JS对DOM和数组的操作. 程序思路:如图:将游 ...
- JS高级---案例:贪吃蛇小游戏
案例:贪吃蛇小游戏 可以玩的小游戏,略复杂,过了2遍,先pass吧 先创建构造函数,再给原型添加方法.分别创建食物,小蛇和游戏对象. 食物,小蛇的横纵坐标,设置最大最小值,运动起来的函数,按上下左右键 ...
- 用canvas和原生js写的一个笨鸟先飞的小游戏(暂时只有一个关卡)
其中一个画布背景是一张图片,还有小鸟,两个管子的图片.暂时不知道怎么附上去就不添加了.这里只有源代码,css和js都是在html写着的,感觉比他们的容易吧,hah <!DOCTYPE html& ...
- 用canvas写个接水管小游戏
声明:本文为原创文章,如需转载,请注明来源WAxes,谢谢! 过年的十八天假期迷迷糊糊一下子就过去了(LZ还是实习生,鉴于大学最后一个寒假了,所以就多请了好多天假),又要返工上班了.这是年后的第一篇博 ...
- js+canvas(H5)实现小球移动小demo
*canvas提供画布,大小自定义,js得到画布,从画布对象通过getContext('2d')来得到画笔,然后就可以开始画了 代码: <!DOCTYPE html> <html l ...
- js中的投掷筛子的小游戏
代码 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8&qu ...
- js下载文件方法与原理小分析
原理:html的a标签,设置dawnload属性后,可以下载href指向的文件. 在js中往往是点击某一个按钮后下载一个文件,并且文件的地址是变化的.因此我们可以动态创建一个a标签,设置好downlo ...
随机推荐
- Flask中的session机制
cookie和sessioncookie:网站中,http请求是无状态的,第一次和服务器连接后并且登陆成功后,第二次请求服务器依然不能知道当前请求是哪个用户.cookie的出现就是解决了改问题,第一次 ...
- SpringCloud-微服务的注册与发现Eureka
一.SpringCloud简介 Spring Cloud是一系列框架的有序集合.它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册.配置中心.消息总线.负载均 ...
- python基础--包、logging、hashlib、openpyxl、深浅拷贝
包:它是一系列模块文件的结合体,表现形式就是一个文件夹,该文件夹内部通常会有一个__init__.py文件,包的本质还是一个模块. 首次导入包:(在导入语句中中 . 号的左边肯定是一个包(文件夹)) ...
- go语言:类型转换
类型转换用于将一种类型的变量转换为另一种类型的变量. 有以下场景: package main import "fmt" func main() { var sum int = 17 ...
- 2019-8-31-Developing-Universal-Windows-Apps-开发UWA应用-问答
title author date CreateTime categories Developing Universal Windows Apps 开发UWA应用 问答 lindexi 2019-08 ...
- python 变量离散化
- Leetcode22.Generate Parentheses括号生成
给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合. 例如,给出 n = 3,生成结果为: [ "((()))", "(()())& ...
- 二维码识别项目zxing横屏改为竖屏
第1步: 在AndroidManifest中将CaptureActivity的screenOrientation属性做如下修改: android:screenOrientation="por ...
- 为Array对象添加一个去除重复项的方法
输入例子 [false, true, undefined, null, NaN, 0, 1, {}, {}, 'a', 'a', NaN].uniq() 输出例子 [false, true, unde ...
- ThInkPHP验证码不显示,解决方法汇总
出现ThInkPHP验证码不显示的情况 官方提示如下:如果无法显示验证码,请检查:① PHP是否已经安装GD库支持:② 输出之前是否有任何的输出(尤其是UTF8的BOM头信息输出):(打开验证码文件为 ...