.container#example_container
	#example_wrapper
		canvas#example_canvas
	.call-to-action
		h1 Voxel Chicky
		p I created this model using MagicaVoxel
		button.btn-bounce#btn_bounce Make Chicky bounce!
View Compiled
@import url("https://fonts.googleapis.com/css2?family=Luckiest+Guy")
@import url("https://fonts.googleapis.com/css2?family=Rubik")

$yellow: #ffec1c
$orange: #e37807

html, body
	width: 100%
	height: 100%

body
	font-size: 24px
	background-image: linear-gradient(#2bb5ff, #3366ff)

.container
	width: 100%
	height: 100%
	margin: auto
	position: relative

.call-to-action
	text-align: center
	width: 100%
	height: 100%
	display: flex
	flex-direction: column
	position: absolute
	top: 0
	left: 0
	h1, p
		padding: 0 1rem
	h1
		color: $yellow
		font-family: "Luckiest Guy", cursive
		margin: 3rem 0 1rem
		text-shadow: 0 0.35rem $orange
	p
		color: #efefef
		font-family: "Rubik", sans-serif
		margin: 0
	.btn-bounce
		color: #4f4f4f
		font-family: "Rubik", sans-serif
		background-color: $yellow
		padding: 1rem 2rem
		border: 0
		margin: auto 2rem 3rem
		align-self: center
		border-radius: 0.75rem
		box-shadow: 0 0.35rem $orange
		transition: box-shadow 64ms ease-out, transform 64ms ease-out
		&:active
			box-shadow: 0 0rem $orange
			transform: translateY(0.35rem)

@media screen and (min-width: 568px)
	.container
		width: 568px
View Compiled
import {
	Scene,
	PerspectiveCamera,
	WebGLRenderer,
	Color,
	AmbientLight,
	DirectionalLight,
	Group,
} from "https://cdn.skypack.dev/three@0.135.0";
import {
	VOXLoader,
	VOXMesh
} from "https://cdn.skypack.dev/three@0.135.0/examples/jsm/loaders/VOXLoader.js";
import gsap from "https://cdn.skypack.dev/gsap@3.8.0";

console.clear();

const exampleContainer = document.querySelector("#example_container");
const exampleCanvas = document.querySelector("#example_canvas");
const btnBounce = document.querySelector("#btn_bounce");

const scene = new Scene();
const camera = new PerspectiveCamera(
	75, exampleContainer.clientWidth / exampleContainer.clientHeight, 0.1, 1000);
const renderer = new WebGLRenderer({
	canvas: exampleCanvas,
	alpha: true,
	antialias: true,
});

const chicky = new Group();

const tl = gsap.timeline();

function setRenderer() {
	renderer.setPixelRatio(window.devicePixelRatio);
	renderer.setSize(exampleContainer.clientWidth, exampleContainer.clientHeight);
}

function setLighting() {
	const ambientColor = 0xFFFFFF;
  const ambientIntensity = 0.3;
  const ambientLight = new AmbientLight(ambientColor, ambientIntensity);
  
  scene.add(ambientLight);
	
	const directionalColor = 0xFFFFFF;
  const directionalIntensity = 1;
  const directionalLight = new DirectionalLight(
		directionalColor, directionalIntensity);
	const directionalX = -10;
	const directionalY = 30;
	const directionalZ = 20;
  
  directionalLight.position.set(directionalX, directionalY, directionalZ);
	directionalLight.castShadow = true;
	
  scene.add(directionalLight);
  scene.add(directionalLight.target);
}

function loadModel() {
	const loader = new VOXLoader();
	
	loader.load("https://assets.codepen.io/430361/Chicky.vox", chunks => {
		for (let chunk of chunks) {
			const voxMesh = new VOXMesh(chunk);
			
			voxMesh.scale.setScalar(0.25);
			
			chicky.add(voxMesh);
		}
		
		scene.add(chicky);
	});
}

function update() {
	requestAnimationFrame(update);
	
	renderer.render(scene, camera);
	
	chicky.rotation.y += 0.0075;
}

function initialize() {
	setRenderer();
	
	// Initialize camera position
	camera.position.z = 5;
	
	setLighting();
	loadModel();
	update();
}

function bounceModel() {
	tl.clear();
	
	// Fall from top
	
	tl.fromTo(chicky.position, {
		y: 3,
	}, {
		y: 0,
		duration: 0.8,
		ease: "power2.in",
	});
	
	// Squish
	
	tl.to(chicky.scale, {
		x: 1.35,
		y: 0.65,
		duration: 0.24,
	});
	
	tl.to(chicky.position, {
		y: -1.05,
		duration: 0.24,
	}, "-=0.24");
	
	// Bounce
	
	tl.to(chicky.scale, {
		x: 0.75,
		y: 1.25,
		duration: 0.32,
	});
	
	tl.to(chicky.position, {
		y: 1.5,
		duration: 0.64,
		ease: "power1.out",
	}, "-=0.32");
	
	// Fall again
	
	tl.to(chicky.position, {
		y: 0,
		duration: 0.56,
		ease: "power2.in",
	});
	
	tl.to(chicky.scale, {
		x: 1,
		y: 1,
		duration: 0.56,
	}, "-=0.56");
	
	// Squish
	
	tl.to(chicky.scale, {
		x: 1.15,
		y: 0.85,
		duration: 0.16,
	});
	
	tl.to(chicky.position, {
		y: -0.48,
		duration: 0.16,
	}, "-=0.16");
	
	// Bounce again
	
	tl.to(chicky.position, {
		y: 1,
		duration: 0.48,
	});
	
	tl.to(chicky.scale, {
		x: 1,
		y: 1,
		duration: 0.48,
		ease: "power1.out",
	}, "-=0.48");
	
	// Bounce animation
	
	tl.to(chicky.position, {
		y: 0,
		duration: 1.024,
		ease: "bounce.out",
	});
}

initialize();

window.addEventListener("resize", () => {
	camera.aspect = exampleContainer.clientWidth / exampleContainer.clientHeight;
  camera.updateProjectionMatrix();

  renderer.setSize(exampleContainer.clientWidth, exampleContainer.clientHeight);
});

btnBounce.addEventListener("click", () => {
	bounceModel();
});
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.