起初完全是按照官方示例中的代码进行编写的

在Graph后面进行链式操作,功能看上去没问题,但鼠标在视图中一滑动就连续报错ଲଇଉକ:

Uncaught TypeError: 'get' on proxy: property 'modelViewMatrix' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value 

    // 创建图实例 传入 DOM 元素
    const Graph = ForceGraph3D()(container);
    // 配置图数据和节点
    Graph.graphData(this.graphData)
      .nodeAutoColorBy("name")
      .linkWidth(2)
      .linkDirectionalArrowLength(4)
      .linkDirectionalArrowRelPos(1)
      .nodeThreeObject((node) => {
        const sprite = new SpriteText(node.id);
        sprite.material.depthWrite = false; // make sprite background transparent
        sprite.color = node.color;
        sprite.textHeight = 10;
        sprite.center.y = -0.6; // shift above node
        return sprite;
      })
      .nodeThreeObjectExtend(true)

网上也有很多人在问这个问题,也有很多大佬解答ฅ ˘ฅ

原因是vue proxy导致的,vue的响应式系统基于proxy,这东西跟three.js的对象不兼容,threejs很多对象内有大量不可配置(non-configurable)、只读的属性(就比如modelViewMatrix),而vue在将对象放入data()或者通过响应式系统处理时,会尝试用proxy包裹这些对象——这就导致了冲突

很多大佬说把声明实例化的threejs对象放在全局即可解决,但是这里,注意!这里我没有将其挂载到vue里,我是局部变量・ࡇ・

但是问题的主要原因是一样的ଲଇଉକ ,只不过Vue的响应式系统与 Three.js 对象在nodeThreeObject回调中被间接“污染”了

但是我这里压根儿没用全局变量,这我他喵的怎么解决啊⚆_⚆?

但的确有解决办法 (具体说明在下面,没空儿直接cv,有空就去看看了解一下) :

解决办法

使用vue的 markRaw 包裹我上述代码中return出的对象sprite(vue2应该不会出现这个问题,我只在vue3遇到了这个问题,所以用了vue3的markRaw)

import {markRaw} from "vue";

//要改的就最后一行,return那个地方!!
//用markRaw包裹即可

Graph.graphData(this.graphData)
      .nodeAutoColorBy("name")
      .linkWidth(2)
      .linkDirectionalArrowLength(4)
      .linkDirectionalArrowRelPos(1)
      .nodeThreeObject((node) => {
        const sprite = new SpriteText(node.id);
        sprite.material.depthWrite = false; // make sprite background transparent
        sprite.color = node.color;
        sprite.textHeight = 10;
        sprite.center.y = -0.6; // shift above node
        return markRaw(sprite); //这里!!!!!!
      })

问题解决:

说明:

再看看报错:

'get' on proxy: property 'modelViewMatrix' is a read-only and non-configurable data property...

这说明:某个 Three.js 对象(如 SpriteText 实例)被 Vue 的响应式系统( reactive() / ref() )包裹了

虽然你没有显式把它放进data() , 但 Vue 在某些情况下会自动追踪闭包中的对象引用(尤其是在 devtools 启用、或组件实例持有引用时)。

而3d-force-graph内部会把nodeThreeObject返回的对象加入到 Three.js 的场景树(scene graph)中,这些对象含有大量只读属性(如matrixWorld, modelViewMatrx),一旦被 Proxy 包裹就会崩溃。

就算你是在函数内部创建的 SpriteText ,但是如果它被 Vue 的响应式上下文“看到”,可能还会出问题。。。

(但我觉得一定会出问题 ( ̄▽ ̄)"),在这种情况下,markRaw应该是唯一最可靠的方法了

最保险的方式是: 明确告诉vue "这个对象不要响应式化" !!!

补充

  • markRaw 不会影响Three.js的正常渲染,它只是告诉vue:"别管这个对象"
  • 这个问题一般出现在 vue3 + 开发模式 + 某些第三方库 中出现,生产构建(  vite build vue-cli-service build  ) 有时会因 tree-shaking 或无 devtools 而不报错,但开发时必须修复 ( ̄_ ̄|||)
  • three-spriteText 本身没啥问题,问题是在于它返回的是 THREE.Sprite 子类,而 THREE.Object3D 族系与 vue3 proxy 天然冲突
  • vue2应该不会出现这个问题,我只在vue3遇到了这个问题,所以用了vue3的markRaw

最后就祝大家新年快乐吧ヾ(≧▽≦*)o

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐