最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

React + Threejs + Swiper 實(shí)現(xiàn)全景圖效果的完整代碼

 更新時間:2021年06月28日 11:42:57   作者:yleavesw  
全景圖效果非常漂亮給人帶來極好的用戶體驗(yàn)效果,那么基于前端開發(fā)如何實(shí)現(xiàn)這種效果呢,下面小編給大家?guī)砹薘eact + Threejs + Swiper 實(shí)現(xiàn)全景圖效果,感興趣的朋友一起看看吧

  咱先看看全景圖實(shí)現(xiàn)效果:展示地址
  截圖:

全景圖

  體驗(yàn)了一下是不是感覺周圍環(huán)境轉(zhuǎn)了一圈,感覺世界是圓的?😁
  沒錯!恭喜你答對了!地球就是圓的!👀

全景效果實(shí)現(xiàn)

  有了上面的提示,對 threejs 有一點(diǎn)了解的小伙伴可能就猜出來了,這個全景效果其實(shí)就是使用一個球體實(shí)現(xiàn)的~ 而我們只是在球體內(nèi)表面上貼了一張紋理貼圖而已(滾輪向外滾就可以看到這個球體了,看上去像個玻璃球,怪好看的,還有個彩蛋😁(好吧,說出來就不是彩蛋了)):

在這里插入圖片描述

  初始時,我們的視角在球體正中心,視角的移動則是依靠 threejs 提供的工具 OrbitControls 來控制。

  那么創(chuàng)建這個球體的代碼如下:

const geometry = new THREE.SphereBufferGeometry(500, 32, 32);
geometry.scale(-1, 1, 1);   // 將紋理反貼
const material = new THREE.MeshBasicMaterial({
    map: new THREE.TextureLoader().load(imglist[0].default)	// 傳入圖片的URL或者路徑,也可以是 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;

不知道 Data URI 是什么的可以看看 MDN 文檔

輪播圖

  輪播圖實(shí)現(xiàn)則是使用 swiper 這個庫,使用起來非常方便,具體可自行查閱文檔。
  在滑動輪播圖時,會觸發(fā)一個 onSliderChange 事件,這個事件傳入當(dāng)前的 swiper 作為參數(shù),我們就可以通過當(dāng)前激活的元素來獲取圖片并替換球體的紋理貼圖了:

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

  下面是我的 swiper 設(shè)置,其中 SwiperSlider 是一個可滑動的輪播圖卡片,EffectCoverflow 是滑動時觸發(fā)的效果,swiper 中提供了四種可選效果:Fade、Coverflow、Flip 以及 Cube。imglist 則是一組圖片,其中 imglist[i].default 屬性保存了圖片的 base64 編碼。

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}	// 間距
    slidesPerView={3}	// 輪播圖里可預(yù)覽圖片數(shù)
    onSlideChange={this.onSliderChange}	// 滑動時觸發(fā)的回調(diào)
    onSwiper={(swiper) => console.log(swiper)}	// 初始加載時觸發(fā)的回調(diào)
    direction='vertical'	// 輪播圖方向,默認(rèn)是水平 horizontal
    effect={'coverflow'}	// 滑動效果
    grabCursor={true}	// 鼠標(biāo)放在輪播圖上是否顯示拖拽
    centeredSlides={true}	// 當(dāng)前處于激活狀態(tài)的圖片是否要居中
    coverflowEffect={{	// coverflow 效果參數(shù)設(shè)置,可自行調(diào)整
        "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>

  全景效果的實(shí)現(xiàn)就說到這了,當(dāng)然,如果什么地方有疑問可以留言或者參考我的代碼(下面貼出來),只要對 threejs 和 react 有一定了解的同學(xué)我相信實(shí)現(xiàn)這么一個效果并不難,代碼量也很小~

完整代碼

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;    // 彩蛋提示
    }

    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 綠豆沙色
        renderer.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);   // 將紋理反貼
        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;

        // 設(shè)置提示框的全局配置
        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('🎊恭喜你發(fā)現(xiàn)了全景效果的小秘密~🎉');
                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>全景圖 | 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>
        );
    }
}

到此這篇關(guān)于React + Threejs + Swiper 實(shí)現(xiàn)全景圖效果的完整代碼的文章就介紹到這了,更多相關(guān)React全景圖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • React中useState的理解和使用案例

    React中useState的理解和使用案例

    Hook是React16.8的新增特性,它可以讓你在不編寫class的情況下使用state以及其他的React特性,本文中講解的useState就是React中的其中一個Hook,這篇文章主要給大家介紹了關(guān)于React中useState理解和使用的相關(guān)資料,需要的朋友可以參考下
    2024-03-03
  • Iconfont不能上傳如何維護(hù)Icon

    Iconfont不能上傳如何維護(hù)Icon

    這篇文章主要為大家介紹了在Iconfont還是不能上傳,要如何維護(hù)你的Icon,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • 通過實(shí)例學(xué)習(xí)React中事件節(jié)流防抖

    通過實(shí)例學(xué)習(xí)React中事件節(jié)流防抖

    這篇文章主要介紹了通過實(shí)例學(xué)習(xí)React中事件節(jié)流防抖,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,,需要的朋友可以參考下
    2019-06-06
  • React中 Zustand狀態(tài)管理庫的使用詳解

    React中 Zustand狀態(tài)管理庫的使用詳解

    Zustand?是一個非常輕量、簡潔的狀態(tài)管理庫,旨在為 React 提供簡便且高效的狀態(tài)管理,相比于 Redux 或 Context API,Zustand 具有更簡潔、靈活和易于理解的優(yōu)點(diǎn),感興趣的朋友一起看看吧
    2024-12-12
  • React?useEffect不支持async?function示例分析

    React?useEffect不支持async?function示例分析

    這篇文章主要為大家介紹了React?useEffect不支持async?function示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • React 全自動數(shù)據(jù)表格組件——BodeGrid的實(shí)現(xiàn)思路

    React 全自動數(shù)據(jù)表格組件——BodeGrid的實(shí)現(xiàn)思路

    表格是在后臺管理系統(tǒng)中用的最頻繁的組件之一,相關(guān)的功能有數(shù)據(jù)的新增和編輯、查詢、排序、分頁、自定義顯示以及一些操作按鈕。這篇文章主要介紹了React 全自動數(shù)據(jù)表格組件——BodeGrid ,需要的朋友可以參考下
    2019-06-06
  • 在React中使用Antd上傳并讀取Excel文件的詳細(xì)步驟

    在React中使用Antd上傳并讀取Excel文件的詳細(xì)步驟

    在React中使用Antd組件庫來上傳并讀取Excel文件,可以結(jié)合antd的Upload組件和xlsx庫來實(shí)現(xiàn),以下是一個詳細(xì)的步驟和示例代碼,展示如何在React應(yīng)用中實(shí)現(xiàn)這一功能,感興趣的小伙伴跟著小編一起來看看吧
    2025-01-01
  • 深入理解React?State?原理

    深入理解React?State?原理

    本文主要介紹了React?State?原理,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • 淺談React19事件調(diào)度的設(shè)計思路

    淺談React19事件調(diào)度的設(shè)計思路

    本文主要介紹了React選擇MessageChannel作為事件調(diào)度機(jī)制的原因,essageChannel屬于宏任務(wù),延遲極低且不會阻塞渲染,能夠滿足React在不阻塞瀏覽器的前提下,盡可能多地推進(jìn)Fiber渲染進(jìn)度的需求
    2026-05-05
  • react裝飾器與高階組件及簡單樣式修改的操作詳解

    react裝飾器與高階組件及簡單樣式修改的操作詳解

    這篇文章主要介紹了react裝飾器與高階組件及簡單樣式修改的操作,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-09-09

最新評論

申扎县| 綦江县| 元谋县| 横山县| 正定县| 巴塘县| 集安市| 怀来县| 古蔺县| 江门市| 馆陶县| 湖北省| 上林县| 壤塘县| 荆州市| 乡城县| 新田县| 抚州市| 五原县| 广西| 中阳县| 仪陇县| 梅河口市| 莒南县| 浦东新区| 芜湖县| 栾川县| 松阳县| 白玉县| 镇安县| 南城县| 获嘉县| 北票市| 芜湖县| 连江县| 京山县| 汕头市| 南乐县| 玉环县| 海城市| 北安市|