我是React的新手,我正在构建一个从MediaStream获取屏幕抓取的应用程序。基于我所看到的,最好的方法是使用context.drawImage()
方法将它绘制到canvas元素上,并将HTMLVideoElement作为参数传入。这是我的动作创建者的样子:
const RecordImage = function(video, canvas, encoder) {
if(!encoder) {
encoder = new GifReadWrite.Encoder()
encoder.setRepeat(0)
encoder.setDelay(100)
encoder.start()
}
const context = canvas.getContext('2d')
context.drawImage(video, 0, 0)
encoder.addFrame(context)
return {
type: RECORD_IMAGE,
payload: encoder
}
}
这在过去是有效的,因为RecordImage动作是从包含<video />
和<canvas />
元素的同一组件调用的,我可以像这样传递它们:
takePic(event) {
event.preventDefault()
this.props.RecordImage(this.video, this.canvas, this.props.encoder)
}
...
render() {
return (
<div>
<video
ref = { video => this.video = video }
width = { this.props.constraints.video.width }
height = { this.props.constraints.video.height }
autoPlay = "true"
/>
<canvas
ref = { canvas => this.canvas = canvas }
width = { this.props.constraints.video.width }
height = { this.props.constraints.video.height }
/>
<button onClick = { this.takePic }>Take Picture</button>
</div>
)
}
但是,我想在另一个组件中放置“拍照”按钮。这是一个问题,因为现在我不知道如何从兄弟组件访问<video />
和<canvas />
元素。通常我会将我需要的参数存储为状态的一部分,但drawImage()
方法需要使用HTML元素本身。我听说将DOM元素存储在状态中是一个坏主意,那么最好的方法是什么呢?
答案 0 :(得分:0)
在React中,可以直接调用其中一个组件上的函数 你可以在你的视频组件中添加一个'getPicture'函数来返回视频元素吗?
您的视频组件:
getPicture() {
return this.video
}
...
render() {
return (
<div>
<video
ref = { video => this.video = video }
width = { this.props.constraints.video.width }
height = { this.props.constraints.video.height }
autoPlay = "true"
/>
</div>
)
}
图片按钮组件可能如下所示:
takePic() {
const element = this.props.getPic
const encoder = new GifReadWrite.Encoder()
encoder.setRepeat(0)
encoder.setDelay(100)
encoder.start()
const canvas = document.createElement("canvas")
canvas.width = element.offsetWidth
canvas.height = element.offsetHeight
canvas.drawImage(video, 0, 0)
encoder.addFrame(context)
return {
type: RECORD_IMAGE,
payload: encoder
}
}
...
render() {
return (
<button onClick = { () => this.takePic() }>Take Picture</button>
)
}
然后是一个父组件来绑定按钮和视频组件:
getPicture() {
return this.video.getPicture()
}
...
render() {
return (
<div>
<VideoElement ref="video => this.video = video"/>
<TakePictureButton getPic="() => this.getPicture()" />
</div>
)
}
免责声明:我不确定绘制功能是如何工作的,但是你明白了
答案 1 :(得分:0)
非常感谢@ stefan-van-de-vooren让我走上了正确的道路!使用Redux connect的子组件使我的情况变得复杂,因此让它们工作需要一些额外的设置。
首先,设置父组件以从另一个子组件中调用一个子组件:
setMedia(pic) {
return this.control.getWrappedInstance().setMedia(pic)
}
getPicture() {
return this.display.getWrappedInstance().getPicture()
}
componentDidMount() {
this.setMedia( this.getPicture() )
}
render() {
return (
<div>
<DisplayContainer ref= { display => this.display = display } />
<ControlContainer ref= { control => this.control = control } />
</div>
)
}
因为Redux connect
返回更高阶的组件,我们需要使用getWrappedInstance()
来访问子函数。接下来,我们需要通过告诉getWrappedInstance()
使用refs来在我们的子组件中启用connect
。此外,我们将设置我们的子功能。
DisplayContainer:
getPicture() {
return this.video
}
...
render() {
return ( <video ref= { video => this.video = video } /> )
}
export default connect(mapStateToProps, null, null, { withRef: true })(Display)
ControlContainer:
setMedia(pic) {
this.setState({ media: pic })
}
takePic(event) {
event.preventDefault()
this.props.RecordImage(this.state.media, this.canvas, this.props.encoder)
}
...
render() {
return(
<button onClick= { this.takePic }>Take Picture</button>
<canvas ref= { canvas => this.canvas = canvas } />
)
}
export default connect(mapStateToProps, mapDispatchToProps, null, { withRef: true })(Control)
动作创建者保持不变:
const RecordImage = function(video, canvas, encoder) {
if(!encoder) {
encoder = new GifReadWrite.Encoder()
encoder.setRepeat(0)
encoder.setDelay(100)
encoder.start()
}
const context = canvas.getContext('2d')
context.drawImage(video, 0, 0)
encoder.addFrame(context)
return {
type: RECORD_IMAGE,
payload: encoder
}
}
我应该注意,我们在这里使用了很多参考资料。在这种情况下,有必要因为context.drawImage()
需要实际的<video />
元素才能工作。然而,refs有时被认为是反模式,如果有更好的方法,值得考虑。阅读本文以获取更多信息:https://reactjs.org/docs/refs-and-the-dom.html#dont-overuse-refs
更好的解决方案是直接从MediaStream中获取图像。有一种实验grabFrame()
方法可以做到这一点,但截至2018年2月,浏览器支持有限。 https://prod.mdn.moz.works/en-US/docs/Web/API/ImageCapture/grabFrame