共计 3510 个字符,预计需要花费 9 分钟才能阅读完成。
前沿:项目采用 create-react-app 脚手架,就是做了一个效果所以只有一个页面但是也用了 react-router-dom 路由把 details 作为 path=’/’ 首页了。
效果图:
图片描述
一、App.js
import React from 'react';
import {BrowserRouter,Route} from 'react-router-dom'
import Details from './routes/Details'
function App() {
return (
<div className="App">
<BrowserRouter>
<Route path='/' component={Details}></Route>
</BrowserRouter>
</div>
);
}
export default App;
这是入口文件的代码内容
二、Details.jsx
import React,{Component} from 'react';
import './Details.css';
import Footer from './Footer';
import SeatSelector from './SeatSelector'
class Details extends Component {
state = {selectSeat:[]
}
addSeat = (seat)=>{
this.setState(prevState=>({selectSeat:[...prevState.selectSeat,seat]
}))
}
removeSeat = (id)=>{
this.setState({selectSeat:this.state.selectSeat.filter(seat=>seat.id !== id)
})
}
render() {const {selectSeat} = this.state;
return (
<div>
<header>
<div className='header-container'>
<h2> 决战时刻 </h2>
<h3> 今天 09-21 15:40~18:00 (国语 2D)</h3>
</div>
</header>
<div className='seat'>
<SeatSelector selected={selectSeat} onAdd={this.addSeat} onRemove={this.removeSeat}/>
</div>
<Footer data={selectSeat} onRemove={this.removeSeat}/>
</div>
);
}
}
export default Details;
这是效果的首页的东西了,分为 3 个部分(头部,中间 canvas 部分,底部),头部基本随便写,底部有一个选座显示几排几座的效果,主要是中间部分
三、SeatSelector.jsx
import React,{Component} from 'react';
import {data} from './mock/data.json';
const SET_WIDTH = 50;
const SET_HEIGHT = 50;
const lastSeat = data[data.length-1];
const canvas_width = lastSeat.x*SET_WIDTH;
const canvas_height = lastSeat.y*SET_HEIGHT;
class Details extends Component {componentDidMount(){this.ctx = this.refs.canvas.getContext('2d');
const emptyImg = new Image();
const selectImg = new Image();
const soldImg = new Image();
let count = 0;
const loadCallback = ()=>{
count++;
if(count===3){
this.emptyImg = emptyImg;
this.selectImg = selectImg;
this.soldImg = soldImg;
this.drawSeat();}
}
emptyImg.onload = loadCallback;
selectImg.onload = loadCallback;
soldImg.onload = loadCallback;
emptyImg.src = './empty.png';
selectImg.src = './select.jpg';
soldImg.src = './sold.jpg';
}
componentDidUpdate(){this.ctx.clearRect(0,0,canvas_width,canvas_height)
this.drawSeat();
this.drawSelectSeat();}
drawSeat(){
const seatData = data;
seatData.forEach((item,index)=>{const {isSold,x,y} = item;
const offsetLeft = (x-1)*SET_WIDTH;
const offsetTop = (y-1)*SET_WIDTH;
if(isSold){this.ctx.drawImage(this.soldImg,offsetLeft,offsetTop,SET_WIDTH,SET_HEIGHT);
}else{this.ctx.drawImage(this.emptyImg,offsetLeft,offsetTop,SET_WIDTH,SET_HEIGHT);
}
})
}
drawSelectSeat = ()=>{const {selected} = this.props;
selected.forEach((item,index)=>{const {isSold,x,y} = item;
const offsetLeft = (x-1)*SET_WIDTH;
const offsetTop = (y-1)*SET_WIDTH;
this.ctx.drawImage(this.selectImg,offsetLeft,offsetTop,SET_WIDTH,SET_HEIGHT);
})
}
clickSeat = (e)=>{let offset = this.refs.canvas.getBoundingClientRect()
let pageX = e.pageX - offset.left;
let pageY = e.pageY - offset.top;
let xPos = Math.ceil(pageX/SET_WIDTH);
let yPos = Math.ceil(pageY/SET_HEIGHT);
let seat = data.find(seat=>seat.x===xPos && seat.y===yPos);
if(seat.isSold){return ;}
const selectIndex = this.props.selected.findIndex(item=>item.id === seat.id);
if(selectIndex>-1){this.props.onRemove(seat.id);
}else{if(this.props.selected.length >= 6){alert("不能超过 6 个");
}else{this.props.onAdd(seat);
}
}
}
render() {
return (
<div>
<canvas onClick={this.clickSeat} ref='canvas' width={canvas_width} height={canvas_height}/>
</div>
);
}
}
export default Details;
这是中间 canvas 比较重要的一块了,大家可以看看代码了
四、Footer.jsx
import React from 'react';
import './footer.css';
const Footer = ({data,onRemove}) => {
return (
<footer>
<div className='seatNum'>
<ul>
{
data.map(seat=>(<li key={seat.id} onClick={()=>onRemove(seat.id)}>
<p>{seat.y}排 {seat.x} 座 </p>
<p>42.7 元 </p>
</li>
))
}
</ul>
</div>
<div className='seatSelect'> 请先选座 </div>
</footer>
)
}
export default Footer;
这是底部代码
正文完