声明式开发Threejs(四)

244 阅读2分钟

界面优化

在上一章最后,我们虽然做了优化,但是还是有点模糊,如下

image.png

我们继续优化

在上一节我们做了如下的设置,使效果好了一些,但是这个是有性能消耗的,官网默认是false,说明关键不是这个,也大概推断一下,默认方法应该是权衡之后的默认值,所以我们应该查看下面的方法(人家也不可能那么傻对吧)

image.png

查看方法的原因就是因为我们有些内容没有设置对,我们查阅官网,查看会导致模糊的原因

image.png

可以看到设备像素比影响像素(清晰度),有两个需要我们设置,我们来设置一下

// useMyContextProvider.ts
  import { shallowRef, watchEffect } from "vue";

  watchEffect(() => {
    const sizes = useElementSize(computed(() => toValue(canvas).parentElement));
    renderer.value.setSize(sizes.width.value, sizes.height.value);
  });

  const { pixelRatio } = useDevicePixelRatio();

  watchEffect(() => {
    renderer.value.setPixelRatio(pixelRatio.value);
  });

image.png

然后我们看一下效果,是不是好了

image.png

OrbitControls

现在界面还是不能控制物体旋转的,我们来继续封装

image.png

当我们传入一个OrbitControls标签的时候,就代表我们有了这个能力,我们来继续开发

在开始之前我们先看一下正常这个东西需要我们怎么做,如下

import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const controls = new OrbitControls( camera, renderer.domElement );

image.png 循环里的update我们先不管,因为我们不需要更改相机等操作

开发

前面我们封装的都是直接传入标签,然后解析完成我们的设置,今天这个我们放到一个单独组件里进行

// App.vue
<script setup lang="ts">
import MyCanvas from "./components/MyCanvas.vue";
import OrbitControls from "./components/OrbitControls.vue";

</script>

<template>
  <MyCanvas>
    <OrbitControls />
    <MyMesh>
      <MyConeGeometry />
      <MyMeshToonMaterial />
    </MyMesh>
  </MyCanvas>
</template>

<style scoped></style>
// ./components/OrbitControls.vue
<script setup lang="ts">

</script>

<template>
  <MyOrbitControls />
</template>

<style scoped></style>

image.png

通过上面我们知道我们需要在实例化的时候,传入相机和画布,然后是实例化的过程在nodeOps中,我们来修改一下,先判断props和参数和tag类型,然后传入相应参数进行实例化

const nodeOps: RendererOptions<MyObject, MyObject> = {
  createElement(tag, _isSVG, _anchor, props) {
    if (!props) {
      props = {};
    }

    if (!props.args) {
      props.args = [];
    }
    if (tag === "template") {
      return null;
    }
    if (isHTMLTag(tag)) {
      return null;
    }

    let instance;
    let name = tag.replace("My", "");
    const target = catalogue.value[name];
    // 实例化 - OrbitControls
    instance = new target(...props.args);

    if (props?.attach === undefined) {
      if (instance.isMaterial) {
        instance.attach = "material";
      } else if (instance.isBufferGeometry) {
        instance.attach = "geometry";
      }
    }

    return instance;
  }
 }

参数我们在组件中传入一下

<MyOrbitControls :args="[camera, canvas]" />

这个参数是应该怎么获取呢?props?inject?pinia?我们这是组件,如果以后想分开呢,做成真正的npm包呢?这样应该很容易的知道要使用 inject 了吧

在创建组件的时候我们提供上下文

// src/components/MyCanvas.vue
const createInternalComponent = (context) =>
  defineComponent({
    setup() {
      provide("context", context);
      return () => h(Fragment, null, slots?.default ? slots.default() : []);
    },
  });

const mountCustomRenderer = (context) => {
  const InternalComponent = createInternalComponent(context);
  render(h(InternalComponent), scene.value);
};

这个时候在在src/components/OrbitControls.vue下面inject没有camera,这是因为我们提供的时候,还没有这个,我们把创建时机移后一些,如下

image.png 然后我们在OrbitControls.vue接收一下并传入参数

<script setup lang="ts">
import { inject, unref } from "vue";

const { camera, canvas } = inject("context");

</script>

<template>
  <MyOrbitControls :args="[camera, canvas]" />
</template>

<style scoped></style>

image.png

最后效果如下

Screen-Recording-2024-05-22-at-14.45.16.gif