如何在React中访问画布上下文

21

我使用React和Canvas制作了一个颜色选择器。目前,组件在React中呈现,而canvas则是使用原始JavaScript完成的。我希望它们更加协调,因此我想用React处理单击事件。

例如,这个

colorStrip.addEventListener("click", click, false);

function click(e) {
  x = e.offsetX;
  y = e.offsetY;
  var imageData = context.getImageData(x, y, 1, 1).data;
  rgbaColor = 'rgba(' + imageData[0] + ',' + imageData[1] + ',' + imageData[2] + ',1)';
  fillGradient();
}

我希望你能翻译成这个。

var ColorPicker = React.createClass({
  colorStripClick: function() {
    //handle click events here
  },
  render: function() {
    var styles = {
      opacity: this.props.isVisible ? '1' : '0'
    };
    return(
      <div id="color-picker" style={styles}>
        <canvas id="color-block" height="150" width="150"></canvas>
        <canvas id="color-strip" height="150" width="30" onClick={this.colorStripClick}></canvas>
      </div>
    );
  }
});

但这不起作用,因为我不知道如何访问context。我该如何在React中获取画布属性?是否有一种方法可以在点击之前访问它?

更新

我使用了David的答案,但是将函数放在ref中会出现错误,所以我改成了ref="canvasBlock"ref="canvasStrip",然后在componentDidMount中分配了context


我做了一个类似的项目 https://github.com/gibbok/react-color-picker-palette ,你可以查看这个例子中如何访问上下文,以获取函数getDrawingContex()的示例:https://github.com/gibbok/react-color-picker-palette/blob/master/ColorPickerPalette.jsx - GibboK
在 David 的函数中,你必须添加 "if (c == null)"。我猜这是因为你只在挂载时获取元素引用,而不是在重新渲染时获取。 - Aus
6个回答

34

根据React16,您可以使用React.createRef()

class ColorPicker extends React.Component {
constructor(props) {
   super(props);

   this.colorPickerRef = React.createRef();
}

componentDidMount() {
   this.context = this.colorPickerRef.current.getContext('2d');
}

render() {
   return (
      <canvas ref={this.colorPickerRef} />
   )
}
}

在我看来,这个答案的评分还不够高。谢谢 =] - mfisher91

20
您可以在canvas元素上添加一个ref函数属性:
<canvas id="color-strip" ref={(c) => this.context = c.getContext('2d')} height="...

然后您将通过this.context访问上下文:

colorStripClick: function() {
    var imageData = this.context.getImageData(x, y, 1, 1).data
}

你还可以使用事件对象来访问DOM节点,正如已经指出的那样,但这种方式让你可以从任何地方访问,而不仅仅是事件处理程序。


你也可以使用事件对象来访问DOM节点,但这种方式使您可以从任何地方访问,而不仅仅是事件处理程序。

1
我建议使用不同的名称,因为上下文也涉及到React。 - Alvaro

18

这里是使用React Hooks的答案:

import { useEffect, useRef } from 'react'

export function Canvas() {
  const ref = useRef()

  useEffect(() => {
    if (ref.current) {
      const canvas = ref.current.getContext('2d')
      // do something here with the canvas
    }
  }, [])

  return <canvas ref={ref} />
}

TypeScript

如果您正在使用 TypeScript:

import { useEffect, useRef } from 'react'

export function Canvas() {
  const ref = useRef<HTMLCanvasElement>(null)

  useEffect(() => {
    if (ref.current) {
      const canvas = ref.current.getContext('2d')
      // do something here with the canvas
    }
  }, [])

  return <canvas ref={ref} />
}

在挂载时

如果你只想在组件“挂载”时对画布实例进行一次操作,最好使用useCallback。这样,在React严格模式下,你的效果不会运行两次。

import { useEffect, useCallback } from 'react'

export function Canvas() {
  const refCallback = useCallback((canvas: HTMLCanvasElement) => {
    // do something here with the canvas
  }, [])
  return <canvas ref={refCallback} />
}

1
如果您正在使用Typescript,请使用以下代码:const ref: RefObject<HTMLCanvasElement> = useRef(null); - LAYTOR
这是一个有用的补充。我更新了我的示例,使用了泛型。 - Fernando Rojo
我尝试了您使用useCallback的“On Mount”解决方案,但在NextJS上热重载无法正常工作,可能是因为它没有被正确清除。它只会再次被调用。 - Ben Butterworth
Hooks 在快速刷新时会再次被调用:https://nextjs.org/docs/basic-features/fast-refresh#fast-refresh-and-hooks - Fernando Rojo

1
这里是在React组件中移除canvas的方法: const canvas = ReactDOM.findDOMNode(this.refs.canvas); const context = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); 只要你能以React的方式定位到DOM节点,你就能够访问Canvas渲染API。

1
它应该只是访问点击的目标。
colorStripClick: function(e) {
  var ctx = e.target.getContext('2d')
}

1
像这样
colorStripClick: function (e) {
    var context = e.currentTarget.getContext('2d');
    // your code
}

示例


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接