有一个需求是这样的。

一个组件里若干个区块。区块数量不定。

区块里面是一个正六边形组件,而这个用 SVG 和 canvas 都可以。我选择 canvas。

所以就变成了在 react 中使用 canvas 的问题。

canvas 和 SVG 有一个很大的不同。

SVG 是标签,所以HTML怎么整,SVG 就怎么整。

而 canvas 是一套相对独立的 web API,以 canvas 标签为容器(HTML接口)。

所以在 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>
);
}
}

有几点需要说明一下。

  • 因为 componentDidUpdate 钩子中 有 init 方法,所以 init 方法中不能再给 state 赋值,否则会触发无限循环。如果需要存值,则需要想别的办法。
  • getWordCoor 是计算文字位置的方法。六边形里面有文字内容。
  • canvas 对象是通过 document.getElementById 获取的,而一个页面中肯定有多个 canvas ,此时就必须做出区分。我的方法是传一个序列号 sn (index + 1),当然生成 ID 是更好的做法。
  • 响应式的样式对 canvas 是无效的。必须手动赋像素值。也就是说必须手动计算 size 。计算 size 的方法在父组件里面。
  • for 循环是用来绘制路径的,就是个数学问题,Math 对象里有三角函数简化了一些运算。顺便把中心点坐标和六边形各个点的坐标存了一下。
  • 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

稍微说明一下。

  • getSize 是计算区块大小的方法。这个方法返回一个 size 方法,在 getContainer 方法中输出 JSX 的时候会调用 size 方法得到宽高。
  • 关于布局的问题(为什么写了个双层数组?)之前的文章里写过,不再赘述。
  • 关于数据绑定的机制。通过 props 来绑定。

以上。

react实战 : react 与 canvas的更多相关文章

  1. react实战 : react 与 svg

    有一个需求是这样的. 一个组件里若干个区块.区块数量不定. 区块里面是一个波浪效果组件,而这个一般用 SVG 做. 所以就变成了在 react 中使用 SVG 的问题. 首先是波浪效果需要的样式. . ...

  2. 【视频合集】极客时间 react实战进阶45讲 【更新中】

    https://up2.v.sharedaka.com/video/ochvq0AVfpa71A24bmugS5EewhFM1553702519936.mp4 01 React出现的历史背景及特性介绍 ...

  3. 《React Native 精解与实战》书籍连载「React 与 React Native 简介」

    此文是我的出版书籍<React Native 精解与实战>连载分享,此书由机械工业出版社出版,书中详解了 React Native 框架底层原理.React Native 组件布局.组件与 ...

  4. RN 实战 & React Native 实战

    RN 实战 & React Native 实战 https://abc.xgqfrms.xyz/react-native-docs/ 0.59 https://github.com/xgqfr ...

  5. easy canvas shape with react antdesign 简单的canvas图形in antd & react

    //show: //code: import React from "react" import {findDOMNode} from 'react-dom' import { B ...

  6. React实战之将数据库返回的时间转换为几分钟前、几小时前、几天前的形式。

    React实战之将数据库返回的时间转换为几分钟前.几小时前.几天前的形式. 不知道大家的时间格式是什么样子的,我先展示下我这里数据库返回的时间格式 ‘2019-05-05T15:52:19Z’ 是这个 ...

  7. React实战之Ant Design—Upload上传_附件上传

    React实战之Ant Design—Upload上传_附件上传 Upload组件大家都在官方文档中看过了,但写的时候还是会遇到许多问题,一些新手看了文档后感觉无从下手,本文过多的简绍就不说了,直接看 ...

  8. React实战之60s倒计时按钮(发送短信验证按钮)

    React实战之60s倒计时按钮——短信验证按钮 导入:(antd组件——Form表单) import { Button, Form, Input } from 'antd'; const FormI ...

  9. 从零开始react实战:云书签-1 react环境搭建

    总览篇:react 实战之云书签 本篇是实战系列的第一篇,主要是搭建 react 开发环境,在create-react-app的基础上加上如下功能: antd 组件库按需引入 ,支持主题定制 支持 l ...

随机推荐

  1. 第十章:RDB持久化

    RDB 保存命令 save 命令,阻塞 Redis 服务器进程,直到保存动作完成: bgsave 命令,派生出一个子进程来完成保存动作: 载入命令 Redis 没有载入 RDB 文件的命令,载入动作在 ...

  2. Redis:rdb和aof

    由于redis的数据都直接存储在内存里,在服务器发生宕机时内存的数据会瞬间清空,那么必须要有重启时恢复数据的方法. redis通过持久化机制将数据存储到磁盘中从而在服务器重启时恢复数据,这篇文章主要简 ...

  3. DNS信息收集-NSLOOKUP

    上一篇文章简单介绍了nslookup的简单用法,这篇文章进一步介绍nslookup. nslookup可以指定DNS服务器,使用server参数: 可以看到,采用不同的DNS服务器的到的结果是不一样的 ...

  4. Python学习笔记——基础语法篇

    一.Python初识(IDE环境及基本语法,Spyder快捷方式) Python是一种解释型.面向对象.动态数据类型的高级程序设计语言,没有编译过程,可移植,可嵌入,可扩展. IDE 1.检查Pyth ...

  5. Linux系统如何使用Fuser命令

    本文不再更新,可能存在内容过时的情况,实时更新请访问原地址:Linux系统如何使用Fuser命令: 什么是Fuser命令? fuser命令是一个非常聪明的unix实用程序,用于查找正在使用某个文件.目 ...

  6. CImage显示位图与CDC双缓冲冲突,使用路径层解决.

    2010年04月29日 星期四 20:35 位图闪的问题困扰我很久了,因为程序的需要,我显示位图的方式是CImage类. 如果从CImage转到CBitmap,之后使用Attach到是可以,但我发现这 ...

  7. Format中的转换说明符

    %a(%A) 浮点数.十六进制数字和p-(P-)记数法(C99)%c 单个字符%d 有符号十进制整数%f 浮点数(包括float和doulbe)%e(%E) 指数形式的浮点数[e-(E-)记数法]%g ...

  8. Centos 下 Jenkins2.6 + Git + Maven Shell一件部署与备份

    使用Jenkins2.6 集成Maven与Git插件做持续集成,同时编写Shell脚本备份与发布(需要稍微知道点Linux/毕竟基于Centos PS:本人Linux也是菜鸡) - 下载Jenkins ...

  9. 2020年全新web前端学习路线图,学完就业20K!

    第一阶段:HTML5+css 配套学习视频: 前端小白零基础入门HTML5+CSS3 第二阶段:移动web网页开发 移动web进阶教程 第三阶段:JavaScript网页编程 前端与移动开发基础入门到 ...

  10. C++ MFC 文件操作(新建,删除,剪切,复制,读数据,写数据,重命名)

    源文件:http://pan.baidu.com/s/1ve0hV 这是运行mfc缺失的dll动态链接库:http://pan.baidu.com/s/17pGlT 哈哈,我也是初接触C++,基础的什 ...