我正在做一个React项目,我正在使用react-player来播放视频。我想在视频播放时获取鼠标移动事件,但react-player似乎捕获了这些事件,而不是传播它们。我可以做些什么来获得这些事件?
首先,我尝试向div添加一个处理程序:
<div onMouseMove={() => console.log("mousemove")}>
<ReactPlayer url={props.url} />
</div>然后我尝试使用addEventListener:
document.getElementById("my-div").addEventListener("mousemove", () => console.log("mousemove"), true)
<div id="my-div">
<ReactPlayer url={props.url} />
</div>我希望如果我将useCapture代码设置为true,addEventListener就能正常工作,但它没有帮助。
发布于 2019-08-29 03:13:36
我找不到任何可能的方法来强制组件传播事件,如果它有内部逻辑阻止的话。
您可以尝试创建具有足够z-index属性值的不可见<div>,使其覆盖ReactPlayer组件。在此之后,您可以直接将侦听器附加到它。
通过这种方式,您将能够捕获所需的所有鼠标事件。这不是理想的方式,但至少是可行的。
发布于 2019-08-29 19:14:58
严格地说,没有办法以您想要的方式提供事件传播。
原因是ReactPlayer是在一个单独的<iframe...>元素中呈现的,这意味着您正在处理两个独立的DOM树:一个在主窗口上下文中,另一个在iframe上下文中。然而,DOM事件仅在单个DOM树内传播,并且永远不会在它之外传播。
在您的特定情况下,这意味着在玩家表面上发生的鼠标事件完全由在iframe上下文中执行的代码处理,您的主窗口甚至永远不会知道这些事件。
上面讨论的解决方案是放置一种透明的div,覆盖整个玩家的表面,也不会起作用。在这种情况下,“overlay div”将捕获鼠标事件,并且这些事件将被正确传播,但仅在主窗口DOM树内。然而,在iframe中创建的DOM树永远不会知道这些事件。正如你所说的,你已经意识到了这一点。这种情况与上一段中描述的情况完全相反。
如果您能够完全控制在iframe中运行的代码,那么您将能够使用Window.postMessage()将事件从主窗口调度到iframe,并可能达到预期的结果,但是ReactPlayer对您来说是一个黑匣子,这也不是一个有效的解决方案。
因此,你必须拒绝你的鼠标事件捕获的想法,或者如果你非常需要知道鼠标指针正在玩家表面上移动,你必须发明其他解决方案,而不是基于不同DOM树之间不存在的鼠标事件传播。
我已经起草了一个名为SmartPlayer的小POC组件,它实现了一种可能的解决方案。
其基本思想是播放器被其他名为SmartOverlay的组件所覆盖,该组件旨在捕获鼠标事件。这个SmartOverlay实际上是一个由更小的“瓦片”组成的网格,每个瓦片都有自己的onMouseMove事件处理程序。诀窍是一旦触发了瓦片的onMouseMove事件处理程序,它实际上就从DOM中移除了瓦片,从而在覆盖图中创建了一个“洞”。通过这个“洞”,鼠标事件对玩家的iframe变得“可见”。我知道这听起来很奇怪,但我希望你能从代码中得到整个画面。如果你设置了“显示叠加”选择器,你实际上可以看到“瓷砖”和移动的“洞”。
请注意,当您在“洞”内移动鼠标时,鼠标移动计数器不会改变。您可以通过使tileHeight和tileWidth更小来减小这种“粒度”,但代价是性能降低。
严格地说,这是一种黑客行为,在将其用于生产之前,我会三思而后行。不过,如果你真的需要在ReactPlayer上捕捉鼠标事件,这可能是你能得到的最简单的解决方案。在这个解决方案中有一些性能开销,但我试图将其保持在可接受的较低水平。
祝你好运:)
附注:我在让这段代码作为代码片段运行时遇到了麻烦。希望能以某种方式修复它。同时,我在这个答案中直接包含了整个代码。
要测试该解决方案,您可以使用create-react-app创建一个React应用程序,然后用下面的代码完全替换App.js内容。
我还将运行版本放在这里:http://react-player.2358.com.ua/
import React, { Component } from 'react'
import ReactPlayer from 'react-player'
class SmartOverlay extends Component {
constructor(props) {
super(props);
const {height, width, } = props;
const tileHeight = props.tileHeight || 64;
const tileWidth = props.tileWidth || 64;
// below we're creating an array of "tiles"
// these "tiles" together are intended to cover all the players sufrace
this.overlayTiles = [];
for (let top = 0; top < height; top += tileHeight) {
for (let left = 0; left < width; left += tileWidth) {
const elementHeight = Math.min(tileHeight, height - top);
const elementWidth = Math.min(tileWidth, width - left);
const tile = {top, left, elementHeight, elementWidth }
// for each tile its own 'mousmove' event handler is created and assigned as the tile's property
tile.onMouseMove = () => this.onMouseMove(tile);
this.overlayTiles.push(tile);
}
}
// all the overlay tiles are "active" at the beginning
this.state = {activeOverlayTiles: this.overlayTiles}
}
onMouseMove(currentTile) {
// call event handler that is passed to the SmartOvelay via its handleMouseMove property
// using setTimeout here isn't strictly necessary but I prefer that "external" handler was executed only after we exit current method
setTimeout(this.props.handleMouseMove);
// "remove" current tile from the activeOverlayTiles array (that will cause removing the tile's DIV element from DOM eventually)
// actually we are not removing the item from the array but creating a new array that contains all the tiles but the current one
this.setState({activeOverlayTiles: this.overlayTiles.filter(item => item !== currentTile)})
// after current tile is removed from DOM the "hole" is left on the overlay "surface"
// and the player's iframe can "see" the mouse events that are happening within the "hole"
}
render() {
const showOverlayTileStyle = this.props.showOverlay ? {opacity: 0.5, background: '#fff', border: "1px solid #555", } : {}
return (
this.state.activeOverlayTiles.map(item => (
<div onMouseMove = {item.onMouseMove} style = {{...showOverlayTileStyle, position: 'absolute', top: item.top, left: item.left, height: item.elementHeight, width: item.elementWidth,}}></div>
))
);
}
}
const PlayerWithoutOverlay = ({height, width, url}) => (
<div style = {{position: 'absolute'}}>
<ReactPlayer height = {height} width = {width} url = {url} />
</div>
)
const SmartPlayer = ({height, width, url, showOverlay, handleMouseMove}) => (
<>
<PlayerWithoutOverlay height = {height} width = {width} url = {url} />
<SmartOverlay height = {height} width = {width} showOverlay = {showOverlay} handleMouseMove = {handleMouseMove} />
</>
)
class App extends Component {
constructor(props) {
super(props);
this.state = {
showOverlay: false,
mouseMoves: 0
}
}
toggleShowOverlay(e) {
// this simply shows/hide the ovelay depending on checkbox state
this.setState(state => ({showOverlay: !state.showOverlay}))
}
handleMouseMove(){
// adds 1 to state.mouseMoves counter
this.setState(state => ({mouseMoves: state.mouseMoves + 1}));
}
render() {
const height = 420;
const width = 640;
return (
<div style = {{margin: 12, position: 'relative'}}>
<div style = {{height: height }}>
<SmartPlayer
height = {height}
width = {width}
showOverlay = {this.state.showOverlay}
url = "https://www.youtube.com/watch?v=A0Z7fQyTb4M"
handleMouseMove = {this.handleMouseMove.bind(this)} />
</div>
<div style = {{marginTop: 12}}>
<label>Moves detected: </label>
<span>{`${this.state.mouseMoves}`}</span>
<label onClick = {this.toggleShowOverlay.bind(this)}> <input type = "checkbox" value="1" checked = {this.state.showOverlay} />Show overlay</label>
</div>
</div>
)
}
}
export default App;https://stackoverflow.com/questions/57698463
复制相似问题