three.js引入
npm install three
安装轨道控件插件:
npm install three-orbit-controls
安装渲染器插件:
npm i --save three-css2drender
vue文件中引用:
import * as Three from 'three'
在页面中创建场景
//创建一个三维场景
const scene = new THREE.Scene();
创建一个透视相机
//创建一个透视相机,窗口宽度,窗口高度
const width = window.innerWidth,
height = 400;
const camera = new THREE.PerspectiveCamera(38, width / height, 0.25, 100);
//设置相机位置
camera.position.set(-5, 3, 10);
//设置相机方向
camera.lookAt(new THREE.Vector3(0, 2, 0));
初始化渲染器
// 初始化渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setClearAlpha(0);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, height);
renderer.outputEncoding = THREE.sRGBEncoding;
container.appendChild(renderer.domElement);
window.addEventListener("resize", onWindowResize);
初始动画混合器
// 创建动画混个器
let mixer = new THREE.AnimationMixer(scene);
// 创建时钟对象 该对象用于跟踪时间
let clock = new THREE.Clock();
动画混合器是用于场景中特定对象的动画的播放器。当场景中的多个对象独立动画时,每个对象都可以使用同一个动画混合器。
参数:rootObject 混合器播放的动画所属的对象。就是包含动画模型的场景对象。
常用参数和属性:
.time 全局的混合器时间。
.clipAction(AnimationClip) 返回所传入的剪辑参数的AnimationAction对象。AnimationAction用来调度存储在AnimationClip中的动画。
AnimationClip 动画剪辑,是一个可重用的关键帧轨道集,它代表动画。
.getRoot() 返回混合器的根对象。
.update() 推进混合器时间并更新动画。在渲染函数中调用更新动画。文章来源地址https://www.toymoban.com/news/detail-767151.html
创建光源,用于视觉可见
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);
hemiLight.position.set(0, 20, 0);
scene.add(hemiLight);
const dirLight = new THREE.DirectionalLight(0xffffff);
dirLight.position.set(0, 20, 10);
scene.add(dirLight);
创建加载glb文件模型
const loader = new GLTFLoader();
loader.load(
"RobotExpressive.glb",
function (gltf) {
// 存储模型含有动画集合
model = gltf.scene;
scene.add(model);
createGUI(model, gltf.animations);
},
undefined,
function (e) {
console.error(e);
}
);
最后加上动画循环渲染
const animate = () => {
const dt = clock.getDelta();
if (mixer) mixer.update(dt);
requestAnimationFrame(animate);
renderer.render(scene, camera);
stats.update();
};
完整代码
<template>
<div id="my-three"></div>
</template>
<script>
import {
defineComponent,
getCurrentInstance,
onMounted,
onUnmounted,
computed,
reactive,
toRefs,
} from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import Stats from "three/examples/jsm/libs/stats.module.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
export default defineComponent({
name: "index",
setup() {
const ctx = getCurrentInstance();
let state = reactive({
currentIndex:0,
});
//创建一个三维场景
const scene = new THREE.Scene();
//创建一个透视相机,窗口宽度,窗口高度
const width = window.innerWidth,
height = 400;
const camera = new THREE.PerspectiveCamera(38, width / height, 0.25, 100);
//设置相机位置
camera.position.set(-5, 3, 10);
//设置相机方向
camera.lookAt(new THREE.Vector3(0, 2, 0));
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
let mixer = new THREE.AnimationMixer(scene);
let stats = new Stats();
let clock = new THREE.Clock();
let actions = {},
activeAction,
previousAction;
// 默认动画
const api = {state:'Walking'}
//控制器
// const Controls = new OrbitControls(camera, renderer.domElement);
onMounted(() => {
onLoad();
});
const onLoad = () => {
let model, container;
container = document.getElementById("my-three");
// scene.background = new THREE.Color(0xe0e0e0);
scene.fog = new THREE.Fog(0xe0e0e0, 20, 100);
// lights
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);
hemiLight.position.set(0, 20, 0);
scene.add(hemiLight);
const dirLight = new THREE.DirectionalLight(0xffffff);
dirLight.position.set(0, 20, 10);
scene.add(dirLight);
// model
const loader = new GLTFLoader();
loader.load(
"RobotExpressive.glb",
function (gltf) {
// 存储模型含有动画集合
model = gltf.scene;
scene.add(model);
createGUI(model, gltf.animations);
},
undefined,
function (e) {
console.error(e);
}
);
renderer.setClearAlpha(0);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, height);
renderer.outputEncoding = THREE.sRGBEncoding;
container.appendChild(renderer.domElement);
window.addEventListener("resize", onWindowResize);
container.appendChild(stats.dom);
animate();
// 点击动画
renderer.domElement.addEventListener("click", (event) => {
const { offsetX, offsetY } = event;
const x = (offsetX / window.innerWidth) * 2 - 1;
const y = -(offsetY / window.innerHeight) * 2 + 1;
const mousePoint = new THREE.Vector2(x, y);
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mousePoint, camera);
const instersects = raycaster.intersectObjects(scene.children);
instersects.forEach((item) => {
console.log(item.object.name);
if (
item.object.name == "Head_2" ||
item.object.name == "Head_3" ||
item.object.name == "Head_4"
) {
// console.log(instersects);
// action.stop();
const states = [
"Idle",
"Dance",
"Death",
"Sitting",
"Standing",
"Jump",
"Yes",
"No",
"Wave",
"Punch",
"ThumbsUp",
];
if(state.currentIndex<states.length - 1){
state.currentIndex = state.currentIndex+1
}else{
state.currentIndex = 0
}
fadeToAction(states[state.currentIndex], 0.2);
mixer.addEventListener("finished", restoreState);
}
});
});
};
const createGUI = (model, animations) => {
const states = [
"Idle",
"Walking",
"Running",
"Dance",
"Death",
"Sitting",
"Standing",
];
const emotes = ["Jump", "Yes", "No", "Wave", "Punch", "ThumbsUp"];
for (let i = 0; i < animations.length; i++) {
const clip = animations[i];
const action = mixer.clipAction(clip);
actions[clip.name] = action;
if (emotes.indexOf(clip.name) >= 0 || states.indexOf(clip.name) >= 4) {
action.clampWhenFinished = true;
action.loop = THREE.LoopOnce;
}
}
activeAction = actions["Walking"];
activeAction.play();
};
const fadeToAction = (name, duration) => {
previousAction = activeAction;
activeAction = actions[name];
if (previousAction !== activeAction) {
previousAction.fadeOut(duration);
}
activeAction
.reset()
.setEffectiveTimeScale(1)
.setEffectiveWeight(1)
.fadeIn(duration)
.play();
};
const restoreState = () => {
mixer.removeEventListener("finished", restoreState);
fadeToAction(api.state, 0.2);
};
const animate = () => {
const dt = clock.getDelta();
if (mixer) mixer.update(dt);
requestAnimationFrame(animate);
renderer.render(scene, camera);
stats.update();
};
const onWindowResize = () => {
camera.aspect = window.innerWidth / height;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, height);
};
onUnmounted(() => {
scene.traverse((e) => {
if (e.BufferGeometry) e.BufferGeometry.dispose();
if (e.material) {
if (Array.isArray(e.material)) {
e.material.forEach((m) => {
m.dispose();
});
} else {
e.material.dispose();
}
}
if (e.isMesh) {
e.remove();
}
});
scene.remove();
renderer.dispose();
renderer.content = null;
window.removeEventListener("resize", onWindowResize, false);
});
return {
...toRefs(state),
onLoad,
createGUI,
};
},
});
</script>
<style lang="less">
@import "./index.less";
</style>
文章来源:https://www.toymoban.com/news/detail-767151.html
到了这里,关于vue3结合three.js实现3D带有交互的动画的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!