<div class="container">
<div class="txt-wrapper">
<div class="emotionTxt__wrap">
<p class="emotionTxt">Loading</p>
</div>
<div class="line"></div>
<h1 class="txt">SAP</h1>
</div>
<div id="vid-wrapper" class="vid-wrapper">
<video id="video" class="video" autoplay muted></video>
</div>
</div>
@import url('https://fonts.googleapis.com/css?family=Roboto+Slab&display=block');
@font-face {
font-family: 'Anybody';
src: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/61488/ETCAnybodyPB.woff2') format('woff2-variations');
font-display: block;
font-style: normal italic;
font-weight: 100 900;
font-stretch: 10% 400%;
}
/* ==========================================================================
Responsive Viewport Sized Typography
========================================================================== */
///
/// Viewport sized typography with minimum and maximum values
///
/// @author Eduardo Boucas (@eduardoboucas)
///
/// @param {Number} $responsive - Viewport-based size
/// @param {Number} $min - Minimum font size (px)
/// @param {Number} $max - Maximum font size (px)
/// (optional)
/// @param {Number} $fallback - Fallback for viewport-
/// based units (optional)
///
/// @example scss - 5vw font size (with 50px fallback),
/// minumum of 35px and maximum of 150px
/// @include responsive-font(5vw, 35px, 150px, 50px);
///
@mixin responsive-font($responsive, $min, $max: false, $fallback: false) {
$responsive-unitless: $responsive / ($responsive - $responsive + 1);
$dimension: if(unit($responsive)=='vh', 'height', 'width');
$min-breakpoint: $min / $responsive-unitless * 100;
@media (max-#{$dimension}: #{$min-breakpoint}) {
font-size: $min;
}
@if $max {
$max-breakpoint: $max / $responsive-unitless * 100;
@media (min-#{$dimension}: #{$max-breakpoint}) {
font-size: $max;
}
}
@if $fallback {
font-size: $fallback;
}
font-size: $responsive;
};
html {
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}
html, body {
width: 100%;
height: 100%;
}
body {
display: flex;
align-items: center;
justify-content: center;
width: 100vw;
height: 100vh;
margin: 0;
padding: 0;
background-color: black;
color: white;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow: hidden;
}
.container {
display: flex;
width: 100vw;
height: 100vh;
align-items: center;
justify-content: center;
}
.txt-wrapper {
position: relative;
z-index: 10;
text-transform: uppercase;
visibility: hidden;
}
.emotionTxt__wrap {
overflow: hidden;
padding-bottom: 8px;
}
.emotionTxt {
margin: 0;
font-family: 'Roboto Slab', serif;
font-weight: 400;
// font-size: 1.3vw;
@include responsive-font(1.3vw, 12px, 27px, 16px);
text-align: center;
letter-spacing: 0px;
}
.line {
display: block;
width: 100%;
height: 1px;
background: white;
}
.txt {
margin: 8px 0 0;
@include responsive-font(15vw, 100px, 300px, 100px);
// font-size: 15vw;
line-height: 0.8;
font-family: 'Anybody';
text-align: center;
font-weight: 100;
font-stretch: 10%;
// overflow: hidden;
}
.char {
overflow: visible;
}
.vid-wrapper {
position: absolute;
z-index: 1;
top: 0;
right: 0;
width: 100%;
height: 100%;
}
canvas {
position: absolute;
z-index: 2;
top: 0;
right: 0;
width: 100%;
height: 100%;
object-fit: cover;
opacity: 0.5
}
video {
width: 100%;
height: 100%;
object-fit: cover;
opacity: 0.2;
}
View Compiled
console.clear();
select = e => document.querySelector(e);
selectAll = e => document.querySelectorAll(e);
const models = "https://petebarr.github.io/Face-Detection-JavaScript/models";
const video = select(".video");
const vidWrap = select(".vid-wrapper");
const txtWrap = select(".txt-wrapper");
const emotionTxt = select(".emotionTxt");
const txt = select(".txt");
const line = select(".line");
let neutral = 1,
happy = 0,
sad = 0,
surprised = 0,
angry = 0,
emo = "Neutral";
function initFaceDetection() {
Promise.all([
faceapi.nets.tinyFaceDetector.loadFromUri(models),
faceapi.nets.faceLandmark68Net.loadFromUri(models),
faceapi.nets.faceRecognitionNet.loadFromUri(models),
faceapi.nets.faceExpressionNet.loadFromUri(models),
]).then(startVideo)
}
function startVideo() {
navigator.mediaDevices.getUserMedia({ audio: false, video: true })
.then( stream => {
var video = document.querySelector('video');
video.srcObject = stream;
video.onloadedmetadata = function(e) {
video.play();
};
})
}
video.addEventListener('play', () => {
let vW = video.videoWidth;
let vH = video.videoHeight;
let aspectRatio = vW/vH;
const displaySize = { width: video.offsetWidth, height: video.offsetWidth/aspectRatio };
const canvas = faceapi.createCanvasFromMedia(video);
const ctx = canvas.getContext('2d');
vidWrap.append(canvas);
faceapi.matchDimensions(canvas, displaySize);
setInterval(async () => {
const detections = await faceapi.detectAllFaces(video, new faceapi.TinyFaceDetectorOptions())
.withFaceLandmarks()
.withFaceExpressions();
const resizedDetections = faceapi.resizeResults(detections, displaySize);
ctx.clearRect(0, 0, canvas.width, canvas.height);
// faceapi.draw.drawDetections(canvas, resizedDetections);
faceapi.draw.drawFaceLandmarks(canvas, resizedDetections);
// faceapi.draw.drawFaceExpressions(canvas, resizedDetections);
happy = resizedDetections[0].expressions.happy > 0.8 ? 1 : 0;
sad = resizedDetections[0].expressions.sad > 0.8 ? 1 : 0;
angry = resizedDetections[0].expressions.angry > 0.8 ? 1 : 0;
surprised = resizedDetections[0].expressions.surprised > 0.8 ? 1 : 0;
neutral = resizedDetections[0].expressions.neutral > 0.8 ? 1 : 0;
animateText();
}, 100);
})
function animateText() {
let fw = 100,
fs = 10,
fsl = 0;
if(happy==1) {
fw = 500;
fs = 200;
fsl = 0;
setEmo("Happy");
}
else if(sad == 1) {
fw = 900;
fs = 50;
fsl = 0;
setEmo("Sad");
}
else if(surprised == 1) {
fw = 900;
fs = 400;
fsl = 50;
setEmo("Surprised");
}
else if(angry == 1) {
fw = 900;
fs = 10;
fsl = 100;
setEmo("Angry");
}
else { // neutral
fw = 100;
fs = 10;
fsl = 0;
setEmo("Boring");
}
gsap.to( '.txt', {
fontWeight: fw,
fontStretch: fs,
fontVariationSettings: () => {
return `'slnt' ${fsl}`;
},
ease: "power2",
duration: 0.4
} );
}
function setEmo(emo) {
emotionTxt.textContent = emo;
}
function init() {
let tSplitText = new SplitText(txt, {type:"chars", charsClass:"char" });
gsap.timeline({ delay: 0.5, onComplete: initFaceDetection })
.set(txtWrap, { autoAlpha: 1 })
.from(emotionTxt, { y: 50, duration: 0.4, ease: "power4"}, 0)
.from(line, { scaleX: 0, duration: 0.4, ease: "power4", transformOrigin: "center center"}, 0.2)
.from(".char", {
y: 300,
opacity: 0,
duration: 0.6,
ease: "elastic(1, 1.5)",
stagger: 0.1
}, 0.1)
}
window.onload = () => {
init();
};
This Pen doesn't use any external CSS resources.