<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>
This Pen doesn't use any external CSS resources.