Can't get Three.js onDocumentMouseDown to work correctly

青春壹個敷衍的年華 提交于 2019-11-28 12:39:37

问题


I've looked at a lot of examples -- and borrowed from some -- and can't seem to get this to work right. What I want is for the raycaster in onDocumentMouseDown to pick up sprites when the user clicks anywhere on the visible surface of a sprite. What I'm getting is a misaligned result, in that a sprite may be picked up if the user clicks somewhat to the right, above, or below the sprite, and will not pick it up at all if the user clicks on the left edge of the sprite. So basically something is misaligned, and I am at a loss for figuring out what I am doing wrong. Any guidance would be appreciated.

<script src="/common/three.js"></script>
<script src="/common/Detector.js"></script>
<script src="/common/CanvasRenderer.js"></script>


<script src="/common/GeometryUtils.js"></script>
<script src="/common/OrbitControls.js"></script>

<div id="WebGLCanvas"></div>



<script>
    var container, scene, camera, renderer, controls;
    var keyboard;

</script>

<script>




    // custom global variables
    var mouse = { x: 0, y: 0 };
    var raycaster;
    var sprites = new Array();
    init();
    try {
        for (i = 0; i < 10; i++) {
            var text = "Text " + i;
            var x = Math.random() * 100;
            var y = Math.random() * 100;
            var z = Math.random() * 100;
            var spritey = addOrUPdateSprite(text, i, x, y, z);
        }

    }
    catch (ex) {
        alert("error when creating sprite: " + ex.message);
    }


    animate();



    function init() {
        try {
            scene = new THREE.Scene();
            // CAMERA
            var cont = document.getElementById("WebGLCanvas");
            var SCREEN_WIDTH = window.innerWidth;
            OFFSET_TOP = document.getElementById("WebGLCanvas").getBoundingClientRect().top;
            var SCREEN_HEIGHT = window.innerHeight - OFFSET_TOP;    //;   //-document.getElementById("upper").clientHeight;
            var VIEW_ANGLE = 60;
            var ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT;
            var NEAR = 0.1;
            var FAR = 1000;
            camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
            scene.add(camera);
            camera.position.set(0, 100, 200);
            camera.lookAt(new THREE.Vector3());
            renderer = new THREE.WebGLRenderer({ antialias: true });
            container = document.getElementById('WebGLCanvas');
            container.appendChild(renderer.domElement);
            renderer.setSize(window.innerWidth, SCREEN_HEIGHT);

            controls = new THREE.OrbitControls(camera, renderer.domElement);
            //                spritey.position.normalize();

            raycaster = new THREE.Raycaster();
            document.addEventListener('mousedown', onDocumentMouseDown, false);
            document.addEventListener('touchstart', onDocumentTouchStart, false);
        }
        catch (ex) {
            alert("error " + ex.message);
        }


    }

    function animate() {
        requestAnimationFrame(animate);
        render();
        update();
    }

    function update() {
        controls.update();
    }

    function render() {
        renderer.render(scene, camera);
    }

    function addOrUPdateSprite(text, name, x, y, z) {
        var sprite = scene.getObjectByName(name);
        if (sprite == null) {
            sprite = makeTextSprite(text, { fontsize: 36, borderColor: { r: 255, g: 0, b: 0, a: 1.0 }, backgroundColor: { r: 255, g: 100, b: 100, a: 0.8 } });
            sprite.name = name;
            sprites.push(sprite);
            scene.add(sprite);
        }

        sprite.position.set(x, y, z);
    }

    function makeTextSprite(message, parameters) {
        if (parameters === undefined) parameters = {};
        var fontface = parameters.hasOwnProperty("fontface") ? parameters["fontface"] : "sans-serif";
        var fontsize = parameters.hasOwnProperty("fontsize") ? parameters["fontsize"] : 36;
        var borderThickness = parameters.hasOwnProperty("borderThickness") ? parameters["borderThickness"] : 1;
        var borderColor = parameters.hasOwnProperty("borderColor") ? parameters["borderColor"] : { r: 0, g: 0, b: 0, a: 1.0 };
        var backgroundColor = parameters.hasOwnProperty("backgroundColor") ? parameters["backgroundColor"] : { r: 255, g: 255, b: 255, a: 1.0 };
        var textColor = parameters.hasOwnProperty("textColor") ? parameters["textColor"] : { r: 0, g: 0, b: 0, a: 1.0 };

        var canvas = document.createElement('canvas');
        var context = canvas.getContext('2d');
        context.font = fontsize + "px " + fontface;
        var metrics = context.measureText(message);
        var textWidth = metrics.width;

        context.fillStyle = "rgba(" + backgroundColor.r + "," + backgroundColor.g + "," + backgroundColor.b + "," + backgroundColor.a + ")";
        context.strokeStyle = "rgba(" + borderColor.r + "," + borderColor.g + "," + borderColor.b + "," + borderColor.a + ")";

        context.lineWidth = borderThickness;
        roundRect(context, borderThickness / 2, borderThickness / 2, (textWidth + borderThickness) * 1.1, fontsize * 1.4 + borderThickness, 8);

        context.fillStyle = "rgba(" + textColor.r + ", " + textColor.g + ", " + textColor.b + ", 1.0)";
        context.fillText(message, borderThickness, fontsize + borderThickness);

        var texture = new THREE.Texture(canvas)
        texture.needsUpdate = true;

        var spriteMaterial = new THREE.SpriteMaterial({ map: texture, useScreenCoordinates: false });
        var sprite = new THREE.Sprite(spriteMaterial);
        sprite.scale.set(1.0 * fontsize, 0.5 * fontsize, 1.5 * fontsize);
        return sprite;


    }

    // function for drawing rounded rectangles
    function roundRect(ctx, x, y, w, h, r) {
        ctx.beginPath();
        ctx.moveTo(x + r, y);
        ctx.lineTo(x + w - r, y);
        ctx.quadraticCurveTo(x + w, y, x + w, y + r);
        ctx.lineTo(x + w, y + h - r);
        ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
        ctx.lineTo(x + r, y + h);
        ctx.quadraticCurveTo(x, y + h, x, y + h - r);
        ctx.lineTo(x, y + r);
        ctx.quadraticCurveTo(x, y, x + r, y);
        ctx.closePath();
        ctx.fill();
        ctx.stroke();
    }



    function onDocumentTouchStart(event) {
        event.preventDefault();

        event.clientX = event.touches[0].clientX;
        event.clientY = event.touches[0].clientY;
        onDocumentMouseDown(event);



    }

    function onDocumentMouseDown(event) {
        mouse.x = (event.clientX / renderer.domElement.clientWidth) * 2 - 1;
        mouse.y = -((event.clientY) / renderer.domElement.clientHeight) * 2 + 1;
        raycaster.setFromCamera(mouse, camera);

        var intersects = raycaster.intersectObjects(sprites, true);

        if (intersects.length > 0) {
            var obj = intersects[0].object;

            alert(obj.name);

            event.preventDefault();
        }


    }




</script>

回答1:


In your makeTextSprite() function, after

var textWidth = metrics.width;

add this

context.strokeStyle = "white";
context.lineWidth = 5;
context.strokeRect(0,0,canvas.width, canvas.height);

and you will see, that your sprites have not the size you think of.

UPD. You can set the size of a canvas like this

var ctxFont = "bold " + fontsize + "px " + fontface;
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
context.font = ctxFont;
var metrics = context.measureText(message);
var textWidth = metrics.width;

canvas.width = textWidth + borderThickness * 2;
canvas.height = fontsize * 1.2 + (borderThickness * 2);
context = canvas.getContext('2d');
context.font = ctxFont;

and then set the scale of a sprite

sprite.scale.set(canvas.width / 10, canvas.height /  10, 1);

jsfiddle example




回答2:


Your Three.js canvas probably isn't at the top left of the screen, and you're not taking into account the offset from 0,0 on the page. To fix it, adjust the mouse position to subtract the offset.

var rect = container.getBoundingClientRect();
mouse.x = ((event.clientX - rect.left) / renderer.domElement.clientWidth) * 2 - 1;
mouse.y = -((event.clientY - rect.top) / renderer.domElement.clientHeight) * 2 + 1;


来源:https://stackoverflow.com/questions/40541390/cant-get-three-js-ondocumentmousedown-to-work-correctly

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!