Canvas/model zooms in on scroll for mobile

Hello everyone,

On mobile, the model or canvas zooms in on scroll - https://voltmeup2022.webflow.io/
I’m not sure why, can anyone help?

Thank you.

Can anyone help me please?

Quite difficult to understand what is going on without seeing the specific code. But maybe you could try adding a media query with touch-action: none to the canvas?

It essentially makes your canvas non interactive. You can also try touch-action: pan-x; which should keep horizontal interactions and remove vertical ones.

Hello @jacopocolo,

Thank you for your reply. I already tried touch-action: none on the canvas previously but it didn’t work. Here’s my code:

import * as THREE from 'three'
import OrbitControls from 'https://cdn.skypack.dev/threejs-orbit-controls'
import { GLTFLoader } from 'https://cdn.skypack.dev/three/examples/jsm/loaders/GLTFLoader'
import GUI from 'https://cdn.jsdelivr.net/npm/lil-gui@0.16.1/dist/lil-gui.esm.min.js'

//Scene
const scene = new THREE.Scene()

//Canvas
const canvas = document.querySelector('.webgl')

//cameraPosition
const cameraPosition = {}
installMediaQueryWatcher('(min-width: 1300px)', (matches) => {
  if(matches){
		cameraPosition.x = 6,
    cameraPosition.y = 0,
    cameraPosition.z = 11.3
  }else{
		cameraPosition.x = 15,
    cameraPosition.y = 0,
    cameraPosition.z = 15
  }
})

//Lights
const ambientLight = new THREE.AmbientLight('#736757', 1.60)
scene.add(ambientLight)

const directionalLight = new THREE.DirectionalLight('#d3b892', 3.38)
scene.add(directionalLight)

//Sizes
const sizes = {
  width: window.innerWidth / 2,
  height: window.innerHeight
}

//Camera
const camera = new THREE.PerspectiveCamera(75, sizes.width/sizes.height)
scene.add(camera)

camera.position.set(20, 0, 25.3)

//Controls 
const controls = new OrbitControls(camera, canvas)
controls.enableDamping = true
controls.dampingFactor = 0.05
controls.enableZoom = false
controls.enablePan = false
controls.rotateSpeed = 0.02
controls.maxPolarAngle = Math.PI * 0.5
controls.minPolarAngle = 1
controls.maxAzimuthAngle = Math.PI * 0.5
controls.minAzimuthAngle = 0

controls.addEventListener('end', () => {
	const radius = camera.position.distanceTo(controls.target)
	gsap.to(camera.position, {
    x: cameraPosition.x,
    y: cameraPosition.y,
    z: cameraPosition.z,
    onUpdate: () => {
      camera.lookAt(controls.target)
      //move camera in a circular path
      camera.position.normalize().multiplyScalar(radius)
    }
  })
})

//Loading manager
const manager = new THREE.LoadingManager()
const percentage = document.querySelector('#percentage')
const progressBar = document.querySelector('.loader-progress-bar')

const loaderTimeline = gsap.timeline()
manager.onLoad = () => {
  loaderTimeline.to('.page-loader-top', {yPercent: -100, opacity: 0, duration: 1}, 'content')
	loaderTimeline.to('.page-loader-bottom', {yPercent: 100, opacity: 0, duration: 1}, 'content')
 	loaderTimeline.to('.loader-overlay', {
  	opacity: 0,
    duration: 1
  }, 'content')
  loaderTimeline.to('.page-loader', {display: 'none'})
  gsap.to(camera.position, {
    x: cameraPosition.x, y: cameraPosition.y, z: cameraPosition.z, delay: 1, duration: 2
  })
}

manager.onProgress = ( url, itemsLoaded, itemsTotal ) => {
	percentage.innerHTML = itemsLoaded / itemsTotal * 100
	progressBar.style.transform = `scaleX(${itemsLoaded/itemsTotal})`
}

//Models
const gltfLoader = new GLTFLoader( manager )
let model

gltfLoader.load('https://volt-three-js.netlify.app/models/volt building.glb', (gltf) => {
  model = gltf.scene
  const box = new THREE.Box3().setFromObject( model )
  const size = box.getSize( new THREE.Vector3() )
  const centerPosition = box.getCenter( new THREE.Vector3() )
  model.position.x += ( model.position.x - centerPosition.x )
  model.position.y += ( model.position.y - centerPosition.y )
  model.position.z += ( model.position.z - centerPosition.z )  
	scene.add(model)
})

//Renderer
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true })

//Resizing
window.addEventListener('resize', () => {
  //Update the sizes object
  sizes.width = window.innerWidth / 2
  sizes.height = window.innerHeight

  //Update camera
  camera.aspect = sizes.width / sizes.height
  camera.updateProjectionMatrix()

  //Update the renderer
  renderer.setSize(sizes.width, sizes.height)
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})

renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
renderer.render(scene, camera)
renderer.setClearColor('#1f1f1f', 0)
renderer.physicallyCorrectLights = true
renderer.outputEncoding = THREE.sRGBEncoding
renderer.toneMapping = THREE.LinearToneMapping
renderer.toneMappingExposure = 1

//Animate
const clock = new THREE.Clock()
const tick = () => {
  const elapsedTime = clock.getElapsedTime()

  //Update controls
  controls.update()

  //Render
  renderer.render(scene, camera)

  window.requestAnimationFrame(tick)
}

tick()

Or could it be because the canvas is not fixed? It’s in a grid system in the hero section, it’s absolute to its parent, and is positioned to the right.

I was using gsap scrollsmoother for smooth scrolling on the site. The issue with the zooming of the model on scroll was due to the fact that the address bar is hidden on scroll on mobile, and this resizes the viewport.

So I added the option normalizeScroll: true when initializing the gsap scrollsmoother, and this prevents the address bar from resizing.

Thank you for your contribution @jacopocolo