在react中用canvas做一个电影院选座功能

45次阅读

共计 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;

这是底部代码

正文完
 0