javascript - 通过坐标 x,y 结构 js 发出点击事件
问题描述
我开始使用three.js 和fabric.js 开发项目,我可以在其中移动和转换3d 模型上的纹理。我在模型上找到了坐标并将其与画布面料 js 上的坐标同步。但我不知道如何模拟点击事件并完全管理这个点击的对象。我找到了说明我的问题的 stackoverflow 主题。非常感谢!
从 Threejs 模型到用于纹理的 Fabricjs 画布的 Raycast 鼠标点击
代码笔:
https://codepen.io/ricardcreagia/pen/EdEGod
console.clear();
console.log("starting scripts...");
/**
* Fabricjs
* @type {fabric}
*/
var canvas = new fabric.Canvas( "canvas" );
canvas.backgroundColor = "#FFBE9F";
var rectangle = new fabric.Rect( {
top: 100,
left: 100,
fill: '#FF6E27',
width: 100,
height: 100,
transparentCorners: false,
centeredScaling: true,
borderColor: 'black',
cornerColor: 'black',
corcerStrokeColor: 'black'
} );
canvas.add( rectangle );
/**
* Threejs
*/
var containerHeight = "512";
var containerWidth = "512";
var camera, renderer, container, scene, texture, material, geometry,
cube;
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var onClickPosition = new THREE.Vector2();
init();
animate();
/**
* Configurator init function
*/
function init() {
/**
* Camera
*/
camera = new THREE.PerspectiveCamera( 30, window.innerWidth / window.innerHeight, 0.01, 100 );
camera.position.set( 0, 0, 3.5 );
/**
* Renderer
*/
container = document.getElementById( "renderer" );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( containerWidth, containerHeight );
camera.aspect = container.clientWidth / container.clientHeight;
camera.updateProjectionMatrix();
container.appendChild( renderer.domElement );
/**
* Scene
*/
scene = new THREE.Scene();
scene.background = new THREE.Color( 0x000000 );
/**
* Texture and material
*/
texture = new THREE.Texture( document.getElementById( "canvas" ) );
texture.anisotropy = renderer.capabilities.getMaxAnisotropy();
material = new THREE.MeshBasicMaterial( { map: texture } );
/**
* Model
*/
geometry = new THREE.BoxGeometry( 1, 1, 1 );
cube = new THREE.Mesh( geometry, material );
scene.add( cube );
}
/**
* Configurator frame render function
*/
function animate() {
requestAnimationFrame( animate );
cube.rotation.x += 0.004;
cube.rotation.y += 0.001;
texture.needsUpdate = true;
renderer.render( scene, camera );
}
/**
* Listeners
*/
container.addEventListener( "mousedown", onMouseClick, false );
/**
* Other methods
*/
function onMouseClick( evt ) {
evt.preventDefault();
var array = getMousePosition( container, evt.clientX, evt.clientY );
onClickPosition.fromArray( array );
var intersects = getIntersects( onClickPosition, scene.children );
if ( intersects.length > 0 && intersects[ 0 ].uv ) {
var uv = intersects[ 0 ].uv;
intersects[ 0 ].object.material.map.transformUv( uv );
var circle = new fabric.Circle({
radius: 3,
left: getRealPosition( "x", uv.x ),
top: getRealPosition( "y", uv.y ),
fill: 'red'
});
canvas.add( circle );
}
}
function getRealPosition( axis, value ) {
let CORRECTION_VALUE = axis === "x"
? 4.5
: 5.5;
return Math.round( value * 512 ) - CORRECTION_VALUE;
}
var getMousePosition = function ( dom, x, y ) {
var rect = dom.getBoundingClientRect();
return [ ( x - rect.left ) / rect.width, ( y - rect.top ) / rect.height ];
};
var getIntersects = function ( point, objects ) {
mouse.set( ( point.x * 2 ) - 1, - ( point.y * 2 ) + 1 );
raycaster.setFromCamera( mouse, camera );
return raycaster.intersectObjects( objects );
};
请注意,这段代码不是我的,我只是在 stackoverflow 上找到的,并没有声明作者身份
解决方案
旧问题的一些答案提到了将鼠标事件从一个画布镜像到另一个画布的正确方法:
var simEvt = new MouseEvent(evt.type, {
clientX: correctedPosition.x,
clientY: correctedPosition.y
});
canvas.upperCanvasEl.dispatchEvent(simEvt);
mousedown
不幸的是,仅在目标画布上调度、mouseup
和mousemove
事件是不够的。原因是当mousedown
在画布上触发事件时,fabric.js 会立即删除mouseup
和mousemove
监听器并将它们重新附加到document
(我猜是因为即使在鼠标离开画布后它也想继续监听这些事件)。这意味着即使我们在目标画布上发送事件,fabric.js 也会忽略它们直到moueup
被触发document
。
解决此问题的一种方法是修补 fabric.js 内部,以便能够检测到鼠标事件具有与我们的目标画布不同的 dom 目标,如果发生这种情况,请以合适的方式更正x
/位置。y
我正在使用以下代码进行修补fabric.Canvas.prototype.getPointer
:
// ...
if (e.target !== this.upperCanvasEl) {
var positionOnScene = getPositionOnScene(container, e);
pointer.x = positionOnScene.x;
pointer.y = positionOnScene.y;
}
// ...
(getPositionOnScene()
是计算指针在镜像画布中的位置的辅助方法;container
是 Three.js 用于渲染场景的 dom 元素)。
我已经在您的问题中使用笔中的代码进行了演示:https ://codepen.io/shkaper/pen/eYOBQVL
推荐阅读
- javascript - 取消选中所有输入复选框后删除/添加类
- node.js - Axios 然后回调在成功的 Post 请求上不起作用
- machine-learning - 如何在机器学习中定义标签(one-hot-encoding 除外)
- intellij-idea - 有没有办法知道我最后一次在 IntelliJ 中运行程序是什么时候?
- python-3.x - Python递归调用不更新全局变量
- python - 区分组中的对象
- css - 暂停 CSS 动画 n 秒
- firebase - 如何在 Firestore 中正确访问要用于列表的集合数据
- imagemagick - Imagemagick 弧
- html - 关于不同浏览器显示样式的差异