- 浏览: 5162586 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
silence19841230:
先拿走看看
SpringBoot2.0开发WebSocket应用完整示例 -
wallimn:
masuweng 写道发下源码下载地址吧!三个相关文件打了个包 ...
SpringBoot2.0开发WebSocket应用完整示例 -
masuweng:
发下源码下载地址吧!
SpringBoot2.0开发WebSocket应用完整示例 -
masuweng:
SpringBoot2.0开发WebSocket应用完整示例 -
wallimn:
水淼火 写道你好,我使用以后,图标不显示,应该怎么引用呢,谢谢 ...
前端框架iviewui使用示例之菜单+多Tab页布局
今天初始完成了页面端的开发工作。把遇到的问题说一说。
(1)开始时,对javascript的对象或数组拷贝、赋值理解不是很透,折磨了我好长时间。 理解了对象或数组的赋值,实际上相当于C语言中的指针地址赋值,就知道了保存每一步的棋盘状态,要把对象拷贝一个副本,避免后继的变化,影响保存的状态。
(2)JQuery提供了对象拷贝的方法,extend。这个方法有深拷贝、浅拷贝之分,如果浅拷贝,不复制对象中的对象。还有个问题,就是数组拷贝后,会变成一个伪数组,能用下标取值,但不支持length属性。调这个错也用了很长时间。还好chrome支持断点调试。
目前支持功能:交替落子、布局摆子、撤销、重做、新建布局。界面如下图所示:
整个工程涉及四个文件。组件文件、状态管理文件、样式文件、网页文件。下面提供的是源码,依赖bootstrap、jquery。
一、状态管理文件GoStateManager.js
二、组件文件Go.js
三、样式文件go.css
四、网页文件go.html
编译好的文件,请点击附件下载,可以单机基于浏览器运行。所有源码已经托管到码云,访问地址:https://git.oschina.net/wallimn/rwne.git。把插件也传上去了,有点儿大。
(1)开始时,对javascript的对象或数组拷贝、赋值理解不是很透,折磨了我好长时间。 理解了对象或数组的赋值,实际上相当于C语言中的指针地址赋值,就知道了保存每一步的棋盘状态,要把对象拷贝一个副本,避免后继的变化,影响保存的状态。
(2)JQuery提供了对象拷贝的方法,extend。这个方法有深拷贝、浅拷贝之分,如果浅拷贝,不复制对象中的对象。还有个问题,就是数组拷贝后,会变成一个伪数组,能用下标取值,但不支持length属性。调这个错也用了很长时间。还好chrome支持断点调试。
目前支持功能:交替落子、布局摆子、撤销、重做、新建布局。界面如下图所示:
整个工程涉及四个文件。组件文件、状态管理文件、样式文件、网页文件。下面提供的是源码,依赖bootstrap、jquery。
一、状态管理文件GoStateManager.js
/** * 用于GO的状态管理。管理所有组件的状态,所有组件订阅事件,同步状态 * http://wallimn.iteye.com */ "use strict" var Events = require('events'); class GoStateManager { constructor() { this.initState(); this.initList(); this.eventEmitter = new Events.EventEmitter(); this.eventEmitter.setMaxListeners(500); var t1 = this.getDefaultPieceState(); var t2 = this.getDefaultPieceState(); }; //一个棋子的初始状态 getDefaultPieceState(){ return {visibility:'hidden',num:0,black:false}; } //所有棋子的初始状态 getDefaultPieces(){ var pieces=[];//所有棋子状态 for(var i=0 ; i<19*19; i++) pieces.push(this.getDefaultPieceState()); return pieces; } initState(){ //指示当前要下的子的状态,该状态使用后,调用next方法,切换状态 this.current= { index:1,//当前步数 goBlack:true,//是否是黑子,指行棋时 placeBlack:true,//是否是黑子,指布局时 numShow:false,//是否显示数字 place:false,//是否是布局摆子,如果是,不改变当前步数,布局时摆的棋子上面不显示数字 }; //所有棋子的状态 this.pieces = this.getDefaultPieces(); } //初始化重做、撤销两个队列 initList(){ this.undoList = [];//后进先出队列 this.redoList = [];//后进先出队列 } //将状态压栈,保存,保存的是对象的副本。 pushUndoList(current,pieces){ this.undoList.push({ current:this.cloneObject(current), pieces:this.cloneObject(pieces) }); } //清空RedoList,当执行下一步时执行此方法 clearRedoList(){ var len=this.redoList.length; if(len>0) this.redoList.splice(0,len); } //将状态压栈,保存,保存的是对象的副本。 pushRedoList(current,pieces){ this.redoList.push({ current:this.cloneObject(current), pieces:this.cloneObject(pieces) }); } //弹出队列中的元素,复制一个副本 popList(list) { var record = list.pop(); return { current: this.cloneObject(record.current), pieces: this.cloneObject(record.pieces) } } //输出链表内容,用于调试 printList(list){ for(var i=0; i<list.length; i++){ console.log("第%d步:%s",i,this.getVisiblePieces(list[i].pieces)); } } getVisiblePieces(pieces){ var info = ""; for(var j=0; j<19*19; j++){ if(pieces[j].visibility=='visible'){ info = info+pieces[j].num+','; } } //console.log("可见棋子序号:"+info); return info; } //撤销 undo(){ if (this.undoList.length==0){ console.log("不能撤销了!"); return; } //当前状态压入RedoList this.pushRedoList(this.current,this.pieces); var record = this.popList(this.undoList) this.current = record.current; this.pieces = record.pieces; this.pubCurrentChange(); this.pubPieceChange(); } //前进一步 redo(){ if (this.redoList.length==0){ console.log("不能前进了!"); return; } this.pushUndoList(this.current,this.pieces); var record = this.popList(this.redoList) this.current = record.current; this.pieces = record.pieces; //this.printList(this.undoList); //this.printList(this.redoList); this.pubCurrentChange(); this.pubPieceChange(); } //订阅当前状态变化事件 subCurrentChange(listener) { //console.log("订阅状态事件!"); this.eventEmitter.addListener('currentChange',listener); } //状态当前变化事件发生,通知监听器 pubCurrentChange(){ //console.log("发布状态事件"); this.eventEmitter.emit("currentChange",this.current); } //订阅棋子状态变化事件 subPieceChange(listener) { //console.log("订阅棋子事件!"); this.eventEmitter.addListener('pieceChange',listener); } //状态棋子变化事件发生,通知监听器 pubPieceChange(){ //console.log("发布棋子事件"); //传递数据,对解耦有一点儿帮助 this.eventEmitter.emit("pieceChange",this.pieces); } //推进当前状态到下一步 //这个函数内部调用 next(){ this.printList(this.undoList); if(this.current.place==true){ //如果是布局状态,不改变编号、颜色 } else{ this.current.index++; this.current.goBlack=!this.current.goBlack; } this.pubCurrentChange(); } //克隆对象 //数组被jquery复制后,变成了类数组(伪数组),不带有length方法,这个也比较坑 cloneObject(object){ return $.extend(true,{},object);//深层次复制。这个比较坑 } //是否处于布局状态 isPlace(){ return this.current.place==true; } //设置布局状态 setPlace(bBlack){ this.current.place=true; this.current.placeBlack=(bBlack==true); this.pubCurrentChange(); } //重新开始 restart(){ this.initState(); this.initList(); this.pubCurrentChange(); this.pubPieceChange(); } //返回当前的步数 getCurrentIndex(){ return this.current.index; } //返回当前状态 getCurrent(){ return this.current; } //返回所有棋子状态 getPieces(){ return this.pieces; } //设置先行方 setFirst(bBlack) { this.current.place = false; //仅处于第一步时,可以改变行棋的黑白颜色 if( this.current.index==1){ this.current.goBlack=(bBlack==true); } this.pubCurrentChange(); } //在棋上点击 //如果棋子状态变化,则返回为true,否则返回false clickPiece(index){ //每次下一步之前的状态都记下来,以便能够回退 this.pushUndoList(this.current,this.pieces); this.clearRedoList(); var state=this.pieces[index];//应该传递的是指针,相当于起了个别名,实际对应同一块内存地址 if (state.visibility=='visible' && this.isPlace()==false){//棋子可见、非布局状态 console.log("棋子可见、非布局状态,退出!"); return false; } if (state.visibility=='visible' && this.isPlace()==true && state.num!=0){//棋子可见、布局状态,且非布局棋子 console.log("棋子可见、布局状态,且非布局棋子,退出!"); return false; } //console.log('可以修改棋子状态'); if (this.isPlace()==false){//行棋中 state.num = this.current.index; state.black = this.current.goBlack; state.visibility = 'visible'; } else{//布局 state.num=0;//布局状态下,放的棋子,其数字设置为0 //如果原来棋子已经显示,且颜色相同,用是布局摆的棋子,设置其隐藏 if(state.visibility=='visible' && this.current.placeBlack==state.black && state.num==0){ state.visibility = 'hidden'; //棋子颜色不重要,下次再显示时,会设置颜色 } else{ state.black = this.current.placeBlack; state.visibility = 'visible'; } } this.pubPieceChange(); //StateManager.setPieceState(this.state.pieceId,state);//这里有点儿乱 //这个放最后,完成大大压栈工作 this.next(); //this.setState(state); return true; } } module.exports = new GoStateManager();
二、组件文件Go.js
//http://wallimn.iteye.com var React = require('react'); var ReactDOM = require('react-dom'); require('../../../css/go.css'); var StateManager = require('../../store/main/GoStateManager'); "user strick" //当前步状态指示器,可以指标当前步数、落子方、是否处理布局状态等信息 class CurrentLabel extends React.Component{ constructor(props){ super(props); //使用全局的状态作为初始状态 var current = StateManager.getCurrent(); this.state={ index:current.index, goBlack:current.goBlack, placeBlack:current.placeBlack, place:current.place, }; //设置currentChange函数的this this.currentChange=this.currentChange.bind(this); //注册事件监听器 StateManager.subCurrentChange(this.currentChange); } //状态改变事件监听器,调整组件的状态 currentChange(current){ this.setState({ index:current.index, goBlack:current.goBlack, placeBlack:current.placeBlack, place:current.place, }); } render(){ return <span className="bg-success"> <strong>当前步数:</strong>{this.state.index} <strong> 落子方:</strong>{this.state.goBlack==true?'黑方':'白方'} <strong> 布局子:</strong>{this.state.placeBlack==true?'黑子':'白子'} <strong> 状态:</strong>{this.state.place==true?'布局':'行棋'} </span>; } } //围棋桌面 class GoDesk extends React.Component { constructor(props) { super(props); this.state = { refresh: false }; } render() { var self = this; this.state.refresh=false; var pieces = []; //每个交叉点上都放一个子,只是未点击时不显示,棋子黑白、编号都不重要,用户点击时会修改 for (var i=0; i<19*19; i++){ pieces.push( <GoPiece black={i % 2==0 ?true:false} key={'go'+(i+1)} pieceId={i}/> ); } return <div className="go-desk"> <div className="go-opr"> <GoBtns /> </div> <div className="go-board"> {pieces} </div> <div className="text-center"> <CurrentLabel /> </div> </div>; } } //使用bootstap的按钮组,可以不用控制按钮的状态,较为方便,还没有完全走通 //使用radio按钮组实现几个控制行棋的按钮,因为只能处于其中一个状态 class GoBtns extends React.Component{ constructor(props){ super(props); this.state={index:1};//指标按钮的激活状态,没有完成 this.setFirstClickHandle=this.setFirstClickHandle.bind(this); this.setPlaceClickHandle=this.setPlaceClickHandle.bind(this); this.newClickHandle=this.newClickHandle.bind(this); this.saveClickHandle=this.saveClickHandle.bind(this); this.loadClickHandle=this.loadClickHandle.bind(this); this.redoClickHandle=this.redoClickHandle.bind(this); this.undoClickHandle=this.undoClickHandle.bind(this); } //这个还没有验证 getActiveBtnIndex(){ if (StateManager.current.place==false) return 1;//黑先、白先差别不大,似乎没有影响 else if (StateManager.current.placeBlack)return 2; else return 3; } //设置黑先 setFirstClickHandle(bBlack){ console.log("设置落子方颜色:"+bBlack); StateManager.setFirst(bBlack); } setPlaceClickHandle(bBlack){ console.log("设置布局子颜色:"+bBlack); StateManager.setPlace(bBlack); } newClickHandle(){ if (confirm('您确定要新建布局吗?')==true){ StateManager.restart(); } } saveClickHandle(){ alert("暂示实现"); } loadClickHandle(){ alert("暂示实现"); } redoClickHandle(){ StateManager.redo(); } undoClickHandle(){ StateManager.undo(); } render(){ return <div> <span> <div className="btn-group" data-toggle="buttons"> <label className="btn btn-sm btn-default active" onClick={this.setFirstClickHandle.bind(this,true)}><input type="radio" autoComplete="off" defaultChecked title="黑方先走" />黑先</label> <label className="btn btn-sm btn-default" onClick={this.setFirstClickHandle.bind(this,false)}><input type="radio" autoComplete="off" />白先</label> <label className="btn btn-sm btn-default" onClick={this.setPlaceClickHandle.bind(this,true)}><input type="radio" autoComplete="off" />黑子</label> <label className="btn btn-sm btn-default" onClick={this.setPlaceClickHandle.bind(this,false)}><input type="radio" autoComplete="off" />白子</label> </div> </span> <span> <button className="btn btn-sm btn-default" onClick={this.newClickHandle}>新建</button> <button className="btn btn-sm btn-default" onClick={this.saveClickHandle}>保存</button> <button className="btn btn-sm btn-default" onClick={this.loadClickHandle}>打开</button> </span> <span> <button className="btn btn-sm btn-default" onClick={this.undoClickHandle}>撤销</button> <button className="btn btn-sm btn-default" onClick={this.redoClickHandle}>重做</button> </span> </div>; } } //棋子 class GoPiece extends React.Component{ constructor(props){ super(props); var pieceId = props.pieceId; var pieceState = StateManager.getPieces()[pieceId]; this.state={ showNum:true,//是否显示数字,这个应该是个全局参数 num:pieceState.num,//子上显示的数字,如果为零,表示布局时摆的子,不显示数字 black:pieceState.black,//true表示为黑 last:false,//是否是最后一个子 pieceId:pieceId,//棋子的ID,左上为0,从左到右、从上到下,赋值后不发生变化 visibility:pieceState.visibility,//不可见时,为未放子或者被吃掉,从全局变量中取, } //设置this,很重要 this.handleClick=this.handleClick.bind(this); this.pieceChange=this.pieceChange.bind(this); StateManager.subPieceChange(this.pieceChange); } pieceChange(piecesArray){ //React会判断UI要不要更新,全部更新,不要紧 this.setState({ visibility:piecesArray[this.state.pieceId].visibility, black:piecesArray[this.state.pieceId].black, num:piecesArray[this.state.pieceId].num, showNum:StateManager.current.numShow, last:piecesArray[this.state.pieceId].num==StateManager.current.index,//没有想好如何判断 }); } //这个函数不直接改变自己组件的状态 handleClick(){ StateManager.clickPiece(this.state.pieceId); } render(){ var className="go-piece go-piece-"+(this.state.black==true?'black':'white'); //console.log(this.state); if (this.state.visibility=='hidden') className = className+" go-piece-hidden"; var pieceNum = this.state.num==0?'':this.state.num; return <div className={className} onClick={this.handleClick} id={'piece_'+this.state.pieceId}> <span style={{visibility:this.state.visibility}}>{pieceNum}</span> </div>; } } ReactDOM.render( <GoDesk />, document.getElementById('go-container') );
三、样式文件go.css
html,body{ height:100%; } .go-desk{ background-image:url(../img/go/bk.png); width:100%; height:100%; padding:20px; } .go-opr{ height:30px; text-align:center; margin-bottom:1em; } .go-opr span{ margin:0 0.5em; } .go-opr span button{ margin:0 0.05em; } .go-board{ width:800px; height:800px; margin:0 auto; background-image:url(../img/go/board.png); background-repeat:no-repeat; padding:20px; } .go-piece{ width:40px; height:40px; float:left; background-image:url(../img/go/piece.png); text-align:center; line-height:40px; vertical-align:middle; font-size:20px; } .go-piece span{ } .go-piece-white{ background-position:-40px 0; color:black; } .go-piece-black{ background-position:0 0; color:white; } .go-piece-hidden{ background-image:none; }
四、网页文件go.html
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"/> <title><%= htmlWebpackPlugin.options.title%></title> </head> <body> <div id="go-container"></div> </body> </html>
编译好的文件,请点击附件下载,可以单机基于浏览器运行。所有源码已经托管到码云,访问地址:https://git.oschina.net/wallimn/rwne.git。把插件也传上去了,有点儿大。
发表评论
-
gradle编译错误:Could not find method compile() for arguments
2020-09-19 10:50 18528编译(IDEA+Gradle)一个别人的工程,出现一个 ... -
netty心跳检查之UDP篇
2019-09-15 08:50 2404部分UDP通信场景中,需要客户端定期发送心跳信息,以获取终 ... -
解决tomcat部署两个SpringBoot应用提示InstanceAlreadyExistsException
2019-06-30 11:49 3402两个SpringBoot应用部署在一个Tomcat中,单独 ... -
Eclipse配置MyBatis代码自动化功能
2019-06-29 10:16 17811.安装插件 Eclipse中,Help->Ecli ... -
vue.js中使用qrcode生成二维码
2019-05-20 00:00 7660一、安装包 npm install qrcodejs2 --s ... -
MySQL插入数据报错: Incorrect string value: '\xFD\xDE'
2019-03-31 23:19 1260我MySQL数据库用的uft-8字符集,插入数据一直很正常 ... -
vue自定义组件并双向绑定属性
2019-03-08 22:46 3261做了两个子组件,原理基本一样,一个是使用原生的select ... -
vue-router简单示例
2019-03-05 00:32 1158写个基本完整、稍有借鉴意义的示例,防止自己忘记。 &l ... -
“联通充值系统繁忙”轻松应对
2019-02-06 11:03 3976大过年的,联通充个值一直报“充值系统繁忙”。昨天晚上试了几 ... -
electron.js数据库应用---导航菜单(element-ui+mysql)
2019-02-05 21:33 2366一、环境搭建 略, ... -
electron.js数据库应用---入门(mysql+element-ui)
2019-01-27 23:19 7505我的机器:Windows10,64 ... -
SpringMVC 在controller层中注入成员变量request,是否线程安全
2018-12-17 21:17 2752@RestController public class ... -
VueJS 组件参数名命名与组件属性转化
2018-12-03 00:00 2078转自:https://www.cnblogs.com/meiy ... -
vue-resource拦截器实现token发送及检验自动化
2018-11-16 22:38 3080用了很长时间vue-resource,最近思考$http发 ... -
element-ui试用手记
2018-10-29 20:25 1751element-ui、iviewui都以vue.js为基础 ... -
iviewui中表格控件中render的使用示例
2018-07-07 16:46 9791示例了如何在表格中显示按钮,如何将代码转化为文字。 i ... -
Tomcat错误“Alias name tomcat does not identify a key entry”解决
2018-07-05 21:39 6579申请到了阿里云的证书后,下载、按照说明生成jks格式证书、 ... -
阿里云免费证书“fileauth.txt内容配置错误”解决
2018-07-05 20:43 5306最近研究微信小程序开发,上阿里云申请了个证书,使用文件验证 ... -
springboot2.0跨域配置
2018-07-04 22:11 5288springboot2.0跨域配置: 一、代码 ... -
微信小程序使用code换openid的方法(JAVA、SpringBoot)
2018-07-01 21:52 10401微信小程序序的代码中提示,使用code换取openid,但 ...
相关推荐
在本篇“React学习之围棋记谱本制作(七)总结”中,我们将深入探讨如何利用React技术栈创建一个功能完善的围棋记谱本应用。React是Facebook开发的一个用于构建用户界面的JavaScript库,尤其适用于构建单页应用。在...
在本文中,我们将探讨如何利用React技术来创建一个围棋记谱本应用。React是Facebook推出的一个用于构建用户界面的JavaScript库,尤其适用于构建组件化的、交互式的Web应用。在这个项目的第一部分,我们将专注于环境...
一个react项目用例,基于react框架快速搭建web页面,进行前端开发
react是优秀的前端开发框架,有一系列优秀的组件,react.js是前端开发不可少的重要工具
React是当前前端开发领域中的主流框架之一,由Facebook开发并维护。它以其组件化、虚拟DOM和单向数据流等特点,极大地提升了Web应用的开发效率和性能。本React学习手册将深入探讨React的核心概念和实战技巧,帮助你...
React是Facebook推出的一款用于构建用户界面的JavaScript库,尤其在构建单页应用(SPA)方面表现出色。这个“react学习入门”资料包显然...这个入门学习资源将引导新手一步步踏入React的世界,开启他们的前端开发之旅。
总的来说,React全家桶涵盖了许多前端开发的各个方面,从基础到进阶,从UI设计到状态管理,再到性能优化和测试,都是前端从业者必须掌握的知识点。通过系统学习和实践,可以构建出高效、可维护的React应用。
包含如下四个文件: - react.development.js,react核心库; - react-dom.development.js,支持react操作DOM; - babel.min.js,用于将jsx转为js; - prop-types.js,用户检查组件的 Props。
在深入探讨React技术栈之前,我们首先需要理解什么是React。React是Facebook开发的一款用于构建用户界面的JavaScript库,...不断学习和实践React及其周边生态,可以提升你的前端开发技能,为你的职业生涯打开新的可能。
在本项目中,我们关注的是一个使用React Ant Design框架构建的App管理平台系统的Web前端工程。React Ant Design是一个基于React的UI组件库,它借鉴了阿里巴巴的Ant Design设计规范,为开发者提供了丰富的组件和优雅...
**颠覆式前端UI开发框架:React** React,由Facebook开发并开源的一款JavaScript库,是当前前端开发领域中的明星框架,以其虚拟DOM(Virtual DOM)技术和组件化思想彻底颠覆了传统前端开发模式。它使得构建复杂、高...
React是目前非常流行的JavaScript库,主要用于构建用户界面,尤其适合单页应用(SPA)。这个"react学习课件.rar"文件包含的"react...通过学习这些内容,开发者可以深入了解React并有效地构建高效、可维护的前端应用。
"大四学生前端实习的第四周周记" ...本周记记录了学生在前端实习第四周的经历和体验,涵盖了开源公共组件库项目、React 框架、编写 Select 组件、Jest 前端测试工具、团队开发和 Git 版本控制等多个知识点。
"React学习手册完整版带目录.zip"包含了深入学习React所需的所有资料,包括基本概念、核心原理以及高级技巧。 首先,手册的目录可能会涵盖以下关键知识点: 1. **React基础知识**:介绍React的基本概念,如JSX语法...
本项目是基于React的JavaScript前端设计源码,包含31个文件,其中包括12个JSX文件、6个JavaScript文件、3个JSON配置文件、2个Markdown文档、2个HTML文件、2个CSS样式文件、1个Gitignore文件、1个LICENSE文件、1个ICO...
泛微ecology 9的java、react开发的demo,前端后端这个项目全包括 让大家知道怎么通过react编辑后生成html,后端通过java编辑后生成api 通过怎样逻辑让前后端关联起来的。 参考内容 ...
React前端技术框架教程,简单学习React一些初级入门的方法
前端React 开发React-环境搭建