<template>
  <section class="content-center gap-8 md:gap-9 grid grid-flow-row-dense items-center justify-center justify-items-center p-5">
    <!-- Third Man Records -->
    <img id="third-man-records" src="https://assets.codepen.io/141041/Third+Man+Records.svg" alt="Third Man Records" class="w-72 md:w-96" />
    
    <div id="visual" class="h-56 md:h-72 relative w-56 md:w-72">
      <!-- Three JS Globe -->
      <canvas id="three"></canvas>
      
      <!-- Bottom Left Note -->
      <img src="https://assets.codepen.io/141041/Third+Man+Records+Note.png" class="absolute left-0" style="bottom:20%;height:15%;width:15%" />
      
      <!-- Top Right Double Note -->
      <img src="https://assets.codepen.io/141041/Third+Man+Records+Double+Note.png" class="absolute right-0" style="height:15%;top:10%;width:15%" />
      
      <!-- Top Right Note -->
      <img src="https://assets.codepen.io/141041/Third+Man+Records+Note.png" class="absolute right-0" style="right:-20%;top:0%;height:15%;width:15%" />
    </div>
    
    <!-- Record -->
    <img id="record" src="https://assets.codepen.io/141041/Third+Man+Records+Record.svg" alt="Record" class="w-60 md:w-80" />
    
    <!-- Locations -->
    <img id="locations" src="https://assets.codepen.io/141041/Third+Man+Records+Locations.svg" alt="Detroit . Nashville . London" class="w-60 md:w-80" />
  </section>
</template>

<script>
export default{
  methods: {
    loadTexture(url) {
      return new Promise((resolve, revoke) => {
        // Initialize texture loader
        const textureLoader = new THREE.TextureLoader()
        
        // Load texture
        textureLoader.load(url, (texture) => {
          // Resolve
          resolve(texture)
          
        }, undefined, (error) => {
          // Revoke
          revoke(error)
          
        })
      })
    },
    async initializeThree() {
      // Base
      // ----------
      
      // Get size
      let { height, width } = document.getElementById('three').parentElement.getBoundingClientRect()
      
      // Initialize scene
      const scene = new THREE.Scene()
      
      // Initialize camera
      const camera = new THREE.PerspectiveCamera(20, 1, 0.1, 1000)
      
      // Position camera
      camera.position.z = 5.6
      
      // Initialize renderer
      const renderer = new THREE.WebGLRenderer({
        alpha: true,
        antialias: true,
        canvas: document.getElementById('three')
      })
      
      // Set renderer pixel ratio
      renderer.setPixelRatio(window.devicePixelRatio)
      
      // Set renderer size
      renderer.setSize(width, height)
      
      // World
      // ----------
      
      // Load world texture
      const worldTexture = await this.loadTexture('https://assets.codepen.io/141041/Third-Man-Records-Equirectangular.png')
      
      // Initialize world geometry
      const worldGeometry = new THREE.SphereGeometry(1, 20, 20)
      
      // Initialize world material
      const worldMaterial = new THREE.MeshBasicMaterial({
        color: 0xFFD200,
        map: worldTexture
      })
      
      // Initialize world
      const world = new THREE.Mesh(worldGeometry, worldMaterial)
      
      // Rotate world
      world.rotation.y = THREE.MathUtils.degToRad(-25)
      
      // Pivot
      // ----------
      
      // Add pivot group
      const pivotGroup = new THREE.Group()
      
      // Rotate pivot group
      pivotGroup.rotation.z = THREE.MathUtils.degToRad(10)
      
      // Add world to pivot group
      pivotGroup.add(world)
      
      // Add pivot group to scene
      scene.add(pivotGroup)
      
      // Rendering
      // ----------
      
      // Loop
      const render = () => {
        // Request animation frame
        requestAnimationFrame(render)
        
        // Rotate world
        world.rotation.y -= 0.005
        
        // Render scene
        renderer.render(scene, camera)

      }
      
      // Render
      render()
      
      // Resizing
      // ----------
      
      // Listen for window resizing
      window.addEventListener('resize', () => {
        // Get size
        let { height, width } = document.getElementById('three').parentElement.getBoundingClientRect()

        // Resize renderer
        renderer.setSize(width, height)

      })
      
    }
  },
  mounted() {
    // Initialize Three
    this.initializeThree()
    
    // Tween notes with Greensock
    gsap.to(document.getElementById('visual').getElementsByTagName('img'), {
      duration: 2,
      ease: "power1.inOut",
      repeat: -1,
      repeatRefresh: true,
      y: "random(20%, 40%)",
      yoyo: true
    })

  }
}
</script>

<style>
  html, body, section{
    height: 100%;
    width: 100%;
  }
  
  body{
    background: #FFD200;
  }
  
  canvas{
    border: 2px solid black;
    border-radius: 100%;
  }
  
  @media (min-width: 1024px) {
    section{
      gap: 5vh !important;
    }
    
    img#third-man-records{
      width: 60vh;
    }
    
    #visual{
      height: 40vh;
      width: 40vh;
    }
    
    img#record{
      width: 50vh;
    }
    
    img#locations{
      width: 50vh;
    }
  }
</style>
Run Pen

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js
  2. https://unpkg.co/[email protected]/dist/gsap.min.js