<template>
  <div>
    <!-- www.intotheabove.com -->
  </div>
</template>

<script setup>
  // Imports
  import { cover, contain } from "https://cdn.skypack.dev/intrinsic-scale@3.0.4"
  import { computed, ref, onMounted, onUnmounted } from 'vue'
  
  // Vars
  let app, background, collage, materials = []
  let portraitTexture, widescreenTexture
  let dragTarget
  let maxRotation = 45
  
  // Refs
  const height = ref(window.innerHeight)
  const orientation = ref(0)
  const width = ref(window.innerWidth)
  
  // Collage materials
  let collageMaterials = [
    {
      src: "https://assets.codepen.io/141041/sunset.jpg",
      type: "image"
    }, {
      src: "https://assets.codepen.io/141041/img-0042.jpg",
      type: "image"
    }, {
      src: "https://assets.codepen.io/141041/img-0022.jpg",
      type: "image"
    }, {
      src: "https://assets.codepen.io/141041/ic-intro.jpg",
      type: "image"
    }, {
      src: "https://assets.codepen.io/141041/dom-eric-seinfeld-bananas.mp4",
      type: "video"
    }, {
      src: "https://assets.codepen.io/141041/cymal-talk-w-steve.mp4",
      type: "video"
    }
  ]
  
  // Computed
  // ----------
  
  // Material positions
  const materialPositions = computed(() => {
    // Portrait positions
    let portraitPositions = [
      [1/3, 1/4],
      [2/3, 1/4],
      [1/3, 2/4],
      [2/3, 2/4],
      [1/3, 3/4],
      [2/3, 3/4]
    ]

    // Widescreen positions
    let widescreenPositions = [
      [1/4, 1/3],
      [2/4, 1/3],
      [3/4, 1/3],
      [1/4, 2/3],
      [2/4, 2/3],
      [3/4, 2/3]
    ]
  
    // Dynamic positions
    return [widescreenPositions, portraitPositions][orientation.value]
    
  })
  
  // Material radius
  const materialRadius = computed(() => {
    // Radius
    let radius = 0.1
    
    // Dynamic radius
    return [width.value * radius, height.value * radius][orientation.value]
    
  })
  
  // Material scale
  const materialScale = computed(() => {
    // Dynamic scale
    return [0.25, 0.2][orientation.value]
    
  })
  
  // Initialize pixi
  // ----------
  async function initializePixi() {
    // App
    app = new PIXI.Application({
      background: 0x000000,
      resizeTo: window
    })
    
    // Add the view to the DOM
    document.body.appendChild(app.view)
    
    // Add background
    await addBackground()
    
    // Add collage
    addCollage()
    
    // Add materials
    addMaterials()
    
    // Start resizing
    startResizing()
    
  }
  
  // Add background
  // ----------
  async function addBackground() {
    // Load portrait texture
    portraitTexture = await PIXI.Assets.load('https://assets.codepen.io/141041/portrait-collage.jpg')
    
    // Load widescreen texture
    widescreenTexture = await PIXI.Assets.load('https://assets.codepen.io/141041/widescreen-collage.jpg')    
    
    // Background sprite
    background = PIXI.Sprite.from(widescreenTexture)
    
    // Add to stage
    app.stage.addChild(background)
    
  }
  
  // Resize background
  // ----------
  function resizeBackground() {
    // Update texture based on orientation
    background.texture = orientation.value ? portraitTexture : widescreenTexture
    
    // Covering
    let { width, height, x, y } = cover(window.innerWidth, window.innerHeight, background.texture.width, background.texture.height)
    
    // Adjust
    background.width = width
    background.height = height
    background.x = x
    background.y = y
    
  }
  
  // Add collage
  // ----------
  function addCollage() {
    // Collage
    collage = new PIXI.Container()
    
    // Event mode
    collage.eventMode = 'static'
    
    // Sorting
    collage.sortableChildren = true
    
    // Add to stage
    app.stage.addChild(collage)
    
    // Listen for drag end
    collage.on('pointerup', onDragEnd)
    collage.on('pointerupoutside', onDragEnd)
    
  }
  
  // Add materials
  // ----------
  async function addMaterials() {
    // Randomize materials
    collageMaterials = _.shuffle(collageMaterials)
    
    // Loop through materials
    collageMaterials.forEach(async (m, i) => {
      // Texture
      let texture, video
      
      // If video
      if (m.type == "video") {
        // Video
        video = document.createElement("video")
        
        // Settings
        video.autoplay = true
        video.crossOrigin = "anonymous"
        video.loop = true
        video.muted = true
        video.playsInline = true
        
        // Update source
        video.src = m.src
        
        // Append to body
        document.body.appendChild(video)
        
        // Await video load
        await new Promise((resolve, revoke) => {
          // On loaded
          video.onloadedmetadata = resolve
          
        })
        
        // Texture
        texture = PIXI.Texture.from(video)
        
      } else {
        // Texture
        texture = await PIXI.Assets.load(m.src)
        
      }
      
      // Material
      let material = PIXI.Sprite.from(texture)
      
      // If video exists
      if (video) {
        // Associate with material
        material.video = video
        
      }
      
      // Anchor
      material.anchor.set(0.5)
      
      // Scale
      material.scale.set(height.value / material.texture.height * randomScale())
      
      // Origin point
      let originPoint = [
        materialPositions.value[i][0] * app.screen.width,
        materialPositions.value[i][1] * app.screen.height
      ]
      
      // Random point
      let randomPoint = randomPosition(originPoint, materialRadius.value)
      
      // Position
      material.x = randomPoint[0]
      material.y = randomPoint[1]
      
      // Rotation
      material.rotation = randomRotation()
      
      // Interactive
      material.eventMode = 'static'
      
      // Mask texture
      let maskTexture = await PIXI.Assets.load('https://assets.codepen.io/141041/tear.jpg?1')
      
      // Mask sprite
      let maskSprite = PIXI.Sprite.from(maskTexture)
      
      // Anchor mask
      maskSprite.anchor.set(0.5)
      
      // Mask sprite
      material.mask = maskSprite
      
      // Add sprite
      material.addChild(maskSprite)
      
      // On mouseover
      material.onmouseover = () => {
        // Pointer
        document.body.style.cursor = "pointer"
        
      }
      
      // On mouseout
      material.onmouseout = () => {
        // Default
        document.body.style.cursor = "default"
        
      }
      
      // On click
      material.on('click', () => {
        // Loop through all materials
        materials.forEach(material => {
          // If material has video
          if (material.video) {
            // Mute video
            material.video.muted = true
            
          }
          
        })
        
        // If this material has video
        if (material.video) {
          // Unmuted it
          material.video.muted = false 
          
        }
        
      }, material)
      
      // On pointerdown
      material.on('pointerdown', onDragStart, material)
      
      // Add to stage
      collage.addChild(material)
      
      // Add to array
      materials.push(material)
      
    })
    
  }
  
  // Resize materials
  // ----------
  function resizeMaterials() {
    // Loop through materials
    materials.forEach((material, i) => {
      let randomScaler = randomScale()
      
      // Scale
      material.scale.set(height.value / material.texture.height * randomScaler)
      
      // Origin point
      let originPoint = [
        materialPositions.value[i][0] * app.screen.width,
        materialPositions.value[i][1] * app.screen.height
      ]
      
      // Random point
      let randomPoint = randomPosition(originPoint, materialRadius.value)
      
      // Position
      material.x = randomPoint[0]
      material.y = randomPoint[1]
      
      // Rotation
      let randomRotate = randomRotation()
      
      // Rotate
      material.rotation = randomRotation()
      
    })
    
  }
  
  // Drag start
  // ----------
  function onDragStart() {
    // Default
    document.body.style.cursor = "grabbing"
    
    // Reset other materials z index
    materials.forEach(material => material.zIndex = 0)

    // Adjust z index
    this.zIndex = 10
    
    // Transparent
    // this.alpha = 0.5
    
    // Update drag target
    dragTarget = this
    
    // Listen for pointer move
    collage.on('pointermove', onDragMove)
    
  }
  
  // Drag move
  // ----------
  function onDragMove(e) {
    // If drag target
    if (dragTarget) {
      // Position target
      dragTarget.parent.toLocal(e.global, null, dragTarget.position)
      
    }
    
  }
  
  // Drag end
  // ----------
  function onDragEnd() {       
    // If drag target
    if (dragTarget) {
      // Default
      document.body.style.cursor = "default"
      
      // Remove pointer move listener
      collage.off('pointermove', onDragMove)
      
      // Return alpha
      // dragTarget.alpha = 1
      
      // Clear drag target
      dragTarget = null
      
    }
    
  }
  
  // Resize
  // ----------
  function resize() {
    // Log
    console.log('resize')
    
    // Update sizes
    height.value = window.innerHeight
    width.value = window.innerWidth
    
    // If height greater than width
    if (window.innerHeight > window.innerWidth) {
      // Portrait
      orientation.value = 1
      
    } else {
      // Landscape
      orientation.value = 0
      
    }
    
    // Resize background
    resizeBackground()
    
    // Resize materials
    resizeMaterials()
    
  }
  
  // Start resizing
  // ----------
  function startResizing() {
    // On window resize
    window.addEventListener('resize', resize)

    // On window orientation
    window.addEventListener('orientationchange', resize)
    
    // Resize
    resize()
    
  }
  
  // Stop resizing
  // ----------
  function stopResizing() {
    // Remove window resize
    window.removeEventListener('resize', resize)

    // Remove window orientation
    window.removeEventListener('orientationchange', resize)
    
  }
  
  // Random point
  // ----------
  function randomPosition(center, radius) {
    // Random angle
    let angle = Math.random() * Math.PI * 2
    
    console.log(center, radius)
    
    // Calculate x and y
    let x = Math.cos(angle) * radius + center[0]
    let y = Math.sin(angle) * radius + center[1]
    
    // Return point
    return [x, y]
    
  }
  
  // Degree to radians
  // ----------
  function degToRad(degrees) {
    // Return radians
    return degrees * (Math.PI / 180)
    
  }
  
  // Random rotation
  // ----------
  function randomRotation() {
    // Return random rotation
    return degToRad(maxRotation * Math.random()) - degToRad(maxRotation * Math.random())
    
  }
  
  // Random scale
  // ----------
  function randomScale() {
    // Return random scale
    return Math.random() * materialScale.value + materialScale.value
    
  }
  
  // On mounted
  // ----------
  onMounted(() => {
    // Initialize pixi
    initializePixi()
    
  })
  
  // On unmounted
  // ----------
  onUnmounted(() => {
    // Stop resizing
    stopResizing()
    
  })
</script>

<style>
  html{
    position: fixed;
  }
  
  video{
    height: 1px;
    left: 0;
    position: absolute;
    top: 0;
    width: 1px;
  }
</style>
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/pixi.js/7.2.4/pixi.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js