【问题标题】:How to fit object inside canvas in three js如何在三个js中将对象放入画布内
【发布时间】:2021-08-07 11:44:43
【问题描述】:

我目前正在使用threejs 库来创建 3d 对象,但不幸的是我无法将对象放入画布中。如果对象有点长,它总是溢出画布之外。请在JSFiddle 中查看我的代码。

脚本

import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r127/build/three.module.js'

function generate3DBox(selector, angle, boxDepth, boxWidth, boxHeight) {
    
    let allowedAngles = [
        'standard-0',
        'standard-90',
        'turn-up-0',
        'turn-up-90',
        'turn-side-0',
        'turn-side-90'
    ]
  
    if(allowedAngles.indexOf(angle) < 0) {
        console.log("Angle is incorrect")
        return false
    }

    const canvas = document.querySelector(selector)
    const renderer = new THREE.WebGLRenderer({ canvas, antialias: true })

    const canvasWidth = canvas.getBoundingClientRect().width
    const canvasHeight = canvas.getBoundingClientRect().height
    const minSize = Math.min(...[boxDepth, boxWidth, boxHeight])
    const maxSize = Math.max(...[boxDepth, boxWidth, boxHeight])
  
    const aspect = canvasWidth / canvasHeight
    const fov = 75
    const near = 0.1
    const far = 1000
  
    let cameraZoom = 1
    let cameraPosition = {
        left: canvasWidth / -2,
        right: canvasWidth / 2,
        top: canvasHeight / 2,
        bottom: canvasHeight / -2
    }

    const camera = new THREE.OrthographicCamera(
        cameraPosition.left,
        cameraPosition.right,
        cameraPosition.top,
        cameraPosition.bottom,
        near,
        far
    )

    camera.position.z = maxSize + minSize
    camera.zoom = cameraZoom

    camera.updateProjectionMatrix()

    const scene = new THREE.Scene()
    scene.background = new THREE.Color(0xe0e0e0)

    const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth)

    const material = new THREE.MeshBasicMaterial({ color: 0xff4500 })  // greenish blue

    const cube = new THREE.Mesh(geometry, material)

    var edge = new THREE.EdgesGeometry(cube.geometry)
    var edgeMaterial = new THREE.LineBasicMaterial({ color: 0xffffff, linewidth: 1 })
    var wireframe = new THREE.LineSegments(edge, edgeMaterial)

    scene.add(cube, wireframe)

    function animate() {
        requestAnimationFrame(animate)

        let rotationX = 0
        let rotationY = 0
        let rotationZ = 0

        if(angle == 'standard-0') {
            rotationX = 0.60
            rotationY = -0.80
            rotationZ = 0
        }
        
        if(angle == 'standard-90') {
            rotationX = 0.60
            rotationY = 0.80
            rotationZ = 0
        }
        
        if(angle == 'turn-up-0') {
            rotationX = -1.20
            rotationY = 0
            rotationZ = 0.80
        }
        
        if(angle == 'turn-up-90') {
            rotationX = -1.20
            rotationY = 0
            rotationZ = -0.80
        }

        if(angle == 'turn-side-0') {
            rotationX = 0.60
            rotationY = -0.60
            rotationZ = -1.60
        }
        
        if(angle == 'turn-side-90') {
            rotationX = 0.60
            rotationY = 0.60
            rotationZ = -1.60
        }

        cube.rotation.x = rotationX
        cube.rotation.y = rotationY
        cube.rotation.z = rotationZ
        
        wireframe.rotation.x = rotationX
        wireframe.rotation.y = rotationY
        wireframe.rotation.z = rotationZ

        renderer.render(scene, camera)
    }
  
    animate()
}

generate3DBox('.standard-0', 'standard-0', 50, 45, 65)
generate3DBox('.standard-90', 'standard-90', 50, 45, 65)
generate3DBox('.turn-up-0', 'turn-up-0', 50, 45, 65)
generate3DBox('.turn-up-90', 'turn-up-90', 50, 45, 65)
generate3DBox('.turn-side-0', 'turn-side-0', 50, 45, 65)
generate3DBox('.turn-side-90', 'turn-side-90', 50, 45, 65)

需要输出

【问题讨论】:

标签: javascript three.js


【解决方案1】:

您可以使用一个简单的公式来根据大小设置相机的变焦。这里我使用cameraZoom = 45 / maxSize,它使相机的变焦适应物体的最大尺寸。它设置缩放,因为设置 Z 似乎不起作用。我选择的值45是任意的,您可以根据需要进行更改。

// import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r127/build/three.module.js'

function generate3DBox(selector, angle, boxDepth, boxWidth, boxHeight) {

  let allowedAngles = [
    'standard-0',
    'standard-90',
    'turn-up-0',
    'turn-up-90',
    'turn-side-0',
    'turn-side-90'
  ]

  if (allowedAngles.indexOf(angle) < 0) {
    console.log("Angle is incorrect")
    return false
  }

  const canvas = document.querySelector(selector)
  const renderer = new THREE.WebGLRenderer({
    canvas,
    antialias: true
  })

  const canvasWidth = canvas.getBoundingClientRect().width
  const canvasHeight = canvas.getBoundingClientRect().height
  const minSize = Math.min(...[boxDepth, boxWidth, boxHeight])
  const maxSize = Math.max(...[boxDepth, boxWidth, boxHeight])

  const aspect = canvasWidth / canvasHeight
  const fov = 75
  const near = 0.1
  const far = 1000

  let cameraZoom = 1
  let cameraPosition = {
    left: canvasWidth / -2,
    right: canvasWidth / 2,
    top: canvasHeight / 2,
    bottom: canvasHeight / -2
  }

  const camera = new THREE.OrthographicCamera(
    cameraPosition.left,
    cameraPosition.right,
    cameraPosition.top,
    cameraPosition.bottom,
    near,
    far
  )

  camera.position.z = maxSize + minSize
  // console.log(camera.position)
  cameraZoom = 45 / maxSize
  camera.zoom = cameraZoom

  camera.updateProjectionMatrix()

  const scene = new THREE.Scene()
  scene.background = new THREE.Color(0xe0e0e0)

  const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth)

  const material = new THREE.MeshBasicMaterial({
    color: 0xff4500
  }) // greenish blue

  const cube = new THREE.Mesh(geometry, material)
  //console.log(cube.position.z -= 25)

  var edge = new THREE.EdgesGeometry(cube.geometry)
  var edgeMaterial = new THREE.LineBasicMaterial({
    color: 0xffffff,
    linewidth: 1
  })
  var wireframe = new THREE.LineSegments(edge, edgeMaterial)

  scene.add(cube, wireframe)

  function animate() {
    requestAnimationFrame(animate)

    let rotationX = 0
    let rotationY = 0
    let rotationZ = 0

    if (angle == 'standard-0') {
      rotationX = 0.60
      rotationY = -0.80
      rotationZ = 0
    }

    if (angle == 'standard-90') {
      rotationX = 0.60
      rotationY = 0.80
      rotationZ = 0
    }

    if (angle == 'turn-up-0') {
      rotationX = -1.20
      rotationY = 0
      rotationZ = 0.80
    }

    if (angle == 'turn-up-90') {
      rotationX = -1.20
      rotationY = 0
      rotationZ = -0.80
    }

    if (angle == 'turn-side-0') {
      rotationX = 0.60
      rotationY = -0.60
      rotationZ = -1.60
    }

    if (angle == 'turn-side-90') {
      rotationX = 0.60
      rotationY = 0.60
      rotationZ = -1.60
    }

    cube.rotation.x = rotationX
    cube.rotation.y = rotationY
    cube.rotation.z = rotationZ

    wireframe.rotation.x = rotationX
    wireframe.rotation.y = rotationY
    wireframe.rotation.z = rotationZ

    renderer.render(scene, camera)
  }

  animate()
}
generate3DBox('.standard-0', 'standard-0', 90, 45, 65)
generate3DBox('.standard-90', 'standard-90', 90, 45, 65)
generate3DBox('.turn-up-0', 'turn-up-0', 90, 45, 65)
generate3DBox('.turn-up-90', 'turn-up-90', 90, 45, 65)
generate3DBox('.turn-side-0', 'turn-side-0', 90, 45, 65)
generate3DBox('.turn-side-90', 'turn-side-90', 90, 45, 65)
.flex {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 100px;
  gap: 20px;
}

canvas {
  width: 65px;
  height: 76px;
  border: solid #313131 1px;
  background: url('https://www.pier2pier.com/loadcalc/images/Spinner-1s-64px-gray.gif');
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>ThreeJS with Sonar</title>
  <link rel="icon" href="#">
  <link rel="stylesheet" href="css/style.css">
</head>

<body>
  <div class="flex">
    <canvas class="standard-0"></canvas>
    <canvas class="standard-90"></canvas>

    <canvas class="turn-up-0"></canvas>
    <canvas class="turn-up-90"></canvas>

    <canvas class="turn-side-0"></canvas>
    <canvas class="turn-side-90"></canvas>
  </div>
  <script src="https://threejsfundamentals.org/threejs/resources/threejs/r127/build/three.min.js"></script>
  <script type="module" src="js/main.js"></script>
</body>

</html>

如果您想对所有框进行相同的缩放,第二个 sn-p 显示了一个解决方案。它计算最大框的最大尺寸的缩放,而不是为每个框计算不同的缩放。

// import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r127/build/three.module.js'

function generate3DBox(selector, angle, boxDepth, boxWidth, boxHeight, zoom) {

  let allowedAngles = [
    'standard-0',
    'standard-90',
    'turn-up-0',
    'turn-up-90',
    'turn-side-0',
    'turn-side-90'
  ]

  if (allowedAngles.indexOf(angle) < 0) {
    console.log("Angle is incorrect")
    return false
  }

  const canvas = document.querySelector(selector)
  const renderer = new THREE.WebGLRenderer({
    canvas,
    antialias: true
  })

  const canvasWidth = canvas.getBoundingClientRect().width
  const canvasHeight = canvas.getBoundingClientRect().height
  const minSize = Math.min(...[boxDepth, boxWidth, boxHeight])
  const maxSize = Math.max(...[boxDepth, boxWidth, boxHeight])

  const aspect = canvasWidth / canvasHeight
  const fov = 75
  const near = 0.1
  const far = 1000

  let cameraZoom = 1
  let cameraPosition = {
    left: canvasWidth / -2,
    right: canvasWidth / 2,
    top: canvasHeight / 2,
    bottom: canvasHeight / -2
  }

  const camera = new THREE.OrthographicCamera(
    cameraPosition.left,
    cameraPosition.right,
    cameraPosition.top,
    cameraPosition.bottom,
    near,
    far
  )

  camera.position.z = maxSize + minSize
  // console.log(camera.position)
  cameraZoom = zoom || (45 / maxSize)
  camera.zoom = cameraZoom

  camera.updateProjectionMatrix()

  const scene = new THREE.Scene()
  scene.background = new THREE.Color(0xe0e0e0)

  const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth)

  const material = new THREE.MeshBasicMaterial({
    color: 0xff4500
  }) // greenish blue

  const cube = new THREE.Mesh(geometry, material)
  //console.log(cube.position.z -= 25)

  var edge = new THREE.EdgesGeometry(cube.geometry)
  var edgeMaterial = new THREE.LineBasicMaterial({
    color: 0xffffff,
    linewidth: 1
  })
  var wireframe = new THREE.LineSegments(edge, edgeMaterial)

  scene.add(cube, wireframe)

  function animate() {
    requestAnimationFrame(animate)

    let rotationX = 0
    let rotationY = 0
    let rotationZ = 0

    if (angle == 'standard-0') {
      rotationX = 0.60
      rotationY = -0.80
      rotationZ = 0
    }

    if (angle == 'standard-90') {
      rotationX = 0.60
      rotationY = 0.80
      rotationZ = 0
    }

    if (angle == 'turn-up-0') {
      rotationX = -1.20
      rotationY = 0
      rotationZ = 0.80
    }

    if (angle == 'turn-up-90') {
      rotationX = -1.20
      rotationY = 0
      rotationZ = -0.80
    }

    if (angle == 'turn-side-0') {
      rotationX = 0.60
      rotationY = -0.60
      rotationZ = -1.60
    }

    if (angle == 'turn-side-90') {
      rotationX = 0.60
      rotationY = 0.60
      rotationZ = -1.60
    }

    cube.rotation.x = rotationX
    cube.rotation.y = rotationY
    cube.rotation.z = rotationZ

    wireframe.rotation.x = rotationX
    wireframe.rotation.y = rotationY
    wireframe.rotation.z = rotationZ

    renderer.render(scene, camera)
  }

  animate()
}
var boxes = [['.standard-0', 'standard-0', 90, 45, 65],['.standard-90', 'standard-90', 90, 45, 65], ['.turn-up-0', 'turn-up-0', 90, 45, 65], ['.turn-up-90', 'turn-up-90', 90, 45, 65], ['.turn-side-0', 'turn-side-0', 90, 45, 65], ['.turn-side-90', 'turn-side-90', 130, 45, 65]];
var maxValue = Math.max(...boxes.map(([cls, angle, ...dimensions]) => Math.max(...dimensions)));
for (let i = 0; i < boxes.length; i++) {
    generate3DBox(...boxes[i], 45 / maxValue);
}
.flex {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 100px;
  gap: 20px;
}

canvas {
  width: 65px;
  height: 76px;
  border: solid #313131 1px;
  background: url('https://www.pier2pier.com/loadcalc/images/Spinner-1s-64px-gray.gif');
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>ThreeJS with Sonar</title>
  <link rel="icon" href="#">
  <link rel="stylesheet" href="css/style.css">
</head>

<body>
  <div class="flex">
    <canvas class="standard-0"></canvas>
    <canvas class="standard-90"></canvas>

    <canvas class="turn-up-0"></canvas>
    <canvas class="turn-up-90"></canvas>

    <canvas class="turn-side-0"></canvas>
    <canvas class="turn-side-90"></canvas>
  </div>
  <script src="https://threejsfundamentals.org/threejs/resources/threejs/r127/build/three.min.js"></script>
  <script type="module" src="js/main.js"></script>
</body>

</html>

【讨论】:

  • 这很好,但是如果您在下面测试这些值,则缩放不相等: generate3DBox('.standard-0', 'standard-0', 65, 5, 5) generate3DBox(' .standard-90', 'standard-90', 65, 5, 5) generate3DBox('.turn-up-0', 'turn-up-0', 90, 45, 65) generate3DBox('.turn-up -90', 'turn-up-90', 90, 45, 605) generate3DBox('.turn-side-0', 'turn-side-0', 90, 45, 65) generate3DBox('.turn-side -90', 'turn-side-90', 0.10, 0.10, 0.10)
  • @Jonjie 你想让六个盒子的缩放比例相等吗?
  • @Jonjie 我添加了一个 sn-p,其中所有框都具有相同的缩放比例
  • 不,你的第一个答案已经很好了,但是如果你像我上面指出的值那样更改值,一些框会占用 99% 的框。你可以看看这个示例输出:i.imgur.com/UwacMCB.png
猜你喜欢
  • 2017-10-17
  • 2021-07-23
  • 2015-09-20
  • 1970-01-01
  • 2023-03-07
  • 2020-03-02
  • 2012-09-06
  • 2021-08-13
  • 1970-01-01
相关资源
最近更新 更多