javascript - 为什么three.module.js的AnimationMixer与three.min.js的工作方式不同
问题描述
当我使用来自three.module.js 的AnimationMixer 时,我注意到了一个令人恼火的系统反应:我的代码旨在改变立方体在VectorKeyframeTrack 中给定的时间轴上的位置。基本上它可以工作,但是当我使用mixer.stopAllAction()停止动画时,立方体并没有在停止的那一刻停留在该位置,而是下降到它的起始位置。同样,当动画剪辑结束并且立方体定位在目标位置时(我使用了clampWhenFinished = true),使用stopAllAction() 时位置更改为起始位置。在我看来,动画并没有真正改变立方体的位置值。原点被保留,一旦动画停止,该原点将再次应用。
现在我发现,在使用three.min.js库时,并没有出现这种现象。然后可以在中间停止动画移动,并且立方体保持其停止点的位置值。换句话说,使用 three.min.js 它可以按预期工作。尽管如此,我还是想使用three.module.js,因为它提供了我以后需要的其他功能。
所以我的问题是:three.module.js 中的 AnimationMixer 行为是错误还是功能?如果是一个功能,有人可以解释为什么这是有意义的吗?
我当前的解决方案是一个混合代码:我使用了 three.module.js 中的所有内容,但我还必须加载 three.min.js 才能将它用于“旧但工作”的 AnimationMixer。
为了解决这个问题,我制作了三个示例代码:a)基于three.min.js(有效)b)基于three.module.js(显示奇怪的行为)c)具有两个库的混合(有效)
我在这里添加 b) 的代码以防有人感兴趣。此代码仅用于演示问题,并且某些值是硬编码的。我正在使用两个可以通过页面上的按钮启动的混音器。x、y 和 z 位置在对象移动时填充。使用 Stop All Animations 按钮,立方体的移动应该停止。但在这个版本 b) 中,您会看到它会退回到原来的位置。
索引.html:
<html>
<head>
<title>Demo 1 based on three.module.js</title>
<style>
body { margin: 0; }
canvas { width: 100%; height: 100% }
</style>
</head>
<div>
<label for="PositionX">X-Position:</label>
<input type="text" id="PositionX" size="2" value="0" onchange="game.changeXPos.call(game, this.value)" />
<label for="PositionY">Y-Position:</label>
<input type="text" id="PositionY" size="2" value="0" onchange="game.changeYPos.call(game, this.value)" />
<label for="PositionZ">Z-Position:</label>
<input type="text" id="PositionZ" size="2" value="0" onchange="game.changeZPos.call(game, this.value)" />
<button onclick="game.stopAnimation.call(game);">Stop All Animations</button>
<button onclick="game.playAnimationCube1.call(game);">Move Cube1</button>
<button onclick="game.playAnimationCube2.call(game);">Move Cube2</button>
</div>
<body>
<script type="module" src="main.js"></script>
</body>
</html>
主.js:
import { Game } from './game.js';
async function main() {
// Get a reference to the container element
const container = document.querySelector('#scene-container');
var game;
document.addEventListener("DOMContentLoaded", function(){
game = new Game(container);
console.log("demo1modular: In mainjs game", game);
window.game = game;
});
}
main().catch((err) => {
console.error(err);
});
带有动画逻辑的game.js:
import { Clock, Color, Scene, PerspectiveCamera, WebGLRenderer, BoxGeometry,
DirectionalLight, AmbientLight, MeshPhongMaterial,
Mesh, AnimationMixer, AnimationClip, LoopOnce, VectorKeyframeTrack
} from 'https://unpkg.com/three@0.117.0/build/three.module.js';
let camera;
let clock;
let renderer;
let scene;
let mode;
let modes;
let cube;
let mixer1;
let mixer2;
let action1;
let action2;
class Game{
constructor(){
modes = Object.freeze({
NONE: Symbol("none"),
ANIMATION_ACTIVE: Symbol("active"),
ANIMATION_OFF: Symbol("off")
});
mode = modes.NONE;
clock = new Clock();
scene = new Scene();
camera = new PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
renderer = new WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
const geometry = new BoxGeometry( 1, 1, 1 );
const light = new DirectionalLight( 0xffffff );
light.position.set( 0, 20, 10 );
const ambient = new AmbientLight( 0x707070 ); // soft white light
const material = new MeshPhongMaterial( { color: 0x00aaff } );
cube = new Mesh( geometry, material );
scene.add( light );
scene.add( ambient );
camera.position.z = 5;
mixer1 = new THREE.AnimationMixer( cube );
const times = [0, 2];
const values1 = [0,0,0, 0,2,0 ];
const positionKF1 = new VectorKeyframeTrack('.position', times, values1);
const length = -1;
const myClipPos1 = new AnimationClip('Position change', length, [positionKF1])
console.log("demo1modular: myClipPos1:", myClipPos1);
action1 = mixer1.clipAction( myClipPos1);
action1.clampWhenFinished = true;
console.log("demo1modular: In createAnimations action:", action1);
// the second track starts where the first has ended
mixer2 = new THREE.AnimationMixer( cube );
const values2 = [0,2,0, -2,0,0 ];
const positionKF2 = new VectorKeyframeTrack('.position', times, values2);
const myClipPos2 = new AnimationClip('Position change', length, [positionKF2])
console.log("demo1modular: myClipPos2:", myClipPos2);
action2 = mixer2.clipAction( myClipPos2);
action2.clampWhenFinished = true;
console.log("demo1modular: In createAnimations action:", action2);
scene.add( cube );
this.animate();
this.positionChange();
}
animate() {
const game = this;
requestAnimationFrame( function(){ game.animate(); } );
const delta = clock.getDelta();
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
if (mode == modes.ANIMATION_ACTIVE)
{
document.getElementById("PositionX").value = cube.position.x;
document.getElementById("PositionY").value = cube.position.y;
document.getElementById("PositionZ").value = cube.position.z;
}
mixer1.update( delta )
mixer2.update( delta )
renderer.render( scene, camera );
}
positionChange() {
// put it on the bottom
cube.position.set (0,-2,0);
// show the actual position in the page
document.getElementById("PositionX").value = cube.position.x;
document.getElementById("PositionY").value = cube.position.y;
document.getElementById("PositionZ").value = cube.position.z;
}
changeXPos(value) {cube.position.x = value;}
changeYPos(value) {cube.position.y = value;}
changeZPos(value) {cube.position.z = value;}
stopAnimation(){
mixer1.stopAllAction();
mixer2.stopAllAction();
mode = modes.ANIMATION_OFF;
document.getElementById("PositionX").value = cube.position.x;
document.getElementById("PositionY").value = cube.position.y;
document.getElementById("PositionZ").value = cube.position.z;
}
playAnimationCube1(){
mode = modes.ANIMATION_ACTIVE;
console.log('demo1modular in playAnimationCube mixer, action', mixer1, action1);
mixer1.stopAllAction();
action1.weight = 1;
action1.fadeIn(0.5);
action1.loop = LoopOnce;
action1.play();
mixer1.addEventListener('finished', function(e){
// the following statement I would like to execute but it does not work
// mode = modes.ANIMATION_OFF;
})
}
playAnimationCube2(){
mode = modes.ANIMATION_ACTIVE;
console.log('demo1modular in playAnimationCube mixer, action', mixer2, action2);
mixer2.stopAllAction();
action2.weight = 1;
action2.fadeIn(0.5);
action2.loop = LoopOnce;
action2.play();
mixer2.addEventListener('finished', function(e){
// the following statement I would like to execute but it does not work
// mode = modes.ANIMATION_OFF;
})
}
}
export { Game };
解决方案
推荐阅读
- java - 单击 BottomSheet RecyclerView 时为空 ArrayList
- google-maps - 用于谷歌地图标记的 SVG 图标
- primefaces - 如何使用 Chart.js 限制 X 轴上的数据点数量
- wordpress - Woocommerce 产品类别和子类别下拉选择
- swift - 解决类的歧义扩展属性
- android - 使用 Ionic 3 将生成的 Blob 下载到 Android 设备中
- extjs - 网格组合框和文本字段编辑器 - 动态更改 emptyText 并清除字段
- awk - awk 脚本未读取要执行的输入文件
- java - 创建 PDF 并在 ResponseEntity 中返回
- python - 在 Python 中读取对称的 Harwell-Boeing 矩阵