简体   繁体   English

使用React时如何使用onClick更改x3dom形状?

[英]How to change a x3dom shape with onClick, when using React?

I am trying to use React together with X3DOM. 我正在尝试将React与X3DOM一起使用。 I want to be able to click on the red x3dom <box> so that it changes its color to blue, when pressed. 我希望能够单击红色的x3dom <box>以便在按下时将其颜色更改为蓝色。 I have tried using a onClick method in the <shape> tag. 我尝试在<shape>标记中使用onClick方法。 I was only able to do this by pressing a html <button> instead. 我只能通过按下html <button>来做到这一点。 I have the button also in the code. 我在代码中也有该按钮。

This is my index.js file. 这是我的index.js文件。

import React from "react";
import ReactDOM from "react-dom";

class Toggle extends React.Component {
    constructor(props) {
        super(props);
        this.state = { isToggleOn: true };

        this.handleClick = this.handleClick.bind(this);
    }

    handleClick() {
        this.setState(prevState => ({
            isToggleOn: !prevState.isToggleOn
        }));
    }

    render() {
        return (
        <div>
            <x3d width='500px' height='300px' >
                <scene>
                    <shape onClick={this.handleClick}>
                        <appearance>
                            <material id='color' diffusecolor={this.state.isToggleOn ? '1 0 0' : '0 0 1'}> </material>
                        </appearance>
                        <box></box>
                    </shape>
                </scene>
            </x3d>

            <button onClick={this.handleClick}>
                {this.state.isToggleOn ? 'BLUE' : 'RED'}
            </button>

        </div>
        );
    }

}

ReactDOM.render(<Toggle />, document.getElementById('toggle'));

Can someone give me a solution, or an explanation why this doesn't work. 有人可以给我一个解决方案,还是一个解释为什么不起作用。 I would greatly appreciate any answer. 我将不胜感激任何答案。

As @Johannes already pointed out, you need to add your event listeners after X3DOM has been initialized. 正如@Johannes已经指出的那样,您需要在X3DOM初始化之后添加事件侦听器。 If you look closely at https://doc.x3dom.org/tutorials/animationInteraction/picking/index.html you will find that they explicitly state: 如果仔细查看https://doc.x3dom.org/tutorials/animationInteraction/picking/index.html,您会发现它们明确声明:

Caveat : The call of the onclick function is handled by x3dom by directly calling the callback function, since the addEventListener method needed to be overwritten. 警告 :x3dom通过直接调用回调函数来处理onclick函数的调用,因为需要覆盖addEventListener方法。 No page-wide onclick events are thrown, so attaching a listener to this object is only possible after . 不会引发整个页面范围的onclick事件,因此仅在之后可以将侦听器附加到此对象。 In this case, do not use $("#myBox").on("click", doSomething(event)) but $("#myBox").attr("onclick", "doSomething(event)") instead. 在这种情况下,请勿使用$("#myBox").on("click", doSomething(event))而应使用$("#myBox").attr("onclick", "doSomething(event)") Alternatively, wait until the page is fully loaded and the document.onload event was fired. 或者,等待页面完全加载完毕并触发document.onload事件。

A working example might look like this: 一个有效的示例可能如下所示:

var glob = {};
class Toggle {
    ...    
    componentDidMount() {
      glob.handleClick = this.handleClick;
      var k = document.getElementsByTagName('shape')[0];
      k.setAttribute('onclick', 'glob.handleClick()');
    }

Please note that I had to introduce a global object in order to get it working, which is probably just a lack of my knowledge of React. 请注意,我必须引入一个全局对象才能使其工作,这可能只是我对React的了解不足。 You may find a better way. 您可能会找到更好的方法。

You can also see it at: https://codepen.io/pgab/pen/gBpEWo 您也可以在以下位置查看它: https : //codepen.io/pgab/pen/gBpEWo

The problem is that X3DOM calls eventHandlers directly. 问题是X3DOM直接调用eventHandlers。 Quote from the docu : 引用DOCU

The call of the onclick function is handled by x3dom by directly calling the callback function, since the addEventListener method needed to be overwritten. 由于需要覆盖addEventListener方法,因此x3dom通过直接调用回调函数来处理onclick函数。 No page-wide onclick events are thrown, so attaching a listener to this object is only possible after . 不会引发整个页面范围的onclick事件,因此仅在之后可以将侦听器附加到此对象。

I have not tested this but adding the onclick once in componentDidMount might be the way to go. 我还没有测试过,但是在componentDidMount添加一次onclick可能是一种方法。

Not sure if this is the best solution, but I was able to make it work by using componentDidUpdate() . 不知道这是否是最好的解决方案,但是我能够通过使用componentDidUpdate()使它起作用。 I added an eventListner by giving <shape> some id. 我通过给<shape>一些ID添加了eventListner。 I also had to remove the eventListner to avoid a infinity loop. 我还必须删除eventListner以避免无限循环。 The problem with using componentDidUpdate() is that the component has to update somehow, before the method can be used. 使用componentDidUpdate()的问题在于,必须先以某种方式更新组件,然后才能使用该方法。 I did this by adding an extra state 'start' which i update from false to true after the first second when the application runs. 为此,我添加了一个额外的状态“开始”,在应用程序运行的第一秒后,状态从“ false”更新为“ true”。

import React from "react";
import ReactDOM from "react-dom";

class Toggle extends React.Component {
 constructor() {
    super();
    this.state = { isToggleOn: true, start: false};

    this.handleClick = this.handleClick.bind(this); 
 }

 handleClick() {
    this.setState(prevState => ({
        isToggleOn: !prevState.isToggleOn
    }));
    console.log("Pressed");
 }

 componentDidUpdate() {
    console.log('Component update');

    //Have to do this to avoid potential infinty loop
    document.getElementById('someId').removeEventListener("click", this.handleClick);

    document.getElementById('someId').addEventListener("click", this.handleClick);
 }

 render() {

    //Need to update component once, to run componentDidUpdate
    if(!(this.state.start)){
        setTimeout(() => {
            this.setState({start: true });
        }, 1000)
    }
    //

    return (
        <div>
        <x3d width='500px' height='300px'>
            <scene>
                <shape id="someId">
                    <appearance>
                        <material id='color' diffusecolor={this.state.isToggleOn ? '1 0 0' : '0 0 1'}> </material>
                    </appearance>
                    <box></box>
                </shape>
            </scene>
        </x3d>
    </div>
    );  
  }
 }

ReactDOM.render(<Toggle />, document.getElementById('toggle'));

I had the same issue for weeks, Thanks to the answers to your question, and to this non-related post : Why does an onclick property set with setAttribute fail to work in IE? 数周以来,我都遇到了同样的问题,这要归功于您的问题的解答以及与本无关的帖子: 为什么使用setAttribute设置的onclick属性在IE中无法正常工作? , i was able to make it work with onClick and mouseover events. ,我能够使其与onClickmouseover事件一起使用。

As said in one of the above answer, X3Dom comes with its own set of functions attached to the onClick event. 如上述答案之一所述,X3Dom带有与onClick事件关联的自己的一组函数。 And as mentioned in the X3DOM documentation, you can only attach your event after the document has loaded. 并且,如X3DOM文档中所述,您只能在文档加载后附加事件。 Also @mistapink idea was correct except the setAttribute method does not work for some reasons. @mistapink的想法也是正确的,除了setAttribute方法由于某些原因不起作用。 So I did that : 所以我做到了:

const Scene = () => {
   const [toggle, setToggle] = useState(false);
   const handleMouseOver = () => {/* do some mouse over stuff here */};
const handleClick = () => setToggle(prevToggle => !prevToggle};
   useEffect(() => {
      const 3dObject = document.getElementById('my-3d-object'); 
/* I guess you can also try to use a reference, don't know if it'll work */
      document.onload = () => {
         3dObject.addEventListener("mouseover", handleMouseOver);
/* magic happens here  */
         3dObject.onclick = () => handleClick(
/* you can pass some extra arguments here */
);
      }
      return () => sphere.removeEvenetListener("mouseover", handleMouseOver);
    });

  return (...)
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM