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

In-depth analysis of the role of HTML <!--...--> comment tags

When we check the source code of many websites, w...

Zabbix monitors mysql instance method

1. Monitoring planning Before creating a monitori...

Detailed example of MySQL (5.6 and below) parsing JSON

MySQL (5.6 and below) parses json #json parsing f...

Detailed explanation of Vue project optimization and packaging

Table of contents Preface 1. Routing lazy loading...

Linux kernel device driver kernel time management notes

/****************** * Linux kernel time managemen...

Analysis of statement execution order of sql and MySQL

I encountered a problem today: Can I use the as a...

A brief analysis of the differences between undo, redo and binlog in MySQL

Table of contents Preface 【undo log】 【redo log】 【...

How to use squid to build a proxy server for http and https

When we introduced nginx, we also used nginx to s...

MySQL detailed explanation of isolation level operation process (cmd)

Read uncommitted example operation process - Read...

Upgrading Windows Server 2008R2 File Server to Windows Server 2016

The user organization has two Windows Server 2008...

Detailed explanation of MySQL injection without knowing the column name

Preface I feel like my mind is empty lately, as I...

SQL fuzzy query report: ORA-00909: invalid number of parameters solution

When using Oracle database for fuzzy query, The c...

Web Design Tutorial (6): Keep your passion for design

<br />Previous article: Web Design Tutorial ...

A brief discussion on group by in MySQL

Table of contents 1. Introduction 2. Prepare the ...