按坐标 x, y fabric js 发出点击事件
Emit click events by coords x, y fabric js
我开始使用 three.js 和 fabric.js 开发项目,我可以在其中移动和转换 3d 模型上的纹理。我在模型上找到坐标并将其与 canvas fabric js 上的坐标同步。但我不知道如何模拟点击事件并完全管理这个点击的对象。我找到了证明我的问题的 Whosebug 主题。非常感谢!
Programmatically select object in a Fabricjs canvas from coords
Raycast mouse clicks from Threejs model to the Fabricjs canvas used for the texture
Emit click events from one canvas to another
代码笔:
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 );
};
请注意,这段代码不是我的,我只是在 Whosebug 上找到的,不声明作者身份
旧问题的一些答案提到了将鼠标事件从一个 canvas 镜像到另一个的正确方法:
var simEvt = new MouseEvent(evt.type, {
clientX: correctedPosition.x,
clientY: correctedPosition.y
});
canvas.upperCanvasEl.dispatchEvent(simEvt);
不幸的是,仅在目标 canvas 上调度 mousedown
、mouseup
和 mousemove
事件是不够的。原因是当 mousedown
事件在 canvas 上触发时,fabric.js 立即删除 mouseup
和 mousemove
侦听器并将它们重新附加到 document
(我猜是因为它想在鼠标离开 canvas 之后继续收听这些事件。这意味着即使我们在目标 canvas 上调度事件,fabric.js 也会忽略它们直到 moueup
在 document
.
上触发
解决此问题的一种方法是修补 fabric.js 内部机制,以便能够检测到鼠标事件具有与我们的目标 canvas 不同的 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()
是一种辅助方法,用于计算指针在镜像 canvas 中的位置;container
是 dom 元素 Three.js 用于渲染一个场景)。
我已经使用您问题中笔中的代码进行了演示:https://codepen.io/shkaper/pen/eYOBQVL
我开始使用 three.js 和 fabric.js 开发项目,我可以在其中移动和转换 3d 模型上的纹理。我在模型上找到坐标并将其与 canvas fabric js 上的坐标同步。但我不知道如何模拟点击事件并完全管理这个点击的对象。我找到了证明我的问题的 Whosebug 主题。非常感谢!
Programmatically select object in a Fabricjs canvas from coords
Raycast mouse clicks from Threejs model to the Fabricjs canvas used for the texture
Emit click events from one canvas to another
代码笔:
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 );
};
请注意,这段代码不是我的,我只是在 Whosebug 上找到的,不声明作者身份
旧问题的一些答案提到了将鼠标事件从一个 canvas 镜像到另一个的正确方法:
var simEvt = new MouseEvent(evt.type, {
clientX: correctedPosition.x,
clientY: correctedPosition.y
});
canvas.upperCanvasEl.dispatchEvent(simEvt);
不幸的是,仅在目标 canvas 上调度 mousedown
、mouseup
和 mousemove
事件是不够的。原因是当 mousedown
事件在 canvas 上触发时,fabric.js 立即删除 mouseup
和 mousemove
侦听器并将它们重新附加到 document
(我猜是因为它想在鼠标离开 canvas 之后继续收听这些事件。这意味着即使我们在目标 canvas 上调度事件,fabric.js 也会忽略它们直到 moueup
在 document
.
解决此问题的一种方法是修补 fabric.js 内部机制,以便能够检测到鼠标事件具有与我们的目标 canvas 不同的 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()
是一种辅助方法,用于计算指针在镜像 canvas 中的位置;container
是 dom 元素 Three.js 用于渲染一个场景)。
我已经使用您问题中笔中的代码进行了演示:https://codepen.io/shkaper/pen/eYOBQVL