@open-dy/threejs
v0.1.1
Published
Scoped Three.js r160 adapter for Douyin Mini Program Canvas V2
Readme
@open-dy/threejs
在抖音小程序 Canvas V2 + WebGL 中使用 Three.js 0.160.1。页面取得 Canvas node 后,通过 createScopedThreejs() 创建当前 Canvas 独立的 THREE;后续场景代码直接使用标准的 THREE.Scene、THREE.WebGLRenderer、材质、Loader 和 addons。
安装
pnpm add @open-dy/threejsimport { createScopedThreejs } from '@open-dy/threejs';CommonJS 使用:
const { createScopedThreejs } = require('@open-dy/threejs');创建 Three.js 场景
下面的函数可由原生 TTML 和 Taro 页面共同使用:
import {
createScopedThreejs,
type MiniappCanvas,
type MiniappRuntimeApi,
} from '@open-dy/threejs';
export interface CanvasInfo {
height: number;
left?: number;
node: MiniappCanvas;
top?: number;
width: number;
}
export function createThree(api: MiniappRuntimeApi, canvasInfo: CanvasInfo) {
const { height, left = 0, node: canvas, top = 0, width } = canvasInfo;
const THREE = createScopedThreejs(canvas, {
api,
displaySize: { height, left, top, width },
});
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 100);
camera.position.z = 4;
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(width, height, false);
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshNormalMaterial();
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
renderer.setAnimationLoop((time) => {
cube.rotation.x = time * 0.0004;
cube.rotation.y = time * 0.0007;
renderer.render(scene, camera);
});
return {
THREE,
dispose() {
renderer.setAnimationLoop(null);
geometry.dispose();
material.dispose();
renderer.dispose();
THREE.dispose();
},
};
}把上面的共享函数放在项目任意模块,并将下方的 <your-three-module> 替换为实际导入路径。
原生 TTML
TTML:
<canvas id="three-canvas" type="webgl" />页面 TypeScript:
import type { MiniappRuntimeApi } from '@open-dy/threejs';
import { createThree, type CanvasInfo } from '<your-three-module>';
let three: ReturnType<typeof createThree> | undefined;
Page({
onReady() {
const query = tt.createSelectorQuery();
query.select('#three-canvas').fields({ node: true, rect: true, size: true });
query.exec((results) => {
const canvasInfo = results[0] as CanvasInfo | undefined;
if (!canvasInfo?.node || !canvasInfo.width || !canvasInfo.height) return;
three = createThree(tt as unknown as MiniappRuntimeApi, canvasInfo);
});
},
onUnload() {
three?.dispose();
three = undefined;
},
});Taro React
import { useRef } from 'react';
import { Canvas } from '@tarojs/components';
import Taro, { useReady, useUnload } from '@tarojs/taro';
import type { MiniappRuntimeApi } from '@open-dy/threejs';
import { createThree, type CanvasInfo } from '<your-three-module>';
export default function ThreePage() {
const threeRef = useRef<ReturnType<typeof createThree>>();
useReady(() => {
const query = Taro.createSelectorQuery();
query.select('#three-canvas').fields({ node: true, rect: true, size: true });
query.exec((results) => {
const canvasInfo = results[0] as CanvasInfo | undefined;
if (!canvasInfo?.node || !canvasInfo.width || !canvasInfo.height) return;
threeRef.current = createThree(
Taro as unknown as MiniappRuntimeApi,
canvasInfo,
);
});
});
useUnload(() => {
threeRef.current?.dispose();
threeRef.current = undefined;
});
return <Canvas id='three-canvas' type='webgl' />;
}