<!--
Made with LUME.
http://lume.io
-->
<script src="https://unpkg.com/lume@0.3.0-alpha.27/dist/global.js"></script>

<!-- Lume works with any framework. Here's we'll try out Vue because
  it works with plain HTML, not requiring any build tool. -->
<script src="https://unpkg.com/vue@2.7.14/dist/vue.js"></script>

<!-- Tween.js is for smoothly animating numbers. We'll animate our buttons up and down. -->
<script src="https://unpkg.com/tween.js@16.6.0/src/Tween.js"></script>

<!-- Pointer Events polyfill (needed in Safari < 13) -->
<script src="https://code.jquery.com/pep/0.4.3/pep.js"></script>

<script>
	LUME.defineElements()
</script>

<style>
	@import url('https://fonts.googleapis.com/css2?family=Poppins&display=swap');
	body,
	html {
		width: 100%;
		height: 100%;
		margin: 0;
		padding: 0;
		overflow: hidden;
		touch-action: none;
		background: black;
	}
	lume-element3d {
		text-align: center;
	}
	button {
		width: 100%;
		height: 100%;
		white-space: nowrap;
		border-radius: 10px;
		border: none;
		background: #595c5e;
		color: #ccc;
		outline: none;
		font-family: 'Poppins', sans-serif;
		font-weight: bold;
		font-size: 16px;
	}
	button:focus,
	button:hover {
		background: #617e9f;
	}
</style>

<template vue>
	<form style="display: contents" onsubmit="console.log('Native form submission!'); event.preventDefault()">
		<div style="width: 100%; height: 100%;">
			<!-- Lights and shadows are powered by WebGL, but written with HTML: -->
			<lume-scene
				webgl="true"
				id="scene"
				background-color="black"
				background-opacity="0"
				perspective="600"
				shadowmap-type="pcfsoft"
				NOTE="one of basic, pcf, pcfsoft"
				touch-action="none"
				@pointermove="onmove"
				@pointerdown="ondown"
				@pointerup="onup"
				physically-correct-lights
			>
				<lume-ambient-light color="#ffffff" intensity="2"></lume-ambient-light>

				<lume-plane
					ref="plane"
					id="background"
					size-mode="literal literal"
					size="300 300 0"
					align-point="0.5 0.5"
					mount-point="0.5 0.5"
					has="phong-material"
					color="white"
					dithering
					color="white"
					comment="free texture from https://polyhaven.com/a/concrete_layers_02"
					texture="https://assets.codepen.io/191583/cement_wall_diff_2k.jpg"
					bump-map="https://assets.codepen.io/191583/cement_wall_disp_2k.jpg"
					bump-scale="8"
					shininess="75"
					specular="#222"
				>
					<lume-element3d
						id="button-container"
						position="0 0 20"
						size="520 38 0"
						align-point="0.5 0.5 0"
						mount-point="0.5 0.5 0"
					>
						<lume-mixed-plane
							v-for="(item, i) in buttons"
							ref="btn"
							:key="i"
							size-mode="literal proportional"
							size="120 1 0"
							:align-point="`${i*0.333} 0 0`"
							:mount-point="`${i*0.333} 0 0`"
							color="#444"
							has="rounded-rectangle-geometry"
							corner-radius="10"
							thickness="1"
							quadratic-corners="false"
							roughness="0.48"
						>
							<!-- Native button elements! -->
							<button>{{item}}</button>
						</lume-mixed-plane>
					</lume-element3d>
				</lume-plane>

				<lume-element3d id="lightContainer" size="0 0 0" position="0 0 300">
					<lume-point-light
						id="light"
						color="white"
						size="0 0 0"
						position="-50 -50"
						:intensity="1000 / Math.PI"
						shadow-map-width="2048"
						shadow-map-height="2048"
						shadow-radius="10"
						distance="800"
						shadow-bias="-0.001"
					>
						<lume-mesh
						   id="bulb"
						   has="sphere-geometry basic-material"
						   size="10 10 10"
						   mount-point="0.5 0.5 0.5"
						   color="white"
						   receive-shadow="false"
						   cast-shadow="false"
						   style="pointer-events: none"
					   ></lume-mesh>
					</lume-point-light>
				</lume-element3d>
			</lume-scene>
		</div>
	</form>
</template>

<div id="buttonsRoot"></div>

<script type="module">
	const {Motor, Events} = LUME

	new Vue({
		el: '#buttonsRoot',
		template: document.querySelector('[vue]').innerHTML,

		data: () => ({
			buttons: ['🏖️ Have Fun', '😊 Smi)e', '🛠️ Create', '♥️ With Love'],
		}),

		mounted() {
			const lightContainer = document.querySelector('#lightContainer')
			const bulb = document.querySelector('#bulb')
			const plane = this.$refs.plane
			this.targetPosition = {x: window.innerWidth / 2, y: window.innerHeight / 2}

			Motor.addRenderTask(time => {
				lightContainer.position.x += (this.targetPosition.x - lightContainer.position.x) * 0.05
				lightContainer.position.y += (this.targetPosition.y - lightContainer.position.y) * 0.05
				plane.rotation.y = 10 * (lightContainer.position.x / window.innerWidth) - 5
				plane.rotation.x = -(10 * (lightContainer.position.y / window.innerHeight) - 5)
			})

			window.addEventListener('resize', resize)
			resize()
			function resize() {
				const winAspect = window.innerWidth / window.innerHeight
				if (winAspect < 1) plane.size = [window.innerHeight * 1.3, window.innerHeight * 1.3]
				else plane.size = [window.innerWidth * 1.3, window.innerWidth * 1.3]
			}
		},

		methods: {
			onmove(e) {
				e.preventDefault()
				this.targetPosition.x = e.clientX
				this.targetPosition.y = e.clientY
			},

			// On mouse down animate the button downward using Tween.js
			// https://github.com/tweenjs/tween.js
			ondown(e) {
				if (e.target.matches('button')) {
					this.pressedButton = e.target

					if (this.upTween) {
						this.upTween.stop()
						this.upTween = null
					}

					this.downTween = new TWEEN.Tween(e.target.parentNode.position)
						.to({z: -16}, 75)
						.start()
						.onComplete(() => (this.downTween = null))

					Motor.addRenderTask(time => {
						if (!this.downTween) return false
						this.downTween.update(time)
					})
				}
			},

			// On mouse up animate the button upward using Tween.js
			onup() {
				if (this.pressedButton) {
					if (this.downTween) {
						this.downTween.stop()
						this.downTween = null
					}

					this.upTween = new TWEEN.Tween(this.pressedButton.parentNode.position)
						.to({z: 0}, 75)
						.start()
						.onComplete(() => (this.upTween = null))

					Motor.addRenderTask(time => {
						if (!this.upTween) return false
						this.upTween.update(time)
					})
				}
			},
		},
	})
</script>

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.