react实战 : react 与 canvas
有一个需求是这样的。
一个组件里若干个区块。区块数量不定。
区块里面是一个正六边形组件,而这个用 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的更多相关文章
- react实战 : react 与 svg
有一个需求是这样的. 一个组件里若干个区块.区块数量不定. 区块里面是一个波浪效果组件,而这个一般用 SVG 做. 所以就变成了在 react 中使用 SVG 的问题. 首先是波浪效果需要的样式. . ...
- 【视频合集】极客时间 react实战进阶45讲 【更新中】
https://up2.v.sharedaka.com/video/ochvq0AVfpa71A24bmugS5EewhFM1553702519936.mp4 01 React出现的历史背景及特性介绍 ...
- 《React Native 精解与实战》书籍连载「React 与 React Native 简介」
此文是我的出版书籍<React Native 精解与实战>连载分享,此书由机械工业出版社出版,书中详解了 React Native 框架底层原理.React Native 组件布局.组件与 ...
- RN 实战 & React Native 实战
RN 实战 & React Native 实战 https://abc.xgqfrms.xyz/react-native-docs/ 0.59 https://github.com/xgqfr ...
- easy canvas shape with react antdesign 简单的canvas图形in antd & react
//show: //code: import React from "react" import {findDOMNode} from 'react-dom' import { B ...
- React实战之将数据库返回的时间转换为几分钟前、几小时前、几天前的形式。
React实战之将数据库返回的时间转换为几分钟前.几小时前.几天前的形式. 不知道大家的时间格式是什么样子的,我先展示下我这里数据库返回的时间格式 ‘2019-05-05T15:52:19Z’ 是这个 ...
- React实战之Ant Design—Upload上传_附件上传
React实战之Ant Design—Upload上传_附件上传 Upload组件大家都在官方文档中看过了,但写的时候还是会遇到许多问题,一些新手看了文档后感觉无从下手,本文过多的简绍就不说了,直接看 ...
- React实战之60s倒计时按钮(发送短信验证按钮)
React实战之60s倒计时按钮——短信验证按钮 导入:(antd组件——Form表单) import { Button, Form, Input } from 'antd'; const FormI ...
- 从零开始react实战:云书签-1 react环境搭建
总览篇:react 实战之云书签 本篇是实战系列的第一篇,主要是搭建 react 开发环境,在create-react-app的基础上加上如下功能: antd 组件库按需引入 ,支持主题定制 支持 l ...
随机推荐
- 人声提取工具Spleeter安装教程(linux)
在安装之前,要确保运行Spleeter的计算机系统是64位,Spleeter不支持32位的系统.如何查看? 因为在linux环境下安装spleeter相对要简单很多,这篇教程先以Ubuntu20.04 ...
- LevelDB/Rocksdb 特性分析
LevelDb是Google开源的嵌入式持久化KV 单机存储引擎.采用LSM(Log Structured Merge)tree的形式组织持久化存储的文件sstable.LSM会造成写放大.读放大的问 ...
- 004.OpenShift命令及故障排查
一 CLI访问OpenShift资源 1.1 资源操作 OCP将OpenShift集群中的为由主节点管理的对象统称为资源,如:node.service.pod.project.deployment.u ...
- Java面试必备Springioc上
配置文件中 Proprety name值必须和 类中的成员变量private IUsedao userDao一一对应 工程项目的代码为:
- ceph SSD HDD分离与openstack调用
本例子ceph L版本采用的是filestore,而不是bluestore. 一.查看class类型,只有一个hdd,.Luminous 为每个OSD添加了一个新的属性:设备类.默认情况下,OSD将根 ...
- Docker搭建Rancher平台
sudo docker run -d --restart=unless-stopped -p 8080:8080 rancher/server service docker resatrt启动失败 ...
- html+css快速入门教程(3)
练习: 1.画盒子 2.相框 5 基础选择器 5.1 id选择器 ID选择器与类选择器的定义与引用方式类似,只是定义的符号不一样.ID通常表示唯一值,因此,ID选择器在CSS 中通常只出现一次.如果出 ...
- MySQL 字符串索引优化方案
字符串建立索引的优化 1. 建立前缀索引 假设建立一个支持邮箱登录的用户表,对于邮件字段来说,可以有以下几种建立索引的方式: 直接对整个字符串建立索引 alter table SUser add in ...
- C# wpf 实现 MD5加密解密 小工具
源文件: http://pan.baidu.com/share/link?shareid=2038099474&uk=3912660076 MD5 C# 实现代码来源于网络,感谢原系作者! 参 ...
- postcss.config.js not found
https://github.com/ElemeFE/element/issues/10249