有一個需求是這樣的。html
一個組件裏若干個區塊。區塊數量不定。react
區塊裏面是一個正六邊形組件,而這個用 SVG 和 canvas 均可以。我選擇 canvas。web
因此就變成了在 react 中使用 canvas 的問題。shell
canvas 和 SVG 有一個很大的不一樣。canvas
SVG 是標籤,因此HTML怎麼整,SVG 就怎麼整。數組
而 canvas 是一套相對獨立的 web API,以 canvas 標籤爲容器(HTML接口)。less
因此在 react 中處理 canvas 相似於在 react 中處理第三方DOM庫。好比那些須要依賴 jQuery 的各類UI組件庫。函數
關於這個能夠看 react 文檔中的與第三方庫協同。佈局
組件文件的結構和上一個文章相似。測試
import React from 'react' class Polygon extends React.Component {} class polygonContainer extends React.Component {} export default polygonContainer
而後是 canvas 組件。
class Polygon extends React.Component { constructor(props){ super(props) this.state = { } } componentDidMount() { console.log("=== componentDidMount Polygon ===") this.init(this.props.data, this.props.sn) } componentDidUpdate() { console.log("=== componentDidUpdate Polygon ===") this.init(this.props.data, this.props.sn) } init = (item, sn) => { const getR = () => { return Math.min(size.width, size.height) * 0.375 } const getWordCoor = (index, centerCoor, sub, fontSize, fontLength) => { const getXCoor = (index, centerCoor, fontSize, fontLength) => { const standand = -1 return (centerCoor.x + fontLength / 2 * (index === 0 ? fontSize : (fontSize / 2)) * standand) } const getYCoor = (index, centerCoor, sub) => { const standand = index === 0 ? -0.3 : 0.6 return (centerCoor.y + sub * standand) } console.log(getXCoor(index, centerCoor, fontSize, fontLength)) return { x: getXCoor(index, centerCoor, fontSize, fontLength), y: getYCoor(index, centerCoor, sub) } } const getStrokeColor = (sn) => { return sn === 5 ? 'rgb(255, 114, 0)' : 'rgb(232, 172, 4)' } const getFillColor = (sn) => { return sn === 5 ? 'rgb(255, 192, 0)' : 'rgb(4, 154, 79)' } const canvas = document.getElementById("canvas" + sn); const size = { width: parseInt(this.props.size.width), height: parseInt(this.props.size.height), } canvas.width = size.width; canvas.height = size.height; const cc = canvas.getContext("2d"); // 多邊形 const coorArray = [] cc.beginPath(); for (var i = 0 ; i < 6 ; i++) { var x = Math.cos((i * 60)/180 * Math.PI) * getR() + (size.width / 2) ; var y = -Math.sin((i * 60)/180 * Math.PI) * getR() + (size.height / 2); coorArray.push({x, y}) cc.lineTo(x,y); } cc.closePath(); cc.lineWidth = 2; cc.fillStyle = getFillColor(sn); cc.fill(); cc.strokeStyle = getStrokeColor(sn); cc.stroke(); // 文字 const centerCoor = { x: (coorArray[0].x + coorArray[3].x) / 2, y: coorArray[0].y } const sub = coorArray[0].y - coorArray[1].y const wordCoorArray = [ getWordCoor(0, centerCoor, sub, 14, item.name.length), getWordCoor(1, centerCoor, sub, 20, item.data.toString().length) ] cc.font="14px Arial" cc.strokeStyle = "#fff"; cc.fillStyle = "#fff"; cc.fillText(item.name, wordCoorArray[0].x, wordCoorArray[0].y); cc.font="20px Arial" cc.fillText(item.data, wordCoorArray[1].x, wordCoorArray[1].y); } render(){ const item = this.props.data const size = this.props.size const sn = this.props.sn const getColor = (item) => { return item.color } return ( <canvas id={'canvas' + sn}></canvas> ); } }
有幾點須要說明一下。
而後是容器組件。
// 六邊形測試 import React from 'react' // import Styles from './polygonContainer.less' class Polygon extends React.Component { constructor(props){ super(props) this.state = { } } componentDidMount() { console.log("=== componentDidMount Polygon ===") this.init(this.props.data, this.props.sn) } componentDidUpdate() { console.log("=== componentDidUpdate Polygon ===") this.init(this.props.data, this.props.sn) } init = (item, sn) => { // console.log(item) // console.log(sn) const getR = () => { return Math.min(size.width, size.height) * 0.375 } const getWordCoor = (index, centerCoor, sub, fontSize, fontLength) => { const getXCoor = (index, centerCoor, fontSize, fontLength) => { const standand = -1 return (centerCoor.x + fontLength / 2 * (index === 0 ? fontSize : (fontSize / 2)) * standand) } const getYCoor = (index, centerCoor, sub) => { const standand = index === 0 ? -0.3 : 0.6 return (centerCoor.y + sub * standand) } console.log(getXCoor(index, centerCoor, fontSize, fontLength)) return { x: getXCoor(index, centerCoor, fontSize, fontLength), y: getYCoor(index, centerCoor, sub) } } const getStrokeColor = (sn) => { return sn === 5 ? 'rgb(255, 114, 0)' : 'rgb(232, 172, 4)' } const getFillColor = (sn) => { return sn === 5 ? 'rgb(255, 192, 0)' : 'rgb(4, 154, 79)' } const canvas = document.getElementById("canvas" + sn); const size = { width: parseInt(this.props.size.width), height: parseInt(this.props.size.height), } // console.log(size) canvas.width = size.width; canvas.height = size.height; const cc = canvas.getContext("2d"); // 多邊形 const coorArray = [] cc.beginPath(); for (var i = 0 ; i < 6 ; i++) { var x = Math.cos((i * 60)/180 * Math.PI) * getR() + (size.width / 2) ; var y = -Math.sin((i * 60)/180 * Math.PI) * getR() + (size.height / 2); coorArray.push({x, y}) cc.lineTo(x,y); } cc.closePath(); cc.lineWidth = 2; cc.fillStyle = getFillColor(sn); cc.fill(); cc.strokeStyle = getStrokeColor(sn); cc.stroke(); // 文字 const centerCoor = { x: (coorArray[0].x + coorArray[3].x) / 2, y: coorArray[0].y } const sub = coorArray[0].y - coorArray[1].y // console.log(centerCoor) // console.log(coorArray) const wordCoorArray = [ getWordCoor(0, centerCoor, sub, 14, item.name.length), getWordCoor(1, centerCoor, sub, 20, item.data.toString().length) ] // console.log(wordCoorArray) cc.font="14px Arial" cc.strokeStyle = "#fff"; cc.fillStyle = "#fff"; cc.fillText(item.name, wordCoorArray[0].x, wordCoorArray[0].y); cc.font="20px Arial" cc.fillText(item.data, wordCoorArray[1].x, wordCoorArray[1].y); } render(){ const item = this.props.data const size = this.props.size const sn = this.props.sn // console.log("Polygon render === ", size) const getColor = (item) => { return item.color } return ( <canvas id={'canvas' + sn}></canvas> // <div>asd</div> ); } } class polygonContainer extends React.Component { constructor(props){ super(props) this.state = { curcity:"" } } componentDidMount() { console.log("componentDidMount") console.log(new Date().getTime()) this.setState({ curcity:this.props.curcity }) } componentDidUpdate(){ console.log("componentDidUpdate") console.log(new Date().getTime()) } // total 總數 SN 序列號 getSize = () => { const pc = document.getElementById('pc') if (!pc) { return null } else { // const length = this.getDataBar().data.sData.length const base = { width:document.getElementById('pc').offsetWidth, height:document.getElementById('pc').offsetHeight } return function (total, SN) { // console.log(base) const standand = 2 const oneRowStd = 3 const ceil = Math.ceil(total / standand) const floor = Math.floor(total / standand) const basicHeight = (total > oneRowStd) ? (base.height / standand) : (base.height) // console.log(ceil, floor) // console.log(total, SN) if (SN <= ceil) { return { width:(total > oneRowStd) ? (base.width / ceil) : (base.width / total), height:basicHeight } } else { // console.log(123) // console.log((total > oneRowStd) ? (base.width / floor) : (base.width / total)) return { width:(total > oneRowStd) ? (base.width / floor) : (base.width / total) , height:basicHeight } } } } } theStyle = () => { const baseFlex = { display: 'flex', justifyContent: 'center', alignItems: 'center' } return { main:{ ...baseFlex, width:'100%', height:'100%', color:"#fff" }, tem:{ ...baseFlex, flex:"auto", color:'#fff' }, shellA:{ ...baseFlex, width:'100%', height:'100%' }, shellB:{ ...baseFlex, width:'100%', height:'50%' } } } getDataBar = () => { if (this.props.curcity && this.props.curcity === 'all') { return { data:{ sData:[ { name: 'a', data: 510 }, { name: 'a', data: 46 }, { name: 'a', data: 471 }, { name: 'a', data: 631 }, { name: 'a', data: 924 }, { name: 'a', data: 582 }, ] } } } else { return { data:{ sData:[ { name: 'a', data: 50 }, { name: 'a', data: 469 }, { name: 'a', data: 41 }, { name: 'a', data: 31 }, { name: 'a', data: 4 }, { name: 'a', data: 825 }, ] } } } } getContainer = () => { const size = this.getSize() if (!size) { return "" } const theStyle = this.theStyle() const dataBar = this.getDataBar() const Container = ((dataBar) => { const flexMatrix = [ [0,0], [1,0], [2,0], [3,0], [2,2], [3,2], [3,3], [4,3], [4,4], [5,4], [5,5], [6,5], [6,6] ] const sData = dataBar.data.sData const length = sData.length const matrix = flexMatrix[length] ? flexMatrix[length] : flexMatrix[12] if (matrix[0] === 0) { return "" } let temShell, temA, temB temA = sData.slice(0, matrix[0]).map((item, index) => <div style={theStyle.tem} key={index.toString()}> <Polygon data={item} sn={index + 1} size={size(length, (index + 1))} /> </div> ); if (matrix[1] === 0) { temB = "" } else { temB = sData.slice(matrix[0], (matrix[0] + matrix[1])).map((item, index) => <div style={theStyle.tem} key={index.toString()}> <Polygon data={item} sn={index + 1 + matrix[0]} size={size(length, (index + 1 + matrix[0]))} /> </div> ); } if (matrix[1] === 0) { temShell = <div style={theStyle.shellA} > {temA} </div> } else { temShell = [0,0].map((item, index) => <div style={theStyle.shellB} key={"temShell" + index.toString()}> {index === 0 ? temA : temB} </div> ); document.getElementById('pc').style.flexWrap = "wrap" } return temShell })(dataBar) return Container } render(){ const theStyle = this.theStyle() const curcity = this.state.curcity // const dataBar = this.props.dataBar return ( <div style={theStyle.main} id="pc"> { this.getContainer() } </div> ); } } export default polygonContainer
稍微說明一下。
以上。