Мои три. js сцена полностью искажена, пока я не наведу указатель мыши куда-нибудь на сайте. Вы можете увидеть характер искажения на изображении ниже:
Когда я двигаю мышью, сцена внезапно появляется, и все в порядке. Кажется, не имеет значения, где именно находится курсор на сайте, он не обязательно должен находиться над холстом, на котором отображается моя сцена. Вот как сцена выглядит после перемещения мыши:
Используются следующие три. js зависимости:
- "три": "^ 0.108.0"
- "три орбиты": "^ 2.102.2"
- "three.meshline": "^ 1.2.0"
Я пробовал обновить три до последней версии (0.116.1), но это тоже не решило проблему. Мне удалось воспроизвести эту проблему на Firefox и Edge, но не на Chrome.
Некоторый дополнительный контекст: мы используем OffscreenCanvas для повышения производительности, позиции мыши отправляются из основного потока в веб-воркер при событии mousemove мы используем эту информацию для небольшого перемещения камеры и фона (со смещениями). Я временно удалил logi c обработчика mousemove из кода веб-воркера, и проблема все еще возникала, так что, вероятно, она не связана. Мы используем tween. js, чтобы сделать анимацию камеры плавной.
Соответствующие фрагменты кода:
Настройка сцены :
const {scene, camera} = makeScene(elem, cameraPosX, 0, 60, 45);
const supportsWebp = (browser !== 'Safari');
imageLoader.load(backgroundImage, mapImage => {
const texture = new THREE.CanvasTexture(mapImage);
texture.anisotropy = renderer.capabilities.getMaxAnisotropy();
texture.minFilter = THREE.LinearFilter;
// Repeat background so we don't run out of it during offset changes on mousemove
texture.wrapS = THREE.MirroredRepeatWrapping;
texture.wrapT = THREE.MirroredRepeatWrapping;
scene.background = texture;
});
// Creating objects in the scene
let orbitingPlanet = getPlanet(0xffffff, true, 1 * mobilePlanetSizeAdjustment);
scene.add(orbitingPlanet);
// Ellipse class, which extends the virtual base class Curve
let curveMain = new THREE.EllipseCurve(
0, 0, // ax, aY
80, 30, // xRadius, yRadius
0, 2 * Math.PI, // aStartAngle, aEndAngle
false, // aClockwise
0.2 // aRotation
);
let ellipseMainGeometry = new THREE.Path(curveMain.getPoints(100)).createPointsGeometry(100);
let ellipseMainMaterial = new MeshLine.MeshLineMaterial({
color: new THREE.Color(0xffffff),
opacity: 0.2,
transparent: true,
});
let ellipseMain = new MeshLine.MeshLine();
ellipseMain.setGeometry(ellipseMainGeometry, function(p) {
return 0.2; // Line width
});
const ellipseMainMesh = new THREE.Mesh(ellipseMain.geometry, ellipseMainMaterial );
scene.add(ellipseMainMesh);
// Create a halfish curve on which one of the orbiting planets will move
let curveMainCut = new THREE.EllipseCurve(
0, 0, // ax, aY
80, 30, // xRadius, yRadius
0.5 * Math.PI, 1.15 * Math.PI, // aStartAngle, aEndAngle
false, // aClockwise
0.2 // aRotation
);
let lastTweenRendered = Date.now();
let startRotation = new THREE.Vector3(
camera.rotation.x,
camera.rotation.y,
camera.rotation.z);
let tweenie;
return (time, rect) => {
camera.aspect = state.width / state.height;
camera.updateProjectionMatrix();
let pt1 = curveMainCut.getPointAt(t_top_faster);
orbitingPlanet.position.set(pt1.x, pt1.y, 1);
t_top_faster = (t_top_faster >= 1) ? 0 : t_top_faster += 0.001;
// Slightly rotate the background on mouse move
if (scene && scene.background) {
// The rotation mush be between 0 and 0.01
scene.background.rotation =
Math.max(-0.001,Math.min(0.01, scene.background.rotation + 0.00005 * target.x));
let offsetX = scene.background.offset.x + 0.00015 * target.x;
let offsetY = scene.background.offset.y + 0.00015 * target.y;
scene.background.offset = new THREE.Vector2(
(offsetX > -0.05 && offsetX < 0.05) ? offsetX : scene.background.offset.x,
(offsetY > -0.05 && offsetY < 0.05) ? offsetY : scene.background.offset.y);
}
lastTweenRendered = tweenAnimateCamera(lastTweenRendered, tweenie, camera, startRotation, 200);
renderer.render(scene, camera);
};
функция makeScene :
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(fieldOfView, state.width / state.height, 0.1, 100000000);
camera.position.set(camPosX, camPosY, camPosZ);
camera.lookAt(0, 0, 0);
scene.add(camera);
return {scene, camera};
Анимация камеры в зависимости от положения мыши:
function tweenAnimateCamera(lastTweenRendered, tween, camera, startRotation, period) {
target.x = (1 - mouse.x) * 0.002;
target.y = (1 - mouse.y) * 0.002;
let now = Date.now();
if ((
// Don't let the camera go too far
startRotation.x > -0.01 && startRotation.x < 0.01) &&
now - lastTweenRendered > (period / 2)) {
if (tween) {
tween.stop();
}
lastTweenRendered = now;
let endRotation = new THREE.Vector3(
camera.rotation.x + 0.005 * (target.y - camera.rotation.x),
camera.rotation.y + 0.005 * (target.x - camera.rotation.y),
camera.rotation.z);
tween = new TWEEN.Tween(startRotation)
.to(endRotation, period * 2)
.easing(TWEEN.Easing.Quadratic.InOut)
.onUpdate(function (v) {
camera.rotation.set(v.x, v.y, v.z);
})
.onComplete(function(v) {
startRotation = v.clone();
});
tween.start();
}
TWEEN.update();
return lastTweenRendered
}
Logi приемника положения мыши c:
if (e.data.type === 'mousePosUpdate') {
if (e.data.x !== -100000 && e.data.y !== -100000) {
mouse.x = ( e.data.x - state.width / 2 );
mouse.y = ( e.data.y - state.height / 2 );
target.x = ( 1 - mouse.x ) * 0.002;
target.y = ( 1 - mouse.y ) * 0.002;
}
}
Отрисовка l oop:
function render(time) {
time *= 0.001;
for (const {elem, fn, ctx} of sceneElements) {
// get the viewport relative position of this element
canvasesUpdatedPos.forEach( canvasUpdate => {
if (canvasUpdate.id === elem.id) {
elem.rect = canvasUpdate.rect;
}
});
const rect = elem.rect;
const bottom = rect.bottom;
const height = rect.height;
const left = rect.left;
const right = rect.right;
const top = rect.top;
const width = rect.width;
const rendererCanvas = renderer.domElement;
const isOffscreen =
bottom < 0 ||
top > state.height ||
right < 0 ||
left > state.width;
if (!isOffscreen && width !== 0 && height !== 0) {
// make sure the renderer's canvas is big enough
let isResize = resizeRendererToDisplaySize(renderer, height, width);
// make sure the canvas for this area is the same size as the area
if (ctx.canvas.width !== width || ctx.canvas.height !== height) {
ctx.canvas.width = width;
ctx.canvas.height = height;
state.width = width;
state.height = height;
}
renderer.setScissor(0, 0, width, height);
renderer.setViewport(0, 0, width, height);
fn(time, rect);
// copy the rendered scene to this element's canvas
ctx.globalCompositeOperation = 'copy';
ctx.drawImage(
rendererCanvas,
0, rendererCanvas.height - height, width, height, // src rect
0, 0, width, height); // dst rect
}
}
// Limiting to 35 FPS.
setTimeout(function() {
if (!stopAnimating) {
requestAnimationFrame(render);
}
}, 1000 / 35);
}