React + Threejs + Swiper complete code to achieve panoramic effect

React + Threejs + Swiper complete code to achieve panoramic effect

Let’s take a look at the panoramic view effect: Display address
screenshot:

Panorama

After experiencing it, do you feel that the surrounding environment has rotated in a circle and that the world is round? 😁
That’s right! Congratulations on getting it right! The earth is round! 👀

Panoramic effect realization

With the above tips, friends who have a little knowledge of threejs may have guessed that this panoramic effect is actually achieved using a sphere~ and we just pasted a texture map on the inner surface of the sphere (you can see this sphere by rolling the wheel outwards. It looks like a glass ball, which is very beautiful. There is also an easter egg 😁 (well, it’s not an easter egg if you say it out)):

insert image description here

Initially, our perspective is at the center of the sphere, and the movement of the perspective is controlled by the OrbitControls tool provided by threejs.

Then the code to create this sphere is as follows:

const geometry = new THREE.SphereBufferGeometry(500, 32, 32);
geometry.scale(-1, 1, 1); // Reverse the texture const material = new THREE.MeshBasicMaterial({
    map: new THREE.TextureLoader().load(imglist[0].default) // Pass in the URL or path of the image, or a Data URI.
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

const controls = new OrbitControls(camera, renderer.domElement);
controls.enablePan = false;
controls.maxDistance = 1000;

If you don't know what Data URI is, you can check out the MDN documentation

Carousel

The carousel is implemented using the swiper library, which is very convenient to use. You can refer to the documentation for details.
When sliding the carousel, an onSliderChange event will be triggered. This event passes the current swiper as a parameter. We can get the image through the currently activated element and replace the texture map of the sphere:

onSliderChange = curSwiper => {
    const mesh = this.mesh;
    const texture = imglist[curSwiper.activeIndex].default;
    mesh.material.map = new THREE.TextureLoader().load(texture);
};

Below is my swiper settings, where SwiperSlider is a slidable carousel card, EffectCoverflow is the effect triggered when sliding, and swiper provides four optional effects: Fade, Coverflow, Flip and Cube. imglist is a group of images, where imglist[i].default attribute stores the base64 encoding of the image.

import { Swiper, SwiperSlide } from 'swiper/react';
import SwiperCore, { EffectCoverflow } from 'swiper';
import 'swiper/swiper.min.css';
import 'swiper/components/effect-coverflow/effect-coverflow.min.css';

SwiperCore.use([EffectCoverflow]);

//....
<Swiper
    className='panoramic-imgs'
    spaceBetween={50} // Spacing slidesPerView={3} // Number of images that can be previewed in the slideshow onSlideChange={this.onSliderChange} // Callback triggered when sliding onSwiper={(swiper) => console.log(swiper)} // Callback triggered on initial load direction='vertical' // Slideshow direction, horizontal by default
    effect={'coverflow'} // Slide effect grabCursor={true} // Whether to display drag when the mouse is placed on the carousel centeredSlides={true} // Whether the currently active image should be centered coverflowEffect={{ // Coverflow effect parameter settings, you can adjust it yourself "rotate": 50,
        "stretch": 0,
        "depth": 100,
        "modifier": 1,
        "slideShadows": true
    }}
    {
        imglist.map((img, idx) => {
            return <SwiperSlide key={idx}>
                <img src={img.default} className='panoramic-img'></img>
            </SwiperSlide>
        })
    }
</Swiper>

That's all about the implementation of the panoramic effect. Of course, if you have any questions, you can leave a message or refer to my code (posted below). As long as you have a certain understanding of threejs and react, I believe it is not difficult to achieve such an effect, and the amount of code is also very small~

Complete code

import React, { Component } from 'react';

import Layout from '@theme/Layout';
import Head from '@docusaurus/Head';

import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import * as _ from 'underscore';
import { message } from 'antd';

import { Swiper, SwiperSlide } from 'swiper/react';
import SwiperCore, { EffectCoverflow } from 'swiper';
import 'swiper/swiper.min.css';
import 'swiper/components/effect-coverflow/effect-coverflow.min.css';

import './index.css';
import imgs from './imgs.json';

SwiperCore.use([EffectCoverflow]);

const imglist = imgs.map(img => {
    return require('../../../static/img/panoramic/' + img.name);
});

export default class Panormatic extends Component {
    constructor() {
        super();
        this.renderer = null;
        this.camera = null;
        this.scene = null;
        this.container = null;
        this.controls = null;
        this.showMessage = true; // Easter egg prompt}

    componentDidMount() {
        const container = document.getElementById('panoramic-canvas-container');
        const canvas = document.getElementById('panoramic-canvas');
        const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });

        renderer.setClearColor(0xffffff); // b2e0df mung bean paste colorrenderer.setPixelRatio( window.devicePixelRatio );
        const height = container.clientHeight;
        const width = container.clientWidth;
        renderer.setSize(width, height);
        
        const camera = new THREE.PerspectiveCamera(60, width / height, 1, 30000);
        camera.position.set(0, 0, 1);
        camera.center = new THREE.Vector3(0, 0, 0);

        const scene = new THREE.Scene();

        const geometry = new THREE.SphereBufferGeometry(500, 32, 32);
        geometry.scale(-1, 1, 1); // Reverse the texture const material = new THREE.MeshBasicMaterial({
            map: new THREE.TextureLoader().load(imglist[0].default)
        });
        const mesh = new THREE.Mesh(geometry, material);
        scene.add(mesh);

        const controls = new OrbitControls(camera, renderer.domElement);
        // controls.enableZoom = false;
        controls.enablePan = false;
        controls.maxDistance = 1000;

        this.renderer = renderer;
        this.camera = camera;
        this.scene = scene;
        this.container = container;
        this.controls = controls;
        this.mesh = mesh;

        // Set the global configuration of the prompt box message.config({
            top: 100,
            duration: 3.5,
            maxCount: 1,
        });

        this.onControlsChange = _.throttle(this.onChange, 100);
        controls.addEventListener('change', this.onControlsChange);
        window.addEventListener('resize', this.onWindowResize);
        this.renderLoop();
    }

    componentWillUnmount() {
        const mesh = this.mesh;
        mesh.material.dispose();
        mesh.geometry.dispose();
        this.scene.remove(mesh);
        window.removeEventListener('resize', this.onWindowResize);
        this.controls.removeEventListener('change', this.onControlsChange);
        message.destroy();
    }

    onChange = (e) => {
        const camera = this.camera;
        if (camera.position.distanceTo(camera.center) >= 700) {
            if (this.showMessage) {
                message.success('🎊Congratulations on discovering the little secret of panoramic effect~🎉');
                this.showMessage = false;
            }
        } else {
            this.showMessage = true;
        }
    }

    onSliderChange = (curSwiper) => {
        const mesh = this.mesh;
        const texture = imglist[curSwiper.activeIndex].default;
        mesh.material.map = new THREE.TextureLoader().load(texture);
    };

    onWindowResize = () => {
        const camera = this.camera;
        const renderer = this.renderer;
        const width = this.container.clientWidth;
        const height = this.container.clientHeight;
        
        camera.aspect = width / height;
        camera.updateProjectionMatrix();
        
        renderer.setSize(width, height);
    };

    renderLoop = () => {
        this.renderer.render(this.scene, this.camera);
        requestAnimationFrame(this.renderLoop);
    };

    render() {
        return (
            <Layout>
                <Head>
                    <title>Panorama | Yle</title>
                </Head>
                <div id='panoramic-container'>
                    <Swiper
                        className='panoramic-imgs'
                        spaceBetween={50}
                        slidesPerView={3}
                        onSlideChange={this.onSliderChange}
                        onSwiper={(swiper) => console.log(swiper)}
                        direction='vertical'
                        effect={'coverflow'}
                        grabCursor={true}
                        centeredSlides={true}
                        coverflowEffect={{
                            "rotate": 50,
                            "stretch": 0,
                            "depth": 100,
                            "modifier": 1,
                            "slideShadows": true
                        }}
                    >
                        {
                            imglist.map((img, idx) => {
                                return <SwiperSlide key={idx}>
                                    <img src={img.default} className='panoramic-img'></img>
                                </SwiperSlide>
                            })
                        }
                    </Swiper>
                    <div id='panoramic-canvas-container'>
                        <canvas id='panoramic-canvas'></canvas>
                    </div>
                </div>
                
                
            </Layout>
        );
    }
}

This is the end of this article about the complete code of React + Threejs + Swiper to achieve panoramic effect. For more relevant React panorama content, please search 123WORDPRESS.COM's previous articles or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • A complete example of using three.js to realize panorama in Vue
  • Realizing 360-degree panoramic images based on Three.js
  • Create 360-degree panorama based on Three.js plug-in
  • THREE.JS Getting Started Tutorial (6) Steps to create your own panorama
  • JS realizes 360 degree rotation of panoramic image effect

<<:  How to solve the problem that VMware virtual machine bridge mode cannot access the Internet

>>:  Detailed explanation of the 10061 unknown error when using Navicat to connect to a remote Linux MySQL database

Recommend

How to clean up Alibaba Cloud MySQL space

Today I received a disk warning notification from...

How to implement Mysql switching data storage directory

How to implement Mysql switching data storage dir...

30 free high-quality English ribbon fonts

30 free high-quality English ribbon fonts for down...

Summary of Common Terms in CSS (Cascading Style Sheet)

If you use CSS don't forget to write DOCTYPE, ...

Web skills: Multiple IE versions coexistence solution IETester

My recommendation Solution for coexistence of mul...

Docker-compose one-click deployment of gitlab Chinese version method steps

1. Introduction to gitlab Gitlab official address...

Monitor changes in MySQL table content and enable MySQL binlog

Preface binlog is a binary log file, which record...

Detailed explanation of pipeline and valve in tomcat pipeline mode

Preface In a relatively complex large system, if ...

JS implements the sample code of decimal conversion to hexadecimal

Preface When we write code, we occasionally encou...

MySQL Optimization Summary - Total Number of Query Entries

1. COUNT(*) and COUNT(COL) COUNT(*) usually perfo...

7 useful new TypeScript features

Table of contents 1. Optional Chaining 2. Null va...

HTML+CSS project development experience summary (recommended)

I haven’t updated my blog for several days. I jus...

Basic understanding and use of HTML select option

Detailed explanation of HTML (select option) in ja...

The latest graphic tutorial of mysql 8.0.16 winx64 installation under win10

In order to download this database, it takes a lo...