【问题标题】:Three.js don't strecht mesh texture (image) - make it cover its containerThree.js 不拉伸网格纹理(图像) - 使其覆盖其容器
【发布时间】:2020-08-01 01:35:44
【问题描述】:

我有一个容器,我使用 three.js 和网格将图像应用到该容器。

这就是我将网格应用于场景的方式:

this.$els = {
    el: el,
    image: el.querySelector('.ch__image') <-- size of container image is being applied to
};

this.loader = new THREE.TextureLoader();
this.image = this.loader.load(this.$els.image.dataset.src);
this.sizes = new THREE.Vector2(0, 0);
this.offset = new THREE.Vector2(0, 0);

getSizes() {
    const { width, height, top, left } = this.$els.image.getBoundingClientRect();

    this.sizes.set(width, height);
    this.offset.set(left - window.innerWidth / 2 + width / 2, -top + window.innerHeight / 2 - height / 2)
}

createMesh() {
    this.geometry = new THREE.PlaneBufferGeometry(1, 1, 1, 1);
    this.material = new THREE.MeshBasicMaterial({
        map: this.image
    });

    this.mesh = new THREE.Mesh(this.geometry, this.material);

    this.mesh.position.set(this.offset.x, this.offset.y, 0);
    this.mesh.scale.set(this.sizes.x, this.sizes.y, 1);

    this.scene.add(this.mesh)
}

目前正在拉伸图像/纹理以适应容器 - 我怎样才能使行为像 object-fit: coverbackground-size: cover

【问题讨论】:

    标签: javascript css three.js


    【解决方案1】:

    我创建了一个 Canvas 元素,并通过 CanvasContext.drwaImage 编辑图像的大小,然后制作一个 CanvasTexture。有效

    import * as THREE from 'three';
    
    function createCoverTexture(
      url: string,
      width: number,
      height: number
    ): Promise<THREE.CanvasTexture> {
      return new Promise((resolve, reject) => {
        const aspect = width / height;
    
        const image = new Image();
        image.crossOrigin = 'anonymous';
    
        image.onload = () => {
          const [sw, sh, left, top] =
            image.height < image.width / aspect
              ? [
                  image.height * aspect,
                  image.height,
                  image.width / 2 - (image.height * aspect) / 2,
                  0,
                ]
              : [
                  image.width,
                  image.width / aspect,
                  0,
                  image.height / 2 - image.width / aspect / 2,
                ];
    
          const canvas = document.createElement('canvas');
          const ctx = canvas.getContext('2d');
          canvas.width = sw;
          canvas.height = sh;
    
          ctx?.drawImage(image, left, top, sw, sh, 0, 0, sw, sh);
    
          resolve(new THREE.CanvasTexture(canvas));
        };
    
        image.onerror = reject;
    
        image.src = url;
      });
    }
    

    【讨论】:

      【解决方案2】:

      尝试使用此代码 sn-p 中提供的 cover() 函数。这个想法是使用容器和图像的方面来实现类似的结果,如background-size: cover

      var camera, scene, renderer;
      
      var texture;
      
      init();
      animate();
      
      function init() {
      
          camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 10 );
          camera.position.z = 1;
      
          scene = new THREE.Scene();
      
          texture = new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/crate.gif', () => {
          
            cover( texture, window.innerWidth / window.innerHeight );
          
            scene.background = texture;
          
          } );
          texture.matrixAutoUpdate = false;
      
          renderer = new THREE.WebGLRenderer( { antialias: true } );
          renderer.setSize( window.innerWidth, window.innerHeight );
          document.body.appendChild( renderer.domElement );
          
          window.addEventListener( 'resize', onWindowResize, false );
      
      }
      
      function onWindowResize() {
      
        var aspect = window.innerWidth / window.innerHeight;
      
        camera.aspect = aspect;
        camera.updateProjectionMatrix();
        
        cover( texture, aspect );
      
        renderer.setSize( window.innerWidth, window.innerHeight );
      
      }
      
      function cover( texture, aspect ) {
      
        var imageAspect = texture.image.width / texture.image.height;
      
        if ( aspect < imageAspect ) {
      
            texture.matrix.setUvTransform( 0, 0, aspect / imageAspect, 1, 0, 0.5, 0.5 );
      
        } else {
      
            texture.matrix.setUvTransform( 0, 0, 1, imageAspect / aspect, 0, 0.5, 0.5 );
      
        }
      
      }
      
      function animate() {
      
          requestAnimationFrame( animate );
          renderer.render( scene, camera );
      
      }
      body {
        margin: 0;
       }
       canvas {
        display: block;
       }
      &lt;script src="https://cdn.jsdelivr.net/npm/three@0.115/build/three.js"&gt;&lt;/script&gt;

      顺便说一句:不要使用window.innerWidthwindow.innerHeight,而是使用容器的尺寸。

      【讨论】:

      • 谢谢,但不幸的是我遇到了一个问题:我有多个必须覆盖的纹理。当它们中的每一个都在视口内时,它可以正常工作。但是有时,页面被加载并且最初一些纹理已经从视口中转换出来 - 在这些情况下,功能覆盖不起作用 - 之后再次调用它 f.e.通过调整大小,它们被成功覆盖。但是如何才能成功调用这些函数,即使它们在视口之外?
      • 嗯,我不确定我是否完全理解。但是如果由于某些原因 DOM 还没有准备好,计算可能会产生错误的结果。这意味着您只能在 DOM 实际准备好时调用 cover()
      • 我在 load 承诺中调用它,就像您所做的那样 - 问题是某些纹理在开始时不可见,它们在开始时被加载,但不在视口内。不知何故,cover() 无法处理这些图像。
      • 你说这些纹理至少是加载的,对吧?如果您登录 texture.image.widthtexture.image.height,您会在浏览器控制台中看到什么。
      • 我看到了它的高度和宽度,没有什么可疑的,只有一件事 - 对于某些图像(可能是扭曲的图像),我收到以下警告:THREE.WebGLRenderer: Texture has been resized from (1044x1566) to (1024x1024)(尺寸不同)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-04-15
      • 2013-03-26
      • 2018-05-13
      • 2015-01-26
      • 2013-10-07
      • 2014-12-08
      • 2014-01-25
      相关资源
      最近更新 更多