前沿:項目採用create-react-app腳手架,就是作了一個效果因此只有一個頁面可是也用了react-router-dom路由把details做爲path=’/'首頁了。css
效果圖:react
1、App.jsjson
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;
這是入口文件的代碼內容canvas
2、Details.jsxreact-router
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部分,底部),頭部基本隨便寫,底部有一個選座顯示幾排幾座的效果,主要是中間部分app
3、SeatSelector.jsxdom
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比較重要的一塊了,你們能夠看看代碼了this
4、Footer.jsxspa
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;
這是底部代碼code