Skip to content

tresjs 集成

tres.js 是一个基于 vue3three.js 的封装库,它让你可以使用 vue 组件的方式来构建 3D 场景,降低了使用 three.js 的门槛,并且更适合 Vue 生态的开发者。

注意

在小程序中使用当前版本的 tresjs 有许多需要注意的问题,详见这里

从模板开始

建议直接使用集成好的项目模板,它包含了完整的分包优化/代码兼容性配置。

如遇下载失败,你也可以手动到 github 代码仓库根据对应分支下载模板。

shell
npx degit minisheeep/threejs-miniprogram-template#uni-ts-tresjs my-project
shell
npx degit minisheeep/threejs-miniprogram-template#weapp-vite-tresjs my-project

手动集成

安装

shell
pnpm i @minisheep/platform-adapter-integration

使用

然后在 vite 的配置文件中添加以下内容:

ts
import { defineConfig } from 'vite';
import threePlatformAdapter from '@minisheep/three-platform-adapter/plugin';
import vue from '@vitejs/plugin-vue'; 
import { templateCompilerOptions } from '@tresjs/core'; 

export default defineConfig({
  plugins: [
    //...
    threePlatformAdapter(),
    vue({
      ...templateCompilerOptions,
    }),
  ],
})

然后使用包导出的 mountTresApp 方法将你使用 tresjs 编写的场景组件挂载到画布上。

js
import { mountTresApp } from "@minisheep/platform-adapter-integration/tresjs";
import YourScene from 'path/to/YourScene.vue'

// ...
await adapter.useCanvas(`#canvas-id`).then(result=>{
  const app = mountTresApp(result.canvas, YourScene)
  // ...
});

相关函数声明如下。

ts
import type { App, RendererElement } from 'vue';

export function mountTresApp(
  canvas: HTMLCanvasElement,
  rootComponent: Component,
  rootProps?: Record<string, unknown> | null
): App<RendererElement>

需要注意的是,挂载的场景组件需要满足如下要求

  • 只有一个 TresCanvas 根节点
  • 禁止包含其他小程序元素

例如:

vue

<template>
  <TresCanvas clear-color="#82DBC5" shadows alpha window-size>
    <!--  场景内容  -->
  </TresCanvas>
</template>

完整的代码你可以参考此模板

uni-app 中使用

uni-app 中集成 tresjs 会稍显复杂, 原因在于 uni-app 构建小程序时使用的是一个经过特殊处理的 Vue 版本(详见 @dcloudio/uni-mp-vue),该版本移除了 @vue/runtime-core 中的多个导出项。

tresjs 是基于被移除的 createRenderer 构建的,因此,除了基本配置外,还需要额外处理,才能使其在 uni-app 中正常工作。

tresjs 组件专门存放

首先,你需要为基于 tresjs 开发的组件单独定义一个目录。

接着在 uni 插件的处理逻辑中排除该目录,以确保这些组件可以访问标准的 Vue 运行时,而非 uni 定制的版本。

vite.config.ts 中添加以下配置:

ts
import { defineConfig } from 'vite';
import uni from '@dcloudio/vite-plugin-uni';
import threePlatformAdapter from '@minisheep/three-platform-adapter/plugin';
import vue from '@vitejs/plugin-vue'; 
import { templateCompilerOptions } from '@tresjs/core';
import { useStandardVuePlugin } from "@minisheep/platform-adapter-integration/tresjs/plugin";

// 匹配组件的 pattern
const tresjsComponentPattern = /\/path_to_your_tresjs_component_folder\/.*.vue///
export default defineConfig({
  plugins: [
    //@ts-expect-error uni-plugin在 当前版本在以 esm 形式导出格式异常
    uni.default({
      vueOptions: {
        exclude: [tresjsComponentPattern] 
      }
    }),
    vue({
      include: [tresjsComponentPattern], 
      ...templateCompilerOptions
    }),
    // ⬇️ 这会使 pattern 匹配到的文件能够使用标准的 vue 导出
    useStandardVuePlugin({
      include: [
        tresjsComponentPattern
      ]
    })
  ],
})

然后像之前一样将组件通过 mountTresApp 挂载到画布上即可。

模板中的配置还处理了一些兼容和分包问题,详细配置你可以参考它。

强制覆盖依赖版本

由于 uni-app 相关插件固定依赖了 @vue/shared 某个版本, 可能会与 @tresjs/core 依赖的 @vue/shared 版本出现不一致的情况,为避免重复导入以及出现非预期问题, 可以通过配置强制统一版本。

你需要在 package.json 增加以下内容, 具体版本号可以根据你安装的 @tresjs/core 所依赖的 vue 版本决定

json
{
  "pnpm": {
    "overrides": {
      "@vue/shared": "^x.x.xx"
    }
  }
}
json
{
  "overrides": {
    "@vue/shared": "^3.5.13"
  }
}
json
{
  "resolutions": {
    "@vue/shared": "^3.5.13"
  }
}

额外处理

由于 trejs 目前的 Event System 在触摸设备上运行是有缺陷的,需要等待后续的 pull 被合并才可能会解决。

具体表现是光线投射的目标元素只会根据 pointermove 事件进行更新,导致单一点击相关事件(click,pointer-down,pointer-up等)不能在正确的元素上触发。

你需要手动进行如下处理:

  • 每次在传递 touchstart 事件之前需要手动复制并修改 typetouchmove,然后传递一次。参考如下代码:
js
import { adapter } from "@minisheep/three-platform-adapter";

Page({
  tresApp: null,
  defaultHandler: ()=>{},
  async onReady() {
    const result = await adapter.useCanvas(`#tresjs_demo`, this);
    this.defaultHandler = (e) => {
      if (e.type === 'touchstart') {
        result.eventHandler({
          ...e,
          type: 'touchmove',
        }, true, true) // 这里第三个参数需要为 true
      }
      result.eventHandler(e, true)
    };
  },
})
wxml
<view class="page-container">
  <canvas
    type="webgl2"
    id="tresjs_demo"
    bindtouchstart="defaultHandler"
    bindtouchmove="defaultHandler"
    bindtouchcancel="defaultHandler"
    bindtouchend="defaultHandler"
    bindtap="defaultHandler"
  >
  </canvas>
</view>